| // Licensed to Apache Software Foundation (ASF) under one or more contributor |
| // license agreements. See the NOTICE file distributed with |
| // this work for additional information regarding copyright |
| // ownership. Apache Software Foundation (ASF) licenses this file to you under |
| // the Apache License, Version 2.0 (the "License"); you may |
| // not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| |
| package injector |
| |
| import ( |
| "encoding/json" |
| "fmt" |
| "net" |
| "reflect" |
| "regexp" |
| "runtime" |
| "strings" |
| |
| corev1 "k8s.io/api/core/v1" |
| "k8s.io/apimachinery/pkg/util/sets" |
| ) |
| |
| // AnnotationValidateFunc is the type of validate function |
| type AnnotationValidateFunc func(annotation, value string) error |
| |
| var ( |
| //AnnotationValidateFuncs define all validate functions |
| AnnotationValidateFuncs = []AnnotationValidateFunc{ |
| ValidateServiceName, |
| ValidateBackendServices, |
| ValidateIPv4OrHostname, |
| ValidateResourceRequirements, |
| } |
| ) |
| |
| const ( |
| // CPU is in cores (100m = .1 cores) |
| CPU string = "cpu" |
| // Memory is in bytes (100Gi = 100GiB = 100 * 1024 * 1024 * 1024) |
| Memory string = "memory" |
| // EphemeralStorage is in bytes (100Gi = 100GiB = 100 * 1024 * 1024 * 1024) |
| EphemeralStorage string = "ephemeral-storage" |
| // HugePagesPrefix is huge pages prefix |
| HugePagesPrefix string = "hugepages-" |
| ) |
| |
| var standardContainerResources = sets.NewString( |
| CPU, |
| Memory, |
| EphemeralStorage, |
| ) |
| |
| // FindValidateFunc is find the validate function for an annotation |
| func FindValidateFunc(funcName string) AnnotationValidateFunc { |
| for _, f := range AnnotationValidateFuncs { |
| // extract the function name into a string , it will be like following |
| // github.com/apache/skywalking-swck/operator/pkg/operator/injector.ValidateBool |
| fname := runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name() |
| // get real validate function name in injector |
| index := strings.LastIndex(fname, ".") |
| funcname := fname[index+1:] |
| |
| if funcname == funcName { |
| return f |
| } |
| } |
| return nil |
| } |
| |
| //ValidateServiceName validates the ServiceName is nil or not |
| func ValidateServiceName(annotation, value string) error { |
| if value == "" { |
| return fmt.Errorf("%s error:the service name is nil", annotation) |
| } |
| return nil |
| } |
| |
| //ValidateBackendServices validates an annotation's value is valid backend services |
| func ValidateBackendServices(annotation, value string) error { |
| services := []string{value} |
| if strings.Contains(value, ",") { |
| services = strings.Split(value, ",") |
| } |
| allEmpty := true |
| for _, service := range services { |
| service = strings.TrimSpace(service) |
| if service == "" { |
| continue |
| } |
| allEmpty = false |
| err := ValidateIPv4OrHostname(annotation, service) |
| if err != nil { |
| return err |
| } |
| } |
| if allEmpty { |
| return fmt.Errorf("%s error:every service is nil", annotation) |
| } |
| return nil |
| } |
| |
| //ValidateIPv4OrHostname validates an annotation's value is valid ipv4 or hostname |
| func ValidateIPv4OrHostname(annotation, service string) error { |
| service = strings.TrimSpace(service) |
| colonIndex := strings.LastIndex(service, ":") |
| host := service |
| if colonIndex != -1 { |
| host = service[:colonIndex] |
| } |
| if host == "" { |
| return fmt.Errorf("%s error:the service name is nil", annotation) |
| } |
| ip := net.ParseIP(host) |
| if ip != nil && ip.To4() != nil { |
| return nil |
| } |
| |
| match, err := regexp.MatchString(`^([a-zA-Z0-9][a-zA-Z0-9_-]{0,62})(\.[a-zA-Z0-9_][a-zA-Z0-9_-]{0,62})*?$`, |
| host) |
| if err != nil { |
| return fmt.Errorf("%s error:%s", annotation, err.Error()) |
| } |
| if !match { |
| return fmt.Errorf("%s=%s error:not a valid ipv4 or hostname", annotation, host) |
| } |
| |
| return nil |
| } |
| |
| //ValidateResourceRequirements validates the resource requirement |
| func ValidateResourceRequirements(annotation, value string) error { |
| if value == "nil" { |
| return nil |
| } |
| |
| resource := make(corev1.ResourceList) |
| err := json.Unmarshal([]byte(value), &resource) |
| if err != nil { |
| return fmt.Errorf("%s unmarshal error:%s", annotation, err.Error()) |
| } |
| |
| for resourceName, quantity := range resource { |
| //validate resource name |
| if !standardContainerResources.Has(string(resourceName)) && !strings.HasPrefix(string(resourceName), HugePagesPrefix) { |
| return fmt.Errorf("%s error:%s isn't a standard resource type", annotation, string(resourceName)) |
| } |
| |
| //validate resource quantity value |
| if quantity.MilliValue() <= int64(0) { |
| return fmt.Errorf("%s error:%d must be greater than 0", annotation, quantity.MilliValue()) |
| } |
| } |
| |
| return nil |
| } |