package models
import (
"context"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"kubernetes-validation-beeyond/conf"
"kubernetes-validation-beeyond/services"
)
type Constraint struct {
Path string `json:"-"`
Min *float32 `json:"min,omitempty"`
Max *float32 `json:"max,omitempty"`
Enum []string `json:"enum,omitempty"`
Regex *string `json:"regex,omitempty"`
Disabled bool `json:"disabled,omitempty"`
GroupKindVersion GroupKindVersion `json:"-"`
}
// IsValid Extension method to Constraint
// checks if exactly one of MinMax, Enum and Regex is filled out, but not multiple
// and if MinMax is present if the valueType is integer, since it can only be used on integer fields
// Parameters:
// - constraint (Constraint): represents the constraint we want to validate
// - valueType (string): represents the type of the property (e.g. integer, string, ...)
// Returns: bool: true if valid, otherwise false
func (constraint Constraint) IsValid(valueType string) bool {
if constraint.Enum == nil && constraint.Min == nil && constraint.Max == nil && constraint.Regex == nil {
return false
}
isValidEnum := constraint.Enum != nil && constraint.Min == nil && constraint.Max == nil && constraint.Regex == nil
isValidMinMax := constraint.Enum == nil && constraint.Min != nil && constraint.Max != nil && constraint.Regex == nil && valueType == "integer"
isValidRegex := constraint.Enum == nil && constraint.Regex != nil && constraint.Min == nil && constraint.Max == nil
return isValidEnum || isValidMinMax || isValidRegex
}
// SaveConstraint Saves a constraint in the database
// Parameter: constraint (Constraint): represents the constraint we want to store
// Returns: error if anny occur when inserting
func SaveConstraint(constraint Constraint) error {
collection := services.GetClient().
Database(conf.Configuration.Database.Name).
Collection("Constraints")
_, err := collection.InsertOne(context.TODO(), constraint)
return err
}
// GetConstraint Gets the constraint that correspond to the given groupKindVersion and path from the database
// Parameters:
// - path (string): represents the path of the we want to get
// - groupKindVersion (*GroupKindVersion): represents the Group, Kind and Version of the constraint we want to get
// Returns: *Constraints: Represents the constraint that matches the path and group kind version
func GetConstraint(path string, groupKindVersion GroupKindVersion) *Constraint {
var constraint Constraint
err := services.GetClient().
Database(conf.Configuration.Database.Name).
Collection("Constraints").
FindOne(context.TODO(), bson.M{"path": path, "groupkindversion": groupKindVersion}).
Decode(&constraint)
if err != nil {
return nil
}
return &constraint
}
// GetConstraintsByGKV Gets all constraint that correspond to the given groupKindVersion from the database
// Parameter: groupKindVersion (*GroupKindVersion): represents the Group, Kind and Version of the constraints we want to get
// Returns: []*Constraints: An array of all constraints found constraints that match to given groupKindVersion
func GetConstraintsByGKV(groupKindVersion *GroupKindVersion) []*Constraint {
var constraints []*Constraint
cur, err := services.GetClient().
Database(conf.Configuration.Database.Name).
Collection("Constraints").
Find(context.TODO(), bson.M{"groupkindversion": groupKindVersion})
if err != nil {
return nil
}
for cur.Next(context.TODO()) {
var constr Constraint
if err := cur.Decode(&constr); err == nil {
constraints = append(constraints, &constr)
}
}
_ = cur.Close(context.TODO())
return constraints
}
// DeleteConstraint Deletes all Constraints from the database that match the given path and groupKindVersion
// Parameters:
// - path (string): Represents the path of the constraint we want to delete
// - groupKindVersion (GroupKindVersion): Represents the Group, Kind and Version of the constraint we want to delete
// Returns: the deleteResult (contains the number of deleted documents)
func DeleteConstraint(path string, groupKindVersion GroupKindVersion) *mongo.DeleteResult {
deleteResult, _ := services.GetClient().
Database(conf.Configuration.Database.Name).
Collection("Constraints").
DeleteMany(context.TODO(), bson.M{"path": path, "groupkindversion": groupKindVersion})
return deleteResult
}
// DeleteAll Deletes all Constraints from the database
// Returns: the deleteResult (contains the number of deleted documents)
func DeleteAll() *mongo.DeleteResult {
deleteResult, _ := services.GetClient().
Database(conf.Configuration.Database.Name).
Collection("Constraints").
DeleteMany(context.TODO(), bson.M{})
return deleteResult
}
package models
import (
"encoding/json"
"kubernetes-validation-beeyond/conf"
"net/http"
"strings"
)
type SchemaCollection struct {
Schemas map[string]*Schema `json:"definitions"`
}
type Schema struct {
Description string `json:"description"`
Required []string `json:"required"`
Type string `json:"type"`
Properties map[string]*Property `json:"properties"`
GroupKindVersion []GroupKindVersion `json:"x-kubernetes-group-version-kind,omitempty"`
Constraint *Constraint `json:"x-constraint,omitempty"`
}
type Property struct {
Description string `json:"description,omitempty"`
Type string `json:"type,omitempty"`
Format string `json:"format,omitempty"`
Reference string `json:"$ref,omitempty"`
Items *PropertyItem `json:"items,omitempty"`
Enum []string `json:"enum,omitempty"`
Constraint *Constraint `json:"x-constraint,omitempty"`
IsKubernetesObject bool `json:"x-is-kubernetes-object"`
}
type PropertyItem struct {
Type string `json:"type,omitempty"`
Reference string `json:"$ref,omitempty"`
}
type GroupKindVersion struct {
Group string `json:"group"`
Kind string `json:"kind"`
Version string `json:"version"`
}
type PathNotFoundError struct{}
func (p PathNotFoundError) Error() string {
return "Path not found"
}
// Extension function to GroupKindVersion which just puts the
// properties Group Kind and Version to lowercase
// Parameters: groupKindVersion (GroupKindVersion), object which we want to lower
// Returns: GroupKindVersion: the object containing the lowercase properties
func (groupKindVersion GroupKindVersion) ToLower() GroupKindVersion {
var groupKindVersionLower GroupKindVersion
groupKindVersionLower.Group = strings.ToLower(groupKindVersion.Group)
groupKindVersionLower.Kind = strings.ToLower(groupKindVersion.Kind)
groupKindVersionLower.Version = strings.ToLower(groupKindVersion.Version)
return groupKindVersionLower
}
// Gets all schemas and return them in the SchemaCollection
// Returns the SchemaCollection and an error if one occurred
func GetSchemaCollection() (*SchemaCollection, error) {
baseUrl := conf.Configuration.KubernetesJsonschema.Url
kubernetesVersion := conf.Configuration.KubernetesJsonschema.KubernetesVersion
versionType := kubernetesVersion + "-standalone-strict"
url := baseUrl + "/" + versionType + "/_definitions.json"
response, _ := http.Get(url)
collection := &SchemaCollection{}
err := json.NewDecoder(response.Body).Decode(collection)
if err != nil {
return nil, err
}
return collection, nil
}
// GetSchemaBySegments Gets the schema corresponding to the given segments
// Parameter: segments ([]string): Represents the "path" to the schema (e.g.: ["Deployment-apps-v1", "spec"])
// Returns the schema and any error that occurred
func GetSchemaBySegments(segments []string) (*Schema, error) {
collection, err := GetSchemaCollection()
if err != nil {
return nil, err
}
var currentSchema *Schema
for i, segment := range segments {
// On the first element search for the GroupKindVersion
if i == 0 {
schemaLoop:
for _, schema := range collection.Schemas {
if len(schema.GroupKindVersion) > 0 {
groupKindVersion := schema.GroupKindVersion[0]
var group string
if groupKindVersion.Group == "" {
group = ""
} else {
group = "-" + groupKindVersion.Group
}
groupKindVersionString := groupKindVersion.Kind + group + "-" + groupKindVersion.Version
if segment == groupKindVersionString {
currentSchema = schema
break schemaLoop
}
}
}
if currentSchema == nil {
return nil, PathNotFoundError{}
}
} else {
property := currentSchema.Properties[segment]
if property == nil {
return nil, PathNotFoundError{}
}
var referencePath string
if property.Reference != "" {
referencePath = property.Reference
} else if property.Items != nil {
referencePath = property.Items.Reference
}
// If the specified path of the user does not exist, return
// This means the user requested something other than object (string, int, ...)
if referencePath == "" {
return nil, PathNotFoundError{}
}
// We want the last part of the reference
// Example: #/definitions/io.k8s.api.apps.v1.DeploymentSpec
split := strings.Split(referencePath, "/")
definitionName := split[len(split)-1]
currentSchema = collection.Schemas[definitionName]
}
}
groupKindVersion, constraintPath := GetGroupKindVersionAndPathFromSegments(segments)
// Attach constraint to the properties if it exists
for propertyName, property := range currentSchema.Properties {
var referencePath string
if property.Reference != "" {
referencePath = property.Reference
} else if property.Type == "array" {
referencePath = property.Items.Reference
}
if referencePath != "" {
// turn: #/definitions/xxx
// into this: xxx
split := strings.Split(referencePath, "/")
definitionName := split[len(split)-1]
// If the reference is of type object and has properties we declare it as kubernetes object
// Add new checks if type object and properties are not enough to determine a kubernetes object
if collection.Schemas[definitionName].Type == "object" && collection.Schemas[definitionName].Properties != nil {
property.IsKubernetesObject = true
}
}
var path string
if constraintPath == "" {
path = propertyName
} else {
path = constraintPath + "." + propertyName
}
property.Constraint = GetConstraint(path, groupKindVersion)
}
if len(segments) > 1 {
constraintPath = strings.Join(segments[1:], ".")
} else {
constraintPath = ""
}
currentSchema.Constraint = GetConstraint(constraintPath, groupKindVersion)
if currentSchema.Type != "" {
delete(currentSchema.Properties, "apiVersion")
delete(currentSchema.Properties, "kind")
return currentSchema, nil
} else {
return nil, PathNotFoundError{}
}
}
// GetGroupKindVersionAndPathFromSegments Gets the group kind version and the path from segments
// Parameter: segments ([]string): e.g.: ["Deployment-apps-v1", "spec", "replicas"])
// Returns:
// - GroupKindVersion (GroupKindVersion) e.g.: group: apps, kind: Deployment, version: v1
// - path (string) e.g.: spec.replicas
func GetGroupKindVersionAndPathFromSegments(segments []string) (GroupKindVersion, string) {
var groupKindVersion GroupKindVersion
parts := strings.Split(segments[0], "-")
groupKindVersion.Kind = parts[0]
if len(parts) == 3 {
groupKindVersion.Group = parts[1]
groupKindVersion.Version = parts[2]
} else {
groupKindVersion.Version = parts[1]
}
constraintPath := strings.Join(segments[1:], ".")
return groupKindVersion, constraintPath
}
// Checks if segments represents a valid path for constraints
// Parameter: segments ([]string): first element represents the Group Kind Version, remaining elements represetn the path
// returns bool: true when the path is valid
func IsValidConstraintPath(segments []string) bool {
var lastSegment *string
if len(segments) != 1 {
lastSegment = &segments[len(segments)-1]
segments = segments[0 : len(segments)-1]
}
currentSchema, err := GetSchemaBySegments(segments)
return err == nil && (lastSegment == nil || currentSchema.Properties[*lastSegment] != nil)
}
package models
import (
"encoding/json"
"fmt"
"github.com/instrumenta/kubeval/kubeval"
"gopkg.in/yaml.v2"
"regexp"
"strconv"
"strings"
)
type NoContentError struct{}
func (e *NoContentError) Error() string {
return "No Content"
}
type ValidationError struct {
Message string `json:"message"`
Value string `json:"value"`
Key string `json:"key"`
}
// Validates the content (syntax wise) checks the constraints
// Parameter: content (string) represents the content of the yaml file,
// which will be validated.
// returns all constraint-errors in []ValidationError and the kubeval error
func ValidateContent(content string) ([]ValidationError, error) {
if len(content) == 0 {
return nil, &NoContentError{}
}
config := kubeval.NewDefaultConfig()
contentBytes := []byte(content)
validationResults, err := kubeval.Validate(contentBytes, config)
if err != nil {
return nil, err
}
var validationError []ValidationError
for _, result := range validationResults {
for _, resultError := range result.Errors {
fieldDetail := resultError.Details()["field"]
var field string
if fieldDetail != nil {
field = fieldDetail.(string)
} else {
field = ""
}
bytes, _ := json.Marshal(resultError.Value())
validationError = append(validationError, ValidationError{
Message: resultError.Description(),
Value: string(bytes),
Key: field,
})
}
}
var groupKindVersion GroupKindVersion
yamlMap := make(map[interface{}]interface{})
err = yaml.Unmarshal([]byte(content), &yamlMap)
groupKindVersion.Kind = getValueFromPath(yamlMap, "kind").(string)
groupversion := getValueFromPath(yamlMap, "apiVersion").(string)
groupVersionSplit := strings.Split(groupversion, "/")
if len(groupVersionSplit) == 1 {
groupKindVersion.Version = groupVersionSplit[0]
} else {
groupKindVersion.Group = groupVersionSplit[0]
groupKindVersion.Version = groupVersionSplit[1]
}
constraints := GetConstraintsByGKV(&groupKindVersion)
for _, currentConstraint := range constraints {
errorDescription := ""
value := getValueFromPath(yamlMap, currentConstraint.Path)
var actual string
var ok bool
isArray := false
if currentConstraint.Disabled && currentConstraint.Path == "" {
errorDescription = fmt.Sprintf("This root object is disabled")
actual = fmt.Sprintf("%s", currentConstraint.GroupKindVersion)
} else if currentConstraint.Disabled && value != nil {
errorDescription = fmt.Sprintf("Found disabled field (%s)", currentConstraint.Path)
} else {
if actual, ok = value.(string); !ok {
if number, ok := value.(int); ok {
actual = strconv.Itoa(number)
} else if arr, ok := value.([]interface{}); ok {
actual = strings.Join(strings.Fields(fmt.Sprint(arr)), ", ")
isArray = true
} else if boolValue, ok := value.(bool); ok {
actual = strconv.FormatBool(boolValue)
}
}
if currentConstraint.Max != nil {
if isArray {
for _, currentValue := range value.([]interface{}) {
if !isBetweenMinMax(currentConstraint, currentValue.(int)) {
errorDescription = fmt.Sprintf("Given value out of range (%.0f-%.0f)", *currentConstraint.Min, *currentConstraint.Max)
break
}
}
} else if !isBetweenMinMax(currentConstraint, value.(int)) {
errorDescription = fmt.Sprintf("Given value out of range (%.0f-%.0f)", *currentConstraint.Min, *currentConstraint.Max)
}
} else if currentConstraint.Enum != nil {
if isArray {
isValid := true
for _, currentValue := range strings.Split(actual[1:len(actual)-1], ", ") {
if !contains(currentConstraint.Enum, currentValue) {
isValid = false
}
}
if !isValid {
errorDescription = "Constraint enum does not contain given one or more of the given values"
}
} else if !contains(currentConstraint.Enum, actual) {
errorDescription = "Constraint enum does not contain given value"
}
} else if currentConstraint.Regex != nil {
if isArray {
isValid := true
for _, currentValue := range strings.Split(actual[1:len(actual)-1], ", ") {
if !matchesRegex(*currentConstraint.Regex, currentValue) {
isValid = false
}
}
if !isValid {
errorDescription = "One or more of the given value does not match the regex"
}
} else if !matchesRegex(*currentConstraint.Regex, actual) {
errorDescription = "Given value does not match regex"
}
}
}
if errorDescription != "" {
validationError = append(validationError, ValidationError{
Message: errorDescription,
Value: actual,
Key: currentConstraint.Path,
})
}
}
return validationError, nil
}
// Checks whether the given string array contains the given searchText
// Parameters:
// - enum ([]string): array which we search through
// - searchText (string): the string we look for in the array
// Returns boolean: true if the array contains the searchText
func contains(enum []string, searchText string) bool {
for _, currentValue := range enum {
if currentValue == searchText {
return true
}
}
return false
}
// Checks whether the given text matches the given regex
// Parameters:
// - regex (string): represents the regex
// - text (string): the text that should match the regex
// Returns bool: true if the text matches the regex
func matchesRegex(regex string, text string) bool {
// TODO: "^"+*currentConstraint.Regex+"$"
matched, _ := regexp.MatchString("^"+regex+"$", text)
return matched
}
// Checks whether the given value is between the min and max values given within the currentConstraint
// Parameters:
// - currentConstraint (*Constraint): Contains the min and max values
// - value (int): integer which should be between min and max
// Returns: bool: true if value is between min and max, otherwise false
func isBetweenMinMax(currentConstraint *Constraint, value int) bool {
actualFloat := float64(value)
return actualFloat <= float64(*currentConstraint.Max) && actualFloat >= float64(*currentConstraint.Min)
}
// Gets the value of the property by the given path from the given k8s specification (map)
// Parameters:
// - m (map[interface{}]interface{}): Represents the content of the given yaml file as a map
// - path (string): Represents the
func getValueFromPath(m map[interface{}]interface{}, path string) interface{} {
var obj interface{} = m
var val interface{} = nil
parts := strings.Split(path, ".")
for _, p := range parts {
if v, ok := obj.(map[interface{}]interface{}); ok {
obj = v[p]
val = obj
} else {
return nil
}
}
return val
}
package routers
import (
"kubernetes-validation-beeyond/models"
"net/http"
"strings"
"github.com/gin-gonic/gin"
)
// @Summary Find root constraints
// @Description Finds all root schemes and their constraints
// @Tags Constraint
// @Success 200 {string} string "ok"
// @Failure 500 {string} string "internal server error"
// @Router /api/constraints/ [get]
func listRootConstraints(c *gin.Context) {
collection, err := models.GetSchemaCollection()
if err != nil {
c.Writer.WriteHeader(http.StatusInternalServerError)
return
}
var kubernetesRootDefinitions []*models.Schema
for _, schema := range collection.Schemas {
groupKindVersions := schema.GroupKindVersion
if len(groupKindVersions) > 0 {
schema.Constraint = models.GetConstraint("", groupKindVersions[0])
kubernetesRootDefinitions = append(kubernetesRootDefinitions, schema)
}
delete(schema.Properties, "apiVersion")
delete(schema.Properties, "kind")
for _, property := range schema.Properties {
var referencePath string
if property.Reference != "" {
referencePath = property.Reference
} else if property.Type == "array" {
referencePath = property.Items.Reference
}
if referencePath != "" {
split := strings.Split(referencePath, "/")
definitionName := split[len(split)-1]
if collection.Schemas[definitionName].Type == "object" && collection.Schemas[definitionName].Properties != nil {
property.IsKubernetesObject = true
}
}
}
}
c.JSON(http.StatusOK, kubernetesRootDefinitions)
}
// @Summary Find constraints by path
// @Description Finds the schema and its constraints according to the given path
// @Tags Constraint
// @Param "path" path string true "path"
// @Success 200 {string} string "ok"
// @Failure 400 {string} string "bad request"
// @Router /api/constraints/{path} [get]
func getConstraintsByPath(c *gin.Context) {
segments := c.GetStringSlice("pathSegments")
schema, err := models.GetSchemaBySegments(segments)
if err != nil {
c.Writer.WriteHeader(http.StatusNotFound)
return
}
c.JSON(http.StatusOK, schema)
}
// @Summary Creates a new constraint
// @Description creates a new constraint and adds it to the database. If the constraint already exists it gets replaced.
// @Tags Constraint
// @Accept json
// @Param "path" path string true "path"
// @Success 201 {string} string "created"
// @Failure 400 {string} string "bad request"
// @Failure 500 {string} string "internal server error"
// @Router /api/constraints/{path} [post]
func createConstraintByPath(c *gin.Context) {
var constraint models.Constraint
if err := c.ShouldBindJSON(&constraint); err != nil {
c.Writer.WriteHeader(http.StatusBadRequest)
return
}
lastSegment := c.GetString("lastPropertyName")
schemaInterface, _ := c.Get("schema")
schema := schemaInterface.(*models.Schema)
if schema.Properties[lastSegment] != nil && !schema.Properties[lastSegment].IsKubernetesObject && !constraint.IsValid(schema.Properties[lastSegment].Type) {
c.Writer.WriteHeader(http.StatusBadRequest)
return
}
groupKindVersionInterface, _ := c.Get("groupKindVersion")
constraint.Path = c.GetString("propertyPath")
// check if constraint on apiVersion or kind
if strings.HasSuffix(constraint.Path, "apiVersion") || strings.HasSuffix(constraint.Path, "kind") {
c.Writer.WriteHeader(http.StatusBadRequest)
return
}
constraint.GroupKindVersion = groupKindVersionInterface.(models.GroupKindVersion)
models.DeleteConstraint(constraint.Path, constraint.GroupKindVersion)
if err := models.SaveConstraint(constraint); err != nil {
c.Writer.WriteHeader(http.StatusInternalServerError)
return
}
c.Writer.WriteHeader(http.StatusCreated)
}
// @Summary Delete constraint
// @Description Deletes the constraint with the given path
// @Tags Constraint
// @Param "path" path string true "path"
// @Success 204 {string} string "no content"
// @Failure 400 {string} string "bad request"
// @Router /api/constraints/{path} [delete]
func deleteConstraintByPath(c *gin.Context) {
groupKindVersion, _ := c.Get("groupKindVersion")
propertyPath := c.GetString("propertyPath")
if models.DeleteConstraint(propertyPath, groupKindVersion.(models.GroupKindVersion)).DeletedCount == 0 {
c.Writer.WriteHeader(http.StatusBadRequest)
return
}
c.Writer.WriteHeader(http.StatusNoContent)
}
// @Summary Toggle disabled on constraint
// @Description Toggles the "disabled" from the constraint with the given path. If the given constraint does not exist, it will be created
// @Tags Constraint
// @Param "path" path string true "path"
// @Success 200 {string} string "ok"
// @Failure 400 {string} string "bad request"
// @Failure 500 {string} string "internal server error"
// @Router /api/constraints/{path} [patch]
func toggleDisableConstraintByPath(c *gin.Context) {
groupKindVersionInterface, _ := c.Get("groupKindVersion")
propertyPath := c.GetString("propertyPath")
groupKindVersion := groupKindVersionInterface.(models.GroupKindVersion)
constraint := models.GetConstraint(propertyPath, groupKindVersion)
// If no constraint exits and the user wants to disable the path, create a new constraint
if constraint == nil {
constraint = &models.Constraint{
Path: propertyPath,
Disabled: false,
GroupKindVersion: groupKindVersion,
}
}
lastSegment := c.GetString("lastPropertyName")
schemaInterface, _ := c.Get("schema")
schema := schemaInterface.(*models.Schema)
for _, req := range schema.Required {
if req == lastSegment {
c.Writer.WriteHeader(http.StatusBadRequest)
return
}
}
constraint.Disabled = !constraint.Disabled
models.DeleteConstraint(propertyPath, groupKindVersion)
if models.SaveConstraint(*constraint) != nil {
c.Writer.WriteHeader(http.StatusInternalServerError)
return
}
schema.Constraint = constraint
c.Writer.WriteHeader(http.StatusOK)
}
package routers
import (
"flag"
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
"kubernetes-validation-beeyond/conf"
_ "kubernetes-validation-beeyond/docs"
"kubernetes-validation-beeyond/middleware"
"net/http"
)
// Creates an Engine with all endpoint, their paths and the used middleware
// Returns: *gin.Engine: an Engine with all defined Endpoints and the used middleware
func GetRouter() *gin.Engine {
router := gin.Default()
router.Use(middleware.Cors())
api := router.Group("/api")
{
api.GET("/swagger-ui", func(c *gin.Context) {
c.Redirect(http.StatusMovedPermanently, "swagger/index.html")
})
// validate
api.POST("/validate", getValidationResult)
api.Use(middleware.PathSegments())
api.Use(middleware.ProvideSchema())
url := ginSwagger.URL("http://localhost:8180/api/swagger/doc.json")
api.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, url))
// constraints
constraints := api.Group("/constraints")
{
if flag.Lookup("test.v") == nil {
constraints.Use(middleware.Oidc())
constraints.Use(middleware.Rbac())
}
constraints.GET("", listRootConstraints)
constraints.GET("/*path", getConstraintsByPath)
pathValid := middleware.PathValid()
constraints.POST("/*path", pathValid, createConstraintByPath)
constraints.DELETE("/*path", pathValid, deleteConstraintByPath)
constraints.PATCH("/*path", pathValid, toggleDisableConstraintByPath)
}
}
return router
}
// Initialises the Router and runs it
func Init() error {
router := GetRouter()
return router.Run(conf.Configuration.Server.HttpPort)
}
package routers
import (
"github.com/gin-gonic/gin"
"kubernetes-validation-beeyond/models"
"net/http"
)
// @Summary Validate content
// @Description Validates the given content
// @Tags Validation
// @Produce json
// @Success 200 {string} string "ok"
// @Router /api/validate/ [post]
func getValidationResult(c *gin.Context) {
data, _ := c.GetRawData()
yamlContent := string(data)
results, err := models.ValidateContent(yamlContent)
if err != nil {
// TODO: find what errors can occur and return them if ok
results = append(results, models.ValidationError{
Message: "YAML-Format not valid",
Value: yamlContent,
Key: "content",
})
}
c.JSON(http.StatusOK, results)
}