cloudfoundry/cf-k8s-controllers

View on GitHub
api/payloads/app.go

Summary

Maintainability
A
0 mins
Test Coverage
B
83%
package payloads

import (
    "fmt"
    "net/url"
    "regexp"

    "code.cloudfoundry.org/korifi/api/config"
    "code.cloudfoundry.org/korifi/api/payloads/parse"
    "code.cloudfoundry.org/korifi/api/payloads/validation"
    "code.cloudfoundry.org/korifi/api/repositories"
    jellidation "github.com/jellydator/validation"
)

// DefaultLifecycleConfig is overwritten by main.go
var DefaultLifecycleConfig = config.DefaultLifecycleConfig{
    Type:            "buildpack",
    Stack:           "cflinuxfs3",
    StagingMemoryMB: 1024,
}

type AppCreate struct {
    Name                 string            `json:"name"`
    EnvironmentVariables map[string]string `json:"environment_variables"`
    Relationships        *AppRelationships `json:"relationships"`
    Lifecycle            *Lifecycle        `json:"lifecycle"`
    Metadata             Metadata          `json:"metadata"`
}

var appNameRegex = regexp.MustCompile(`^[-\w]+$`)

func (c AppCreate) Validate() error {
    return jellidation.ValidateStruct(&c,
        jellidation.Field(&c.Name, jellidation.Required, jellidation.Match(appNameRegex).Error("name must consist only of letters, numbers, underscores and dashes")),
        jellidation.Field(&c.Relationships, jellidation.NotNil),
        jellidation.Field(&c.Lifecycle),
        jellidation.Field(&c.Metadata),
    )
}

type AppRelationships struct {
    Space *Relationship `json:"space"`
}

func (r AppRelationships) Validate() error {
    return jellidation.ValidateStruct(&r,
        jellidation.Field(&r.Space, jellidation.NotNil),
    )
}

func (p AppCreate) ToAppCreateMessage() repositories.CreateAppMessage {
    lifecycleBlock := repositories.Lifecycle{
        Type: DefaultLifecycleConfig.Type,
        Data: repositories.LifecycleData{
            Stack: DefaultLifecycleConfig.Stack,
        },
    }
    if p.Lifecycle != nil {
        lifecycleBlock.Type = p.Lifecycle.Type
        lifecycleBlock.Data.Stack = p.Lifecycle.Data.Stack
        lifecycleBlock.Data.Buildpacks = p.Lifecycle.Data.Buildpacks
    }

    return repositories.CreateAppMessage{
        Name:                 p.Name,
        SpaceGUID:            p.Relationships.Space.Data.GUID,
        Metadata:             repositories.Metadata(p.Metadata),
        State:                repositories.StoppedState,
        Lifecycle:            lifecycleBlock,
        EnvironmentVariables: p.EnvironmentVariables,
    }
}

type AppSetCurrentDroplet struct {
    Relationship `json:",inline"`
}

func (d AppSetCurrentDroplet) Validate() error {
    return jellidation.ValidateStruct(&d,
        jellidation.Field(&d.Relationship, jellidation.NotNil),
    )
}

type AppList struct {
    Names         string
    GUIDs         string
    SpaceGuids    string
    OrderBy       string
    LabelSelector string
}

func (a AppList) Validate() error {
    return jellidation.ValidateStruct(&a,
        jellidation.Field(&a.OrderBy, validation.OneOfOrderBy("created_at", "updated_at", "name", "state")),
    )
}

func (a *AppList) ToMessage() repositories.ListAppsMessage {
    return repositories.ListAppsMessage{
        Names:         parse.ArrayParam(a.Names),
        Guids:         parse.ArrayParam(a.GUIDs),
        SpaceGuids:    parse.ArrayParam(a.SpaceGuids),
        LabelSelector: a.LabelSelector,
    }
}

func (a *AppList) SupportedKeys() []string {
    return []string{"names", "guids", "space_guids", "order_by", "per_page", "page", "label_selector"}
}

func (a *AppList) DecodeFromURLValues(values url.Values) error {
    a.Names = values.Get("names")
    a.GUIDs = values.Get("guids")
    a.SpaceGuids = values.Get("space_guids")
    a.OrderBy = values.Get("order_by")
    a.LabelSelector = values.Get("label_selector")
    return nil
}

type AppPatchEnvVars struct {
    Var map[string]interface{} `json:"var"`
}

func (p AppPatchEnvVars) Validate() error {
    return jellidation.ValidateStruct(&p,
        jellidation.Field(&p.Var,
            validation.StrictlyRequired,
            jellidation.Map().Keys(
                validation.NotStartWith("VCAP_"),
                validation.NotStartWith("VMC_"),
                validation.NotEqual("PORT"),
            ).AllowExtraKeys(),
        ))
}

func (a *AppPatchEnvVars) ToMessage(appGUID, spaceGUID string) repositories.PatchAppEnvVarsMessage {
    message := repositories.PatchAppEnvVarsMessage{
        AppGUID:              appGUID,
        SpaceGUID:            spaceGUID,
        EnvironmentVariables: map[string]*string{},
    }

    for k, v := range a.Var {
        switch v := v.(type) {
        case nil:
            message.EnvironmentVariables[k] = nil
        case bool:
            stringVar := fmt.Sprintf("%t", v)
            message.EnvironmentVariables[k] = &stringVar
        case float32:
            stringVar := fmt.Sprintf("%f", v)
            message.EnvironmentVariables[k] = &stringVar
        case int:
            stringVar := fmt.Sprintf("%d", v)
            message.EnvironmentVariables[k] = &stringVar
        case string:
            message.EnvironmentVariables[k] = &v
        }
    }

    return message
}

type AppPatch struct {
    Name      string          `json:"name"`
    Metadata  MetadataPatch   `json:"metadata"`
    Lifecycle *LifecyclePatch `json:"lifecycle"`
}

func (p AppPatch) Validate() error {
    return jellidation.ValidateStruct(&p,
        jellidation.Field(&p.Name, jellidation.Match(appNameRegex).Error("name must consist only of letters, numbers, underscores and dashes")),
        jellidation.Field(&p.Metadata),
        jellidation.Field(&p.Lifecycle),
    )
}

func (a *AppPatch) ToMessage(appGUID, spaceGUID string) repositories.PatchAppMessage {
    msg := repositories.PatchAppMessage{
        AppGUID:   appGUID,
        SpaceGUID: spaceGUID,
        Name:      a.Name,
        MetadataPatch: repositories.MetadataPatch{
            Annotations: a.Metadata.Annotations,
            Labels:      a.Metadata.Labels,
        },
    }

    if a.Lifecycle != nil {
        msg.Lifecycle = &repositories.LifecyclePatch{}

        if a.Lifecycle.Type != "" {
            msg.Lifecycle.Type = &a.Lifecycle.Type
        }

        if a.Lifecycle.Data != nil {
            msg.Lifecycle.Data = &repositories.LifecycleDataPatch{
                Stack:      a.Lifecycle.Data.Stack,
                Buildpacks: a.Lifecycle.Data.Buildpacks,
            }
        }
    }

    return msg
}