From 2161702ed4276f933ed926dc9479dc83eeb36409 Mon Sep 17 00:00:00 2001 From: Luke Hoban Date: Thu, 19 Nov 2020 21:30:22 -0800 Subject: [PATCH] Add `backend` configuration Adds a top level `backend` field to the Stack CR that allows pointing all operations for the target Stack at the given backend. In cases where `backend` is set to a non-service backend, the AccessTokenSecret field is also no longer required. This is dependent on https://github.com/pulumi/pulumi/pull/5789, and cannot be merged until the operator can take a dependency on that change in the core `pulumi` CLI. Fixes #83. --- deploy/crds/pulumi.com_stacks.yaml | 6 ++-- pkg/apis/pulumi/v1alpha1/stack_types.go | 7 +++-- pkg/controller/stack/stack_controller.go | 38 ++++++++++++++---------- test/stack_controller_test.go | 10 ++++++- 4 files changed, 41 insertions(+), 20 deletions(-) diff --git a/deploy/crds/pulumi.com_stacks.yaml b/deploy/crds/pulumi.com_stacks.yaml index aeafef6f..a0038065 100644 --- a/deploy/crds/pulumi.com_stacks.yaml +++ b/deploy/crds/pulumi.com_stacks.yaml @@ -33,7 +33,10 @@ spec: description: StackSpec defines the desired state of Pulumi Stack being managed by this operator. properties: accessTokenSecret: - description: AccessTokenSecret is the name of a secret containing the PULUMI_ACCESS_TOKEN for Pulumi access. + description: (optional) AccessTokenSecret is the name of a secret containing the PULUMI_ACCESS_TOKEN for Pulumi access. + type: string + backend: + description: (optional) Backend is an optional backend URL to use for all Pulumi operations. type: string branch: description: (optional) Branch is the branch name to deploy, either the simple or fully qualified ref name. This is mutually exclusive with the Commit setting. If both are empty, the `master` branch is deployed. @@ -89,7 +92,6 @@ spec: description: Stack is the fully qualified name of the stack to deploy (/). type: string required: - - accessTokenSecret - projectRepo - stack type: object diff --git a/pkg/apis/pulumi/v1alpha1/stack_types.go b/pkg/apis/pulumi/v1alpha1/stack_types.go index f371085f..ee0b0fcc 100644 --- a/pkg/apis/pulumi/v1alpha1/stack_types.go +++ b/pkg/apis/pulumi/v1alpha1/stack_types.go @@ -12,13 +12,16 @@ import ( type StackSpec struct { // Auth info: - // AccessTokenSecret is the name of a secret containing the PULUMI_ACCESS_TOKEN for Pulumi access. - AccessTokenSecret string `json:"accessTokenSecret"` + // (optional) AccessTokenSecret is the name of a secret containing the PULUMI_ACCESS_TOKEN for Pulumi access. + AccessTokenSecret string `json:"accessTokenSecret,omitempty"` // (optional) Envs is an optional array of config maps containing environment variables to set. Envs []string `json:"envs,omitempty"` // (optional) SecretEnvs is an optional array of secret names containing environment variables to set. SecretEnvs []string `json:"envSecrets,omitempty"` + // (optional) Backend is an optional backend URL to use for all Pulumi operations. + Backend string `json:"backend,omitempty"` + // Stack identity: // Stack is the fully qualified name of the stack to deploy (/). diff --git a/pkg/controller/stack/stack_controller.go b/pkg/controller/stack/stack_controller.go index 5e9fe823..9de8146f 100644 --- a/pkg/controller/stack/stack_controller.go +++ b/pkg/controller/stack/stack_controller.go @@ -137,21 +137,24 @@ func (r *ReconcileStack) Reconcile(request reconcile.Request) (reconcile.Result, // indicated by the deletion timestamp being set. isStackMarkedToBeDeleted := instance.GetDeletionTimestamp() != nil - // Fetch the API token from the named secret. - secret := &corev1.Secret{} - if err = r.client.Get(context.TODO(), - types.NamespacedName{Name: stack.AccessTokenSecret, Namespace: request.Namespace}, secret); err != nil { - reqLogger.Error(err, "Could not find secret for Pulumi API access", - "Namespace", request.Namespace, "Stack.AccessTokenSecret", stack.AccessTokenSecret) - return reconcile.Result{}, err - } + var accessToken string + if stack.AccessTokenSecret != "" { + // Fetch the API token from the named secret. + secret := &corev1.Secret{} + if err = r.client.Get(context.TODO(), + types.NamespacedName{Name: stack.AccessTokenSecret, Namespace: request.Namespace}, secret); err != nil { + reqLogger.Error(err, "Could not find secret for Pulumi API access", + "Namespace", request.Namespace, "Stack.AccessTokenSecret", stack.AccessTokenSecret) + return reconcile.Result{}, err + } - accessToken := string(secret.Data["accessToken"]) - if accessToken == "" { - err = errors.New("Secret accessToken data is empty") - reqLogger.Error(err, "Illegal empty secret accessToken data for Pulumi API access", - "Namespace", request.Namespace, "Stack.AccessTokenSecret", stack.AccessTokenSecret) - return reconcile.Result{}, err + accessToken = string(secret.Data["accessToken"]) + if accessToken == "" { + err = errors.New("Secret accessToken data is empty") + reqLogger.Error(err, "Illegal empty secret accessToken data for Pulumi API access", + "Namespace", request.Namespace, "Stack.AccessTokenSecret", stack.AccessTokenSecret) + return reconcile.Result{}, err + } } // Create a new reconciliation session. @@ -522,7 +525,12 @@ func (sess *reconcileStackSession) SetupPulumiWorkdir(gitAuth *auto.GitAuth) err if err != nil { return errors.Wrap(err, "failed to create local workspace") } - w.SetEnvVar("PULUMI_ACCESS_TOKEN", sess.accessToken) + if sess.stack.Backend != "" { + w.SetEnvVar("PULUMI_BACKEND_URL", sess.stack.Backend) + } + if sess.accessToken != "" { + w.SetEnvVar("PULUMI_ACCESS_TOKEN", sess.accessToken) + } // Create a new stack if the stack does not already exist, or fall back to // selecting the existing stack. If the stack does not exist, it will be created and selected. diff --git a/test/stack_controller_test.go b/test/stack_controller_test.go index 9dffe123..d47738f8 100644 --- a/test/stack_controller_test.go +++ b/test/stack_controller_test.go @@ -4,6 +4,7 @@ import ( "context" "encoding/base32" "fmt" + "io/ioutil" "math/rand" "os" "os/exec" @@ -120,9 +121,16 @@ var _ = Describe("Stack Controller", func() { stackName := fmt.Sprintf("%s/s3-op-project/dev-%s", stackOrg, randString()) fmt.Fprintf(GinkgoWriter, "Stack.Name: %s\n", stackName) + // Use a local backend for this test + backendDir, err := ioutil.TempDir("", "") + if err != nil { + Fail("Could not create backend directory") + } + defer os.RemoveAll(backendDir) + // Define the stack spec spec := pulumiv1alpha1.StackSpec{ - AccessTokenSecret: pulumiAPISecret.ObjectMeta.Name, + Backend: fmt.Sprintf("file://%s", backendDir), SecretEnvs: []string{ pulumiAWSSecret.ObjectMeta.Name, },