Skip to content

Commit

Permalink
refactor: reuse Argo CD types to manage manifest generation (#201)
Browse files Browse the repository at this point in the history
* refactor: reuse Argo CD types to manage manifest generation

Signed-off-by: Alexander Matyushentsev <[email protected]>

* address reviewer notes

Signed-off-by: Alexander Matyushentsev <[email protected]>

* fix configManagementConfig field schema

Signed-off-by: Alexander Matyushentsev <[email protected]>

---------

Signed-off-by: Alexander Matyushentsev <[email protected]>
  • Loading branch information
alexmt authored Oct 26, 2023
1 parent 0c63b13 commit bb12522
Show file tree
Hide file tree
Showing 28 changed files with 649 additions and 994 deletions.
234 changes: 234 additions & 0 deletions argocd-schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "argocd-schema.json",
"definitions": {
"helm": {
"description": "Helm holds helm specific options",
"properties": {
"fileParameters": {
"description": "FileParameters are file parameters to the helm template",
"items": {
"description": "HelmFileParameter is a file parameter that's passed to helm template during manifest generation",
"properties": {
"name": {
"description": "Name is the name of the Helm parameter",
"type": "string"
},
"path": {
"description": "Path is the path to the file containing the values for the Helm parameter",
"type": "string"
}
},
"type": "object"
},
"type": "array"
},
"ignoreMissingValueFiles": {
"description": "IgnoreMissingValueFiles prevents helm template from failing when valueFiles do not exist locally by not appending them to helm template --values",
"type": "boolean"
},
"parameters": {
"description": "Parameters is a list of Helm parameters which are passed to the helm template command upon manifest generation",
"items": {
"description": "HelmParameter is a parameter that's passed to helm template during manifest generation",
"properties": {
"forceString": {
"description": "ForceString determines whether to tell Helm to interpret booleans and numbers as strings",
"type": "boolean"
},
"name": {
"description": "Name is the name of the Helm parameter",
"type": "string"
},
"value": {
"description": "Value is the value for the Helm parameter",
"type": "string"
}
},
"type": "object"
},
"type": "array"
},
"passCredentials": {
"description": "PassCredentials pass credentials to all domains (Helm's --pass-credentials)",
"type": "boolean"
},
"releaseName": {
"description": "ReleaseName is the Helm release name to use. If omitted it will use the application name",
"type": "string"
},
"skipCrds": {
"description": "SkipCrds skips custom resource definition installation step (Helm's --skip-crds)",
"type": "boolean"
},
"valueFiles": {
"description": "ValuesFiles is a list of Helm value files to use when generating a template",
"items": {
"type": "string"
},
"type": "array"
},
"values": {
"description": "Values specifies Helm values to be passed to helm template, typically defined as a block. ValuesObject takes precedence over Values, so use one or the other.",
"type": "string"
},
"valuesObject": {
"description": "ValuesObject specifies Helm values to be passed to helm template, defined as a map. This takes precedence over Values.",
"type": "object",
"x-kubernetes-preserve-unknown-fields": true
},
"version": {
"description": "Version is the Helm version to use for templating (\"3\")",
"type": "string"
}
},
"type": "object"
},
"kustomize": {
"description": "Kustomize holds kustomize specific options",
"properties": {
"commonAnnotations": {
"additionalProperties": {
"type": "string"
},
"description": "CommonAnnotations is a list of additional annotations to add to rendered manifests",
"type": "object"
},
"commonAnnotationsEnvsubst": {
"description": "CommonAnnotationsEnvsubst specifies whether to apply env variables substitution for annotation values",
"type": "boolean"
},
"commonLabels": {
"additionalProperties": {
"type": "string"
},
"description": "CommonLabels is a list of additional labels to add to rendered manifests",
"type": "object"
},
"forceCommonAnnotations": {
"description": "ForceCommonAnnotations specifies whether to force applying common annotations to resources for Kustomize apps",
"type": "boolean"
},
"forceCommonLabels": {
"description": "ForceCommonLabels specifies whether to force applying common labels to resources for Kustomize apps",
"type": "boolean"
},
"images": {
"description": "Images is a list of Kustomize image override specifications",
"items": {
"description": "KustomizeImage represents a Kustomize image definition in the format [old_image_name=]<image_name>:<image_tag>",
"type": "string"
},
"type": "array"
},
"namePrefix": {
"description": "NamePrefix is a prefix appended to resources for Kustomize apps",
"type": "string"
},
"nameSuffix": {
"description": "NameSuffix is a suffix appended to resources for Kustomize apps",
"type": "string"
},
"namespace": {
"description": "Namespace sets the namespace that Kustomize adds to all resources",
"type": "string"
},
"replicas": {
"description": "Replicas is a list of Kustomize Replicas override specifications",
"items": {
"properties": {
"count": {
"anyOf": [
{
"type": "integer"
},
{
"type": "string"
}
],
"description": "Number of replicas",
"x-kubernetes-int-or-string": true
},
"name": {
"description": "Name of Deployment or StatefulSet",
"type": "string"
}
},
"required": [
"count",
"name"
],
"type": "object"
},
"type": "array"
},
"version": {
"description": "Version controls which version of Kustomize to use for rendering manifests",
"type": "string"
}
},
"type": "object"
},
"plugin": {
"description": "Plugin holds config management plugin specific options",
"properties": {
"env": {
"description": "Env is a list of environment variable entries",
"items": {
"description": "EnvEntry represents an entry in the application's environment",
"properties": {
"name": {
"description": "Name is the name of the variable, usually expressed in uppercase",
"type": "string"
},
"value": {
"description": "Value is the value of the variable",
"type": "string"
}
},
"required": [
"name",
"value"
],
"type": "object"
},
"type": "array"
},
"name": {
"type": "string"
},
"parameters": {
"items": {
"properties": {
"array": {
"description": "Array is the value of an array type parameter.",
"items": {
"type": "string"
},
"type": "array"
},
"map": {
"additionalProperties": {
"type": "string"
},
"description": "Map is the value of a map type parameter.",
"type": "object"
},
"name": {
"description": "Name is the name identifying a parameter.",
"type": "string"
},
"string": {
"description": "String_ is the value of a string type parameter.",
"type": "string"
}
},
"type": "object"
},
"type": "array"
}
},
"type": "object"
}
}
}
82 changes: 36 additions & 46 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,30 @@ import (
"github.com/pkg/errors"
"github.com/xeipuuv/gojsonschema"

"github.com/akuity/kargo-render/internal/argocd"
"github.com/akuity/kargo-render/internal/file"
"github.com/akuity/kargo-render/internal/helm"
"github.com/akuity/kargo-render/internal/kustomize"
"github.com/akuity/kargo-render/internal/ytt"

_ "embed"
)

//go:embed schema.json
var configSchemaBytes []byte
var configSchemaJSONLoader gojsonschema.JSONLoader

//go:embed argocd-schema.json
var argocdConfigSchemaBytes []byte

var configSchema *gojsonschema.Schema

func init() {
configSchemaJSONLoader = gojsonschema.NewBytesLoader(configSchemaBytes)
sl := gojsonschema.NewSchemaLoader()
if err := sl.AddSchema("argocd-schema.json", gojsonschema.NewBytesLoader(argocdConfigSchemaBytes)); err != nil {
panic(fmt.Sprintf("error adding Argo CD schema: %s", err))
}

var err error
if configSchema, err = sl.Compile(gojsonschema.NewBytesLoader(configSchemaBytes)); err != nil {
panic(fmt.Sprintf("error compiling schema: %s", err))
}
}

// repoConfig encapsulates all Kargo Render configuration options for a
Expand All @@ -48,7 +58,7 @@ func (r *repoConfig) GetBranchConfig(name string) (branchConfig, error) {
}
submatches := regex.FindStringSubmatch(name)
if len(submatches) > 0 {
return cfg.expand(submatches), nil
return cfg.expand(submatches)
}
}
}
Expand Down Expand Up @@ -80,23 +90,31 @@ type branchConfig struct {
PreservedPaths []string `json:"preservedPaths,omitempty"`
}

func (b branchConfig) expand(values []string) branchConfig {
func (b branchConfig) expand(values []string) (branchConfig, error) {
cfg := b
cfg.AppConfigs = map[string]appConfig{}
for appName, appConfig := range b.AppConfigs {
cfg.AppConfigs[appName] = appConfig.expand(values)
var err error
if cfg.AppConfigs[appName], err = appConfig.expand(values); err != nil {
return cfg, errors.Wrapf(
err,
"error expanding app config for app %q",
appName,
)
}
}

for i, path := range b.PreservedPaths {
b.PreservedPaths[i] = file.ExpandPath(path, values)
}
return cfg
return cfg, nil
}

// appConfig encapsulates application-specific Kargo Render configuration.
type appConfig struct {
// ConfigManagement encapsulates configuration management options to be
// used with this branch and app.
ConfigManagement configManagementConfig `json:"configManagement,omitempty"`
ConfigManagement argocd.ConfigManagementConfig `json:"configManagement"`
// OutputPath specifies a path relative to the root of the repository where
// rendered manifests for this app will be stored in this branch.
OutputPath string `json:"outputPath,omitempty"`
Expand All @@ -105,40 +123,14 @@ type appConfig struct {
CombineManifests bool `json:"combineManifests,omitempty"`
}

func (a appConfig) expand(values []string) appConfig {
func (a appConfig) expand(values []string) (appConfig, error) {
cfg := a
cfg.ConfigManagement = a.ConfigManagement.expand(values)
cfg.OutputPath = file.ExpandPath(a.OutputPath, values)
return cfg
}

// configManagementConfig is a wrapper around more specific configuration for
// one of three supported configuration management tools: helm, kustomize, or
// ytt. Only one of its fields may be non-nil.
type configManagementConfig struct { // nolint: revive
// Helm encapsulates optional Helm configuration options.
Helm *helm.Config `json:"helm,omitempty"`
// Kustomize encapsulates optional Kustomize configuration options.
Kustomize *kustomize.Config `json:"kustomize,omitempty"`
// Ytt encapsulates optional ytt configuration options.
Ytt *ytt.Config `json:"ytt,omitempty"`
}

func (c configManagementConfig) expand(values []string) configManagementConfig {
cfg := c
if c.Helm != nil {
helmCfg := c.Helm.Expand(values)
cfg.Helm = &helmCfg
}
if c.Kustomize != nil {
kustomizeCfg := c.Kustomize.Expand(values)
cfg.Kustomize = &kustomizeCfg
}
if c.Ytt != nil {
yttCfg := c.Ytt.Expand(values)
cfg.Ytt = &yttCfg
var err error
if cfg.ConfigManagement, err = a.ConfigManagement.Expand(values); err != nil {
return cfg, errors.Wrap(err, "error expanding config management config")
}
return cfg
cfg.OutputPath = file.ExpandPath(a.OutputPath, values)
return cfg, nil
}

// pullRequestConfig encapsulates details related to PR management for a branch.
Expand Down Expand Up @@ -210,10 +202,8 @@ func normalizeAndValidate(configBytes []byte) ([]byte, error) {
return nil,
errors.Wrap(err, "error normalizing Kargo Render configuration")
}
validationResult, err := gojsonschema.Validate(
configSchemaJSONLoader,
gojsonschema.NewBytesLoader(configBytes),
)

validationResult, err := configSchema.Validate(gojsonschema.NewBytesLoader(configBytes))
if err != nil {
return nil, errors.Wrap(err, "error validating Kargo Render configuration")
}
Expand Down
Loading

0 comments on commit bb12522

Please sign in to comment.