Skip to content
This repository was archived by the owner on Nov 20, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions api/v1alpha1/workspace_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,12 @@ type WorkspaceSpec struct {
// SSH Key ID. This key must already exist in the TF Cloud organization. This can either be the user assigned name of the SSH Key, or the system assigned ID.
// +optional
SSHKeyID string `json:"sshKeyID,omitempty"`
// Overrides default output name. Default output name is `<workspaceName>-outputs`.
// +optional
OutputName string `json:"outputName,omitempty"`
// Overrides default output namespace. Default namespace is the same namespace as the workspace.
// +optional
OutputNamespace string `json:"outputNamespace,omitempty"`
// Outputs denote outputs wanted
// +optional
Outputs []*OutputSpec `json:"outputs,omitempty"`
Expand Down Expand Up @@ -157,6 +163,14 @@ type WorkspaceStatus struct {
RunID string `json:"runID"`
// Configuration Version ID
ConfigVersionID string `json:"configVersionID"`
// Current output name
// +optional
// +nullable
OutputName string `json:"outputName"`
// Current output namespace
// +optional
// +nullable
OutputNamespace string `json:"outputNamespace"`
// Outputs from state file
// +optional
Outputs []*OutputStatus `json:"outputs,omitempty"`
Expand Down
6 changes: 6 additions & 0 deletions config/crd/bases/app.terraform.io_workspaces.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,12 @@ spec:
organization:
description: Terraform Cloud organization
type: string
outputName:
description: Overrides default output name. Default output name is <workspaceName>-outputs.
type: string
outputNamespace:
description: Overrides default output namespace. Default namespace is the same namespace as the workspace.
type: string
outputs:
description: Outputs denote outputs wanted
items:
Expand Down
53 changes: 46 additions & 7 deletions workspacehelper/k8s_configmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package workspacehelper
import (
"context"
"errors"
"fmt"
"reflect"

"github.com/hashicorp/terraform-k8s/api/v1alpha1"
Expand Down Expand Up @@ -116,13 +115,53 @@ func outputsToMap(outputs []*v1alpha1.OutputStatus) map[string][]byte {
return data
}

// CleanPreviousSecretOutputsIfChanged cleans previous secret if output has changed
func (r *WorkspaceHelper) CleanPreviousSecretOutputsIfChanged(outputName string, outputNamespace string, status *v1alpha1.WorkspaceStatus) error {
currentName := status.OutputName
currentNamespace := status.OutputNamespace

// no previous name was set, do nothing
if currentName == "" || currentNamespace == "" {
return nil
}

// no changes to name/namespace
if currentName == outputName && currentNamespace == outputNamespace {
return nil
}

// attempt to cleanup whatever is configured, if it exists
r.reqLogger.Info("Terraform Output Secret name has changed. Removing previous version.")
found := &corev1.Secret{}

err := r.client.Get(context.TODO(), types.NamespacedName{Name: currentName, Namespace: currentNamespace}, found)
if err != nil && k8serrors.IsNotFound(err) {
// resource already removed, do nothing
return nil
} else if err != nil {
return err
}

if err := r.client.Delete(context.TODO(), found); err != nil {
// unable to remove object
return err
}

return nil
}

// UpsertSecretOutputs creates a Secret for the outputs
func (r *WorkspaceHelper) UpsertSecretOutputs(w *v1alpha1.Workspace, outputs []*v1alpha1.OutputStatus) error {
func (r *WorkspaceHelper) UpsertSecretOutputs(outputName string, outputNamespace string, w *v1alpha1.Workspace) error {
found := &corev1.Secret{}
outputName := fmt.Sprintf("%s-outputs", w.Name)
err := r.client.Get(context.TODO(), types.NamespacedName{Name: outputName, Namespace: w.Namespace}, found)

if err := r.CleanPreviousSecretOutputsIfChanged(outputName, outputNamespace, &w.Status); err != nil {
r.reqLogger.Error(err, "Failed to clean previous output Secret")
return err
}

err := r.client.Get(context.TODO(), types.NamespacedName{Name: outputName, Namespace: outputNamespace}, found)
if err != nil && k8serrors.IsNotFound(err) {
secret := secretForOutputs(outputName, w.Namespace, outputs)
secret := secretForOutputs(outputName, outputNamespace, w.Status.Outputs)
err = controllerutil.SetControllerReference(w, secret, r.scheme)
if err != nil {
return err
Expand All @@ -138,12 +177,12 @@ func (r *WorkspaceHelper) UpsertSecretOutputs(w *v1alpha1.Workspace, outputs []*
return err
}

currentOutputs := outputsToMap(outputs)
currentOutputs := outputsToMap(w.Status.Outputs)
if !reflect.DeepEqual(found.Data, currentOutputs) {
r.reqLogger.Info("Updating secrets", "name", outputName)
found.Data = currentOutputs
if err := r.client.Update(context.TODO(), found); err != nil {
r.reqLogger.Error(err, "Failed to update output secrets", "Namespace", w.Namespace, "Name", outputName)
r.reqLogger.Error(err, "Failed to update output secrets", "Namespace", outputNamespace, "Name", outputName)
return err
}
return nil
Expand Down
25 changes: 20 additions & 5 deletions workspacehelper/workspace_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,26 @@ func (r *WorkspaceHelper) processFinishedRun(instance *appv1alpha1.Workspace) er
return err
}

if !reflect.DeepEqual(outputs, instance.Status.Outputs) {
outputName := fmt.Sprintf("%s-outputs", instance.Name)
if instance.Spec.OutputName != "" {
outputName = instance.Spec.OutputName
}

outputNamespace := instance.Namespace
if instance.Spec.OutputNamespace != "" {
outputNamespace = instance.Spec.OutputNamespace
}

if err = r.UpsertSecretOutputs(outputName, outputNamespace, instance); err != nil {
r.reqLogger.Error(err, "Error with creating Secret for Terraform Outputs")
return err
}

if !reflect.DeepEqual(outputs, instance.Status.Outputs) || instance.Status.OutputName != outputName || instance.Status.OutputNamespace != outputNamespace {
instance.Status.Outputs = outputs
instance.Status.OutputName = outputName
instance.Status.OutputNamespace = outputNamespace

err := r.client.Status().Update(context.TODO(), instance)
if err != nil {
r.reqLogger.Error(err, "Failed to update output status")
Expand All @@ -256,10 +274,7 @@ func (r *WorkspaceHelper) processFinishedRun(instance *appv1alpha1.Workspace) er
r.recorder.Event(instance, corev1.EventTypeNormal, "WorkspaceEvent",
fmt.Sprintf("Updated outputs for run %s", instance.Status.RunID))
}
if err = r.UpsertSecretOutputs(instance, instance.Status.Outputs); err != nil {
r.reqLogger.Error(err, "Error with creating ConfigMap for Terraform Outputs")
return err
}

return nil
}

Expand Down