diff --git a/charts/terranetes-controller/Chart.yaml b/charts/terranetes-controller/Chart.yaml index 9fbc3afc1..c1b9b7d70 100644 --- a/charts/terranetes-controller/Chart.yaml +++ b/charts/terranetes-controller/Chart.yaml @@ -3,8 +3,8 @@ apiVersion: v2 name: terranetes-controller description: Controller used to provision a terraform workflow within kubernetes type: application -version: v0.8.1 -appVersion: v0.5.1 +version: v0.8.2 +appVersion: v0.5.3 sources: - https://github.com/appvia/terranetes-controller - https://github.com/appvia/terranetes diff --git a/charts/terranetes-controller/values.yaml b/charts/terranetes-controller/values.yaml index bbbb28b36..aff844557 100644 --- a/charts/terranetes-controller/values.yaml +++ b/charts/terranetes-controller/values.yaml @@ -43,11 +43,11 @@ controller: # policy is image for policy policy: bridgecrew/checkov:3.2.298 # preload is the image to use for preload data jobs - preload: ghcr.io/appvia/terranetes-executor:v0.5.0 + preload: ghcr.io/appvia/terranetes-executor:v0.5.3 # is the controller image - controller: ghcr.io/appvia/terranetes-controller:v0.5.0 + controller: ghcr.io/appvia/terranetes-controller:v0.5.3 # The terranetes image used when running jobs - executor: ghcr.io/appvia/terranetes-executor:v0.5.0 + executor: ghcr.io/appvia/terranetes-executor:v0.5.3 # Rate limting on configurations to prevent the controller from being overwhelmed. This # is the percentage of configurations which are permitted to run a plan at any one time. # Note, zero means no rate limiting is applied. diff --git a/pkg/apis/terraform/v1alpha1/configuration_types.go b/pkg/apis/terraform/v1alpha1/configuration_types.go index 6bdadd037..029a7bc11 100644 --- a/pkg/apis/terraform/v1alpha1/configuration_types.go +++ b/pkg/apis/terraform/v1alpha1/configuration_types.go @@ -493,6 +493,20 @@ type ConfigurationStatus struct { TerraformVersion string `json:"terraformVersion,omitempty"` } +// IsRevisioned returns true if the configuration is revisioned +func (c *Configuration) IsRevisioned() bool { + switch { + case c.Spec.Plan == nil: + return false + case c.Spec.Plan.Name == "": + return false + case c.Spec.Plan.Revision == "": + return false + } + + return false +} + // GetNamespacedName returns the namespaced resource type func (c *Configuration) GetNamespacedName() types.NamespacedName { return types.NamespacedName{ diff --git a/pkg/cmd/tnctl/create/assets/tnctl.revision.yaml.tpl b/pkg/cmd/tnctl/create/assets/tnctl.revision.yaml.tpl index af81a0f44..dfa63c630 100644 --- a/pkg/cmd/tnctl/create/assets/tnctl.revision.yaml.tpl +++ b/pkg/cmd/tnctl/create/assets/tnctl.revision.yaml.tpl @@ -36,7 +36,7 @@ spec: {{- if .Inputs }} ## Inputs dictate the variables which the consumer is permitted, or - ## required to provides. It is best to keep this to a minimum; so a developer + ## required to provide. It is best to keep this to a minimum; so a developer ## needn't be concerned with the inner workings of the module, just the ## contextual requirements, i.e database name, size etc. inputs: diff --git a/pkg/cmd/tnctl/create/revision.go b/pkg/cmd/tnctl/create/revision.go index be4b49ecc..95a5d869f 100644 --- a/pkg/cmd/tnctl/create/revision.go +++ b/pkg/cmd/tnctl/create/revision.go @@ -73,7 +73,7 @@ type RevisionCommand struct { File string // Provider is the name of the provider to use Provider string - // DeleteDownload indicates we should retain the download + // DeleteDownload indicates we should retain the download DeleteDownload bool } @@ -245,7 +245,7 @@ func (o *RevisionCommand) retrieveOutputs(module *tfconfig.Module) error { // @step: ask the user which outputs should exposed var selected []string if err := survey.AskOne(&survey.MultiSelect{ - Message: "What outputs should be extract into the secret?", + Message: "What outputs should be extracted into the secret?", Options: suggestions, PageSize: 20, }, &selected, survey.WithKeepFilter(false)); err != nil { @@ -272,7 +272,7 @@ func (o *RevisionCommand) retrieveRevision() error { if !found { if err := survey.AskOne(&survey.Input{ Message: fmt.Sprintf("What is the version of this %s (in semver format)?", color.YellowString("revision")), - Help: "Revisions must have a version, cloud resource reference both the plan and the version", + Help: "Revisions must have a version, cloud resources reference both the plan name and the version", Default: "v0.0.1", }, &o.Revision); err != nil { return err @@ -299,7 +299,7 @@ func (o *RevisionCommand) retrieveInputs(module *tfconfig.Module) error { return nil } - // @step: calculate the max variable size - just of spacing + // @step: calculate the max variable size - just for spacing for _, x := range module.Variables { if len(x.Name) > length { length = len(x.Name) @@ -430,7 +430,7 @@ func (o *RevisionCommand) retrievePlan() error { // @step: we an produce a list from the current plans if err := survey.AskOne(&survey.Select{ - Message: fmt.Sprintf("The cluster already contains plans, will the %s will be part of?", + Message: fmt.Sprintf("The cluster already contains plans, which will the %s will be part of?", color.YellowString("revision"), ), Options: append(list, "None of these..."), diff --git a/pkg/controller/configuration/ensure.go b/pkg/controller/configuration/ensure.go index 88fdb0a7f..7caf0ee55 100644 --- a/pkg/controller/configuration/ensure.go +++ b/pkg/controller/configuration/ensure.go @@ -23,6 +23,7 @@ import ( "encoding/json" "errors" "fmt" + "reflect" "strconv" "strings" "time" @@ -78,6 +79,25 @@ func (c *Controller) ensureCapturedState(configuration *terraformv1alpha1.Config return reconcile.Result{}, err } + // @step: if we are based on a Revision, lets try and grab the definition + if configuration.IsRevisioned() { + revision := &terraformv1alpha1.Revision{} + revision.Name = configuration.Spec.Plan.Revision + + found, err := kubernetes.GetIfExists(ctx, c.cc, revision) + if err != nil { + cond.Failed(err, "Failed to retrieve the plan for the configuration") + + return reconcile.Result{}, err + } + if !found { + cond.ActionRequired("Revision %q does not exist", configuration.Spec.Plan.Revision) + + return reconcile.Result{RequeueAfter: 5 * time.Minute}, nil + } + state.revision = revision + } + // @step: retrieve a list of all the confugrations in the cluster - shouldn't have much impact // as it's a cached client and we defer to the cache configurations := &terraformv1alpha1.ConfigurationList{} @@ -454,9 +474,38 @@ func (c *Controller) ensureAuthenticationSecret(configuration *terraformv1alpha1 } secret := &v1.Secret{} - secret.Namespace = configuration.Namespace secret.Name = configuration.Spec.Auth.Name + if configuration.IsRevisioned() { + revision := state.revision + + // @step: use the auth from the revision (sourcing secret from different namespace) if it's the same as in the configuration + if revision.Spec.Configuration.Auth != nil { + if reflect.DeepEqual(revision.Spec.Configuration.Auth, configuration.Spec.Auth) { + secret.Namespace = revision.Spec.Configuration.Auth.Namespace + log.WithFields(log.Fields{ + "auth_name": secret.Name, + "auth_namespace": secret.Namespace, + "name": configuration.Name, + "revision": revision.Name, + }).Info("auth secrets match, retrieving from the specified auth namespace, as defined in the revision") + } else { + secret.Namespace = configuration.Namespace + log.WithFields(log.Fields{ + "name": configuration.Name, + "namespace": configuration.Namespace, + "revision": revision.Name, + }).Info("configuration and revision auth secrets do not match, retrieving from the configuration's namespace") + } + } + } else { + secret.Namespace = configuration.Namespace + log.WithFields(log.Fields{ + "name": configuration.Name, + "namespace": configuration.Namespace, + }).Info("no plan referenced, retrieving auth secret from the configuration's namespace") + } + found, err := kubernetes.GetIfExists(ctx, c.cc, secret) if err != nil { cond.Failed(err, "Failed to retrieve the authentication secret: (%s/%s)", secret.Namespace, secret.Name) diff --git a/pkg/controller/configuration/reconcile.go b/pkg/controller/configuration/reconcile.go index 2d7313c63..7cc703a51 100644 --- a/pkg/controller/configuration/reconcile.go +++ b/pkg/controller/configuration/reconcile.go @@ -38,6 +38,8 @@ type state struct { configurations *terraformv1alpha1.ConfigurationList // checkovConstraint is the policy constraint for this configuration checkovConstraint *terraformv1alpha1.PolicyConstraint + // revision is the Revision we are based from + revision *terraformv1alpha1.Revision // hasDrift is a flag to indicate if the configuration has drift hasDrift bool // backendTemplate is the template to use for the terraform state backend. diff --git a/pkg/utils/download.go b/pkg/utils/download.go index dd88567dc..411249d9c 100644 --- a/pkg/utils/download.go +++ b/pkg/utils/download.go @@ -58,8 +58,8 @@ func Download(ctx context.Context, source, destination string) error { new(getter.FileDetector), }), }, - Pwd: pwd, - Src: source, + Pwd: pwd, + Src: source, } doneCh := make(chan struct{})