Skip to content

Commit

Permalink
🐛 Fix Workspace (re-)creation issue (#234)
Browse files Browse the repository at this point in the history
  • Loading branch information
arybolovlev authored Aug 23, 2023
1 parent 5bbc16a commit 551a55c
Show file tree
Hide file tree
Showing 9 changed files with 56 additions and 9 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ BUG FIXES:
* `AgentPool`: fix an issue when `plan_queued` and `apply_queued` statuses do not trigger agent scaling. [[GH-215](https://github.com/hashicorp/terraform-cloud-operator/pull/215)]
* `Helm Chart`: fix an issue with the Deployment template in the Helm chart where `name` in path `spec.template.spec.containers[0]` was duplicated. [[GH-216](https://github.com/hashicorp/terraform-cloud-operator/pull/216)]
* `Workspace`: fix an issue when the Operator panics when `spec.executionMode` is configured as `agent` but `spec.agentPool` is not set which is mandatory in this case. [[GH-242](https://github.com/hashicorp/terraform-cloud-operator/pull/242)]
* `Workspace`: fix an issue when a new Workspace is successfully created, but its `status.WorkspaceID` status fails to update with a new Workspace ID due to an error during subsequent reconciliation. Consequently, the Workspace controller continuously encounters failures while attempting to reconcile the newly created Workspace. [[GH-234](https://github.com/hashicorp/terraform-cloud-operator/pull/234)]

ENHANCEMENT:

* `Operator`: Add the ability to skip TLS certificate validation for communication between the Operator and the TFC/E endpoint. A new environment variable `TFC_TLS_SKIP_VERIFY` should be set to `true` to skip the validation. Default: `false`. [[GH-222](https://github.com/hashicorp/terraform-cloud-operator/pull/222)]
* `Helm Chart`: Add a new parameter `operator.skipTLSVerify` to configure the ability to skip TLS certificate validation for communication between the Operator and the TFC/E endpoint. Default: `false`. [[GH-222](https://github.com/hashicorp/terraform-cloud-operator/pull/222)]
* `Workspace`: Add `spec.Tags` validation to align with the TFC requirement. [[GH-234](https://github.com/hashicorp/terraform-cloud-operator/pull/234)]

DEPENDENCIES:

Expand Down
9 changes: 8 additions & 1 deletion api/v1alpha2/workspace_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,12 @@ type SSHKey struct {
Name string `json:"name,omitempty"`
}

// Tags allows you to correlate, organize, and even filter workspaces based on the assigned tags.
// Tags must be one or more characters; can include letters, numbers, colons, hyphens, and underscores; and must begin and end with a letter or number.
//
// +kubebuilder:validation:Pattern:="^[A-Za-z0-9][A-Za-z0-9:_-]*$"
type Tag string

// NotificationTrigger represents the different TFC notifications that can be sent as a run's progress transitions between different states.
// This must be aligned with go-tfe type `NotificationTriggerType`.
//
Expand Down Expand Up @@ -385,10 +391,11 @@ type WorkspaceSpec struct {
//+optional
RunTasks []WorkspaceRunTask `json:"runTasks,omitempty"`
// Workspace tags are used to help identify and group together workspaces.
// Tags must be one or more characters; can include letters, numbers, colons, hyphens, and underscores; and must begin and end with a letter or number.
//
//+kubebuilder:validation:MinItems:=1
//+optional
Tags []string `json:"tags,omitempty"`
Tags []Tag `json:"tags,omitempty"`
// Terraform Cloud workspaces can only be accessed by users with the correct permissions.
// You can manage permissions for a workspace on a per-team basis.
// When a workspace is created, only the owners team and teams with the "manage workspaces" permission can access it,
Expand Down
2 changes: 1 addition & 1 deletion api/v1alpha2/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -351,8 +351,15 @@ spec:
type: object
tags:
description: Workspace tags are used to help identify and group together
workspaces.
workspaces. Tags must be one or more characters; can include letters,
numbers, colons, hyphens, and underscores; and must begin and end
with a letter or number.
items:
description: Tags allows you to correlate, organize, and even filter
workspaces based on the assigned tags. Tags must be one or more
characters; can include letters, numbers, colons, hyphens, and
underscores; and must begin and end with a letter or number.
pattern: ^[A-Za-z0-9][A-Za-z0-9:_-]*$
type: string
minItems: 1
type: array
Expand Down
9 changes: 8 additions & 1 deletion config/crd/bases/app.terraform.io_workspaces.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -348,8 +348,15 @@ spec:
type: object
tags:
description: Workspace tags are used to help identify and group together
workspaces.
workspaces. Tags must be one or more characters; can include letters,
numbers, colons, hyphens, and underscores; and must begin and end
with a letter or number.
items:
description: Tags allows you to correlate, organize, and even filter
workspaces based on the assigned tags. Tags must be one or more
characters; can include letters, numbers, colons, hyphens, and
underscores; and must begin and end with a letter or number.
pattern: ^[A-Za-z0-9][A-Za-z0-9:_-]*$
type: string
minItems: 1
type: array
Expand Down
26 changes: 25 additions & 1 deletion controllers/workspace_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,30 @@ func (r *WorkspaceReconciler) reconcileWorkspace(ctx context.Context, w *workspa
var workspace *tfc.Workspace
var err error

defer func() {
// Update the status with the Workspace ID. This is useful if the reconciliation failed.
// An example here would be the case when the workspace has been created successfully,
// but further reconciliation steps failed.
//
// If a Workspace creation operation failed, we don't have a workspace object
// and thus don't update the status. An example here would be the case when the workspace name has already been taken.
//
// Cannot call updateStatus method since it updated multiple fields and can break reconciliation logic.
//
// TODO:
// - Use conditions(https://maelvls.dev/kubernetes-conditions/)
// - Let Objects update their own status conditions
// - Simplify updateStatus method in a way it could be called anytime
if workspace != nil && workspace.ID != "" {
w.instance.Status.WorkspaceID = workspace.ID
err = r.Status().Update(ctx, &w.instance)
if err != nil {
w.log.Error(err, "Workspace Controller", "msg", "update status with workspace ID")
r.Recorder.Event(&w.instance, corev1.EventTypeWarning, "ReconcileWorkspace", "Failed to update status with workspace ID")
}
}
}()

// verify whether the Kubernetes object has been marked as deleted and if so delete the workspace
if w.instance.IsDeletionCandidate(workspaceFinalizer) {
w.log.Info("Reconcile Workspace", "msg", "object marked as deleted, need to delete workspace first")
Expand All @@ -451,7 +475,7 @@ func (r *WorkspaceReconciler) reconcileWorkspace(ctx context.Context, w *workspa
if w.instance.IsCreationCandidate() {
w.log.Info("Reconcile Workspace", "msg", "status.WorkspaceID is empty, creating a new workspace")
r.Recorder.Event(&w.instance, corev1.EventTypeNormal, "ReconcileWorkspace", "Status.WorkspaceID is empty, creating a new workspace")
_, err = r.createWorkspace(ctx, w)
workspace, err = r.createWorkspace(ctx, w)
if err != nil {
w.log.Error(err, "Reconcile Workspace", "msg", "failed to create a new workspace")
r.Recorder.Event(&w.instance, corev1.EventTypeWarning, "ReconcileWorkspace", "Failed to create a new workspace")
Expand Down
2 changes: 1 addition & 1 deletion controllers/workspace_controller_tags.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func getTags(instance *appv1alpha2.Workspace) map[string]bool {
}

for _, t := range instance.Spec.Tags {
tags[t] = true
tags[string(t)] = true
}

return tags
Expand Down
4 changes: 2 additions & 2 deletions controllers/workspace_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ var _ = Describe("Workspace controller", Ordered, func() {
{Name: "env:dev"},
}

instance.Spec.Tags = []string{"kubernetes-operator"}
instance.Spec.Tags = []appv1alpha2.Tag{"kubernetes-operator"}
// Create a new Kubernetes workspace object and wait until the controller finishes the reconciliation
createWorkspace(instance, namespacedName)
// Make sure that the TFC Workspace has all desired tags
Expand All @@ -185,7 +185,7 @@ var _ = Describe("Workspace controller", Ordered, func() {
}).Should(BeTrue())

// Update the Kubernetes workspace tags
instance.Spec.Tags = []string{"kubernetes-operator", "env:dev"}
instance.Spec.Tags = []appv1alpha2.Tag{"kubernetes-operator", "env:dev"}
Expect(k8sClient.Update(ctx, instance)).Should(Succeed())
// Wait until the controller updates Terraform Cloud workspace correcly
Eventually(func() bool {
Expand Down
2 changes: 1 addition & 1 deletion docs/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ _Appears in:_
| `agentPool` _[WorkspaceAgentPool](#workspaceagentpool)_ | Terraform Cloud Agents allow Terraform Cloud to communicate with isolated, private, or on-premises infrastructure. More information: - https://developer.hashicorp.com/terraform/cloud-docs/agents |
| `executionMode` _string_ | Define where the Terraform code will be executed. More information: - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/settings#execution-mode |
| `runTasks` _[WorkspaceRunTask](#workspaceruntask) array_ | Run tasks allow Terraform Cloud to interact with external systems at specific points in the Terraform Cloud run lifecycle. More information: - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/settings/run-tasks |
| `tags` _string array_ | Workspace tags are used to help identify and group together workspaces. |
| `tags` _Tag array_ | Workspace tags are used to help identify and group together workspaces. Tags must be one or more characters; can include letters, numbers, colons, hyphens, and underscores; and must begin and end with a letter or number. |
| `teamAccess` _[TeamAccess](#teamaccess) array_ | Terraform Cloud workspaces can only be accessed by users with the correct permissions. You can manage permissions for a workspace on a per-team basis. When a workspace is created, only the owners team and teams with the "manage workspaces" permission can access it, with full admin permissions. These teams' access can't be removed from a workspace. More information: - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/settings/access |
| `terraformVersion` _string_ | The version of Terraform to use for this workspace. If not specified, the latest available version will be used. More information: - https://www.terraform.io/cloud-docs/workspaces/settings#terraform-version |
| `workingDirectory` _string_ | The directory where Terraform will execute, specified as a relative path from the root of the configuration directory. More information: - https://www.terraform.io/cloud-docs/workspaces/settings#terraform-working-directory |
Expand Down

0 comments on commit 551a55c

Please sign in to comment.