diff --git a/api/restHandler/PipelineConfigRestHandler.go b/api/restHandler/PipelineConfigRestHandler.go index cd91911096..c815dcc5a3 100644 --- a/api/restHandler/PipelineConfigRestHandler.go +++ b/api/restHandler/PipelineConfigRestHandler.go @@ -25,6 +25,8 @@ import ( "fmt" "io" "net/http" + "os" + "strconv" "strings" @@ -1290,8 +1292,17 @@ func (handler PipelineConfigRestHandlerImpl) UpdateAppOverride(w http.ResponseWr writeJsonResp(w, err, nil, http.StatusBadRequest) } ChartVersion := dat.RefChartTemplate - validate, error := validator2.DeploymentTemplateValidate(dat.ValuesOverride, ChartVersion) - if validate { + + if _, err := os.Stat(fmt.Sprintf("schema/%s.json",ChartVersion)); err == nil { + validate, error := validator2.DeploymentTemplateValidate(dat.ValuesOverride, ChartVersion) + if !validate { + fmt.Println("Values are incorrect", error) + writeJsonResp(w, error, nil, http.StatusBadRequest) + return + } + + + } err = handler.validator.Struct(templateRequest) if err != nil { handler.Logger.Errorw("validation err, UpdateAppOverride", "err", err, "payload", templateRequest) @@ -1320,11 +1331,9 @@ func (handler PipelineConfigRestHandlerImpl) UpdateAppOverride(w http.ResponseWr return } writeJsonResp(w, err, createResp, http.StatusOK) - } else { - fmt.Println("Values are incorrect", error) - writeJsonResp(w, error, nil, http.StatusBadRequest) - return - } + + + } func (handler PipelineConfigRestHandlerImpl) FetchArtifactForRollback(w http.ResponseWriter, r *http.Request) { diff --git a/internal/validator/DeploymentTemplateValidator.go b/internal/validator/DeploymentTemplateValidator.go index 378fc2fb55..337a600141 100644 --- a/internal/validator/DeploymentTemplateValidator.go +++ b/internal/validator/DeploymentTemplateValidator.go @@ -4,8 +4,10 @@ import ( "encoding/json" "errors" "fmt" + + "github.com/devtron-labs/devtron/internal/util" "io/ioutil" - "log" + "os" "regexp" @@ -70,6 +72,9 @@ const cpu = "cpu" const memory = "memory" func DeploymentTemplateValidate(templatejson interface{}, schemafile string) (bool, error) { + + sugaredLogger := util.NewSugardLogger() + gojsonschema.FormatCheckers.Add("cpu", CpuChecker{}) gojsonschema.FormatCheckers.Add("memory", MemoryChecker{}) @@ -81,47 +86,51 @@ func DeploymentTemplateValidate(templatejson interface{}, schemafile string) (bo documentLoader := gojsonschema.NewGoLoader(templatejson) buff, err := json.Marshal(templatejson) if err != nil { - log.Fatal(err) + sugaredLogger.Error(err) return false, err } fmt.Println(string(buff)) result, err := gojsonschema.Validate(schemaLoader, documentLoader) if err != nil { - log.Fatal(err) + sugaredLogger.Error(err) return false, err } if result.Valid() { var dat map[string]interface{} if err := json.Unmarshal(buff, &dat); err != nil { - log.Fatal(err) + sugaredLogger.Error(err) return false, err } //limits and requests are mandatory fields in schema + autoscaleEnabled := dat["autoscaling"] + if autoscaleEnabled == nil { + fmt.Println(autoscaleEnabled) + }else if autoscaleEnabled.(map[string]interface{})["enabled"] == nil { + fmt.Println("hello") + }else{ + if autoscaleEnabled.(map[string]interface{})["enabled"].(bool) { + limit := dat["resources"].(map[string]interface{})["limits"].(map[string]interface{}) + request := dat["resources"].(map[string]interface{})["requests"].(map[string]interface{}) + + cpu_limit, _ := util2.CpuToNumber(limit["cpu"].(string)) + memory_limit, _ := util2.MemoryToNumber(limit["memory"].(string)) + cpu_request, _ := util2.CpuToNumber(request["cpu"].(string)) + memory_request, _ := util2.MemoryToNumber(request["memory"].(string)) + + envoproxy_limit := dat["envoyproxy"].(map[string]interface{})["resources"].(map[string]interface{})["limits"].(map[string]interface{}) + envoproxy_request := dat["envoyproxy"].(map[string]interface{})["resources"].(map[string]interface{})["requests"].(map[string]interface{}) + + envoproxy_cpu_limit, _ := util2.CpuToNumber(envoproxy_limit["cpu"].(string)) + envoproxy_memory_limit, _ := util2.MemoryToNumber(envoproxy_limit["memory"].(string)) + envoproxy_cpu_request, _ := util2.CpuToNumber(envoproxy_request["cpu"].(string)) + envoproxy_memory_request, _ := util2.MemoryToNumber(envoproxy_request["memory"].(string)) + if (envoproxy_cpu_limit < envoproxy_cpu_request) || (envoproxy_memory_limit < envoproxy_memory_request) || (cpu_limit < cpu_request) || (memory_limit < memory_request) { + return false, errors.New("requests is greater than limits") + } - autoscaleEnabled := dat["autoscaling"].(map[string]interface{}) - if autoscaleEnabled["enabled"].(bool) { - limit := dat["resources"].(map[string]interface{})["limits"].(map[string]interface{}) - request := dat["resources"].(map[string]interface{})["requests"].(map[string]interface{}) - - cpu_limit, _ := util2.CpuToNumber(limit["cpu"].(string)) - memory_limit, _ := util2.MemoryToNumber(limit["memory"].(string)) - cpu_request, _ := util2.CpuToNumber(request["cpu"].(string)) - memory_request, _ := util2.MemoryToNumber(request["memory"].(string)) - - envoproxy_limit := dat["envoyproxy"].(map[string]interface{})["resources"].(map[string]interface{})["limits"].(map[string]interface{}) - envoproxy_request := dat["envoyproxy"].(map[string]interface{})["resources"].(map[string]interface{})["requests"].(map[string]interface{}) - - envoproxy_cpu_limit, _ := util2.CpuToNumber(envoproxy_limit["cpu"].(string)) - envoproxy_memory_limit, _ := util2.MemoryToNumber(envoproxy_limit["memory"].(string)) - envoproxy_cpu_request, _ := util2.CpuToNumber(envoproxy_request["cpu"].(string)) - envoproxy_memory_request, _ := util2.MemoryToNumber(envoproxy_request["memory"].(string)) - if (envoproxy_cpu_limit < envoproxy_cpu_request) || (envoproxy_memory_limit < envoproxy_memory_request) || (cpu_limit < cpu_request) || (memory_limit < memory_request) { - return false, errors.New("requests is greater than limits") } - } - fmt.Println("ok") return true, nil } else { diff --git a/schema/reference-chart_3-10-0.json b/schema/reference-chart_3-10-0.json new file mode 100644 index 0000000000..442259829d --- /dev/null +++ b/schema/reference-chart_3-10-0.json @@ -0,0 +1,487 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "ContainerPort": { + "type": "array", + "items": { + "type": "object", + "properties": { + "envoyPort": { + "type": "integer", + "enum": [ + 8799, + 8800 + ] + }, + "idleTimeout": { + "type": "string" + }, + "name": { + "type": "string" + }, + "port": { + "type": "integer" + }, + "servicePort": { + "type": "integer" + }, + "supportStreaming": { + "type": "boolean" + }, + "useHTTP2": { + "type": "boolean" + } + }, + "required": [ + "port" + ] + } + }, + "EnvVariables": { + "type": "array", + "items": {} + }, + "GracePeriod": { + "type": "integer" + }, + "LivenessProbe": { + "type": "object", + "properties": { + "Path": { + "type": "string" + }, + "command": { + "type": "array", + "items": {} + }, + "failureThreshold": { + "type": "integer" + }, + "httpHeader": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, + "initialDelaySeconds": { + "type": "integer" + }, + "periodSeconds": { + "type": "integer" + }, + "port": { + "type": "integer" + }, + "scheme": { + "type": "string" + }, + "successThreshold": { + "type": "integer" + }, + "tcp": { + "type": "boolean" + }, + "timeoutSeconds": { + "type": "integer" + } + } + }, + "MaxSurge": { + "type": "integer" + }, + "MaxUnavailable": { + "type": "integer" + }, + "MinReadySeconds": { + "type": "integer" + }, + "ReadinessProbe": { + "type": "object", + "properties": { + "Path": { + "type": "string" + }, + "command": { + "type": "array", + "items": {} + }, + "failureThreshold": { + "type": "integer" + }, + "httpHeader": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, + "initialDelaySeconds": { + "type": "integer" + }, + "periodSeconds": { + "type": "integer" + }, + "port": { + "type": "integer" + }, + "scheme": { + "type": "string" + }, + "successThreshold": { + "type": "integer" + }, + "tcp": { + "type": "boolean" + }, + "timeoutSeconds": { + "type": "integer" + } + } + }, + "Spec": { + "type": "object", + "properties": { + "Affinity": { + "type": "object", + "properties": { + "Key": { + "type": "null" + }, + "Values": { + "type": "string" + }, + "key": { + "type": "string" + } + } + } + } + }, + "args": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "value": { + "type": "array", + "items": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ] + } + } + }, + "autoscaling": { + "type": "object", + "properties": { + "MaxReplicas": { + "type": "integer" + }, + "MinReplicas": { + "type": "integer" + }, + "TargetCPUUtilizationPercentage": { + "type": "integer" + }, + "TargetMemoryUtilizationPercentage": { + "type": "integer" + }, + "enabled": { + "type": "boolean" + }, + "extraMetrics": { + "type": "array", + "items": {} + } + } + }, + "command": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "value": { + "type": "array", + "items": {} + } + } + }, + "containers": { + "type": "array", + "items": {} + }, + "dbMigrationConfig": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + } + }, + "envoyproxy": { + "type": "object", + "properties": { + "configMapName": { + "type": "string" + }, + "image": { + "type": "string" + }, + "resources": { + "type": "object", + "properties": { + "limits": { + "type": "object", + "properties": { + "cpu": { + "type": "string" + }, + "memory": { + "type": "string" + } + }, + "required": [ + "cpu", + "memory" + ] + }, + "requests": { + "type": "object", + "properties": { + "cpu": { + "type": "string" + }, + "memory": { + "type": "string" + } + } + } + }, + "required": [ + "limits" + ] + } + } + }, + "image": { + "type": "object", + "properties": { + "pullPolicy": { + "type": "string" + } + } + }, + "ingress": { + "type": "object", + "properties": { + "annotations": { + "type": "object", + "patternproperties": { + "[a-zA-Z_]+[0-9.]+!@#%&*+-=:;?/><,.": { + "type": "string" + }, + "[a-zA-Z_]+[0-9.]+!@#%&*+-=:;?/.,><": { + "type": "string" + }, + "[a-zA-Z_]+[0-9.]+!@#&%*+-=:;?/><,.": { + "type": "string" + }, + "[a-zA-Z_]+[0-9.]+!@#&%+*-=:;?/><,.": { + "type": "string" + } + } + }, + "enabled": { + "type": "boolean" + }, + "host": { + "type": "string" + }, + "path": { + "type": "string" + }, + "tls": { + "type": "array", + "items": {} + } + } + }, + "ingressInternal": { + "type": "object", + "properties": { + "annotations": { + "type": "object", + "patternproperties": { + "[a-zA-Z_]+[0-9.]+!@#%&*+-=:;?/><,.": { + "type": "string" + }, + "[a-zA-Z_]+[0-9.]+!@#%&*+-=:;?/.,><": { + "type": "string" + }, + "[a-zA-Z_]+[0-9.]+!@#&%*+-=:;?/><,.": { + "type": "string" + }, + "[a-zA-Z_]+[0-9.]+!@#&%+*-=:;?/><,.": { + "type": "string" + } + } + }, + "enabled": { + "type": "boolean" + }, + "host": { + "type": "string" + }, + "path": { + "type": "string" + }, + "tls": { + "type": "array", + "items": {} + } + } + }, + "initContainers": { + "type": "array", + "items": {} + }, + "pauseForSecondsBeforeSwitchActive": { + "type": "integer" + }, + "prometheus": { + "type": "object", + "properties": { + "release": { + "type": "string" + } + } + }, + "rawYaml": { + "type": "array", + "items": {} + }, + "replicaCount": { + "type": "integer" + }, + "resources": { + "type": "object", + "properties": { + "limits": { + "type": "object", + "properties": { + "cpu": { + "type": "string" + }, + "memory": { + "type": "string" + } + }, + "required": [ + "cpu", + "memory" + ] + }, + "requests": { + "type": "object", + "properties": { + "cpu": { + "type": "string" + }, + "memory": { + "type": "string" + } + } + } + }, + "required": [ + "limits" + ] + }, + "secret": { + "type": "object", + "properties": { + "data": { + "type": "object" + }, + "enabled": { + "type": "boolean" + } + } + }, + "server": { + "type": "object", + "properties": { + "deployment": { + "type": "object", + "properties": { + "image": { + "type": "string" + }, + "image_tag": { + "type": "string" + } + } + } + } + }, + "service": { + "type": "object", + "properties": { + "annotations": { + "type": "object" + }, + "type": { + "type": "string" + } + } + }, + "servicemonitor": { + "type": "object", + "properties": { + "additionalLabels": { + "type": "object" + } + } + }, + "tolerations": { + "type": "array", + "items": {} + }, + "volumeMounts": { + "type": "array", + "items": {} + }, + "volumes": { + "type": "array", + "items": {} + }, + "waitForSecondsBeforeScalingDown": { + "type": "integer" + } + }, + "required": [ + "replicaCount", + "ContainerPort", + "ingress", + "ingressInternal" + ] + } \ No newline at end of file