From 7c561ea58b0cdf1d3784aa65f5f94e0f2e8cbfc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20Wid=C3=A9n?= Date: Tue, 15 Oct 2024 08:45:46 +0200 Subject: [PATCH] Fix so that TF resources can be reconciled and suspended (#125) --- pkg/flux/reconcile.go | 8 +++ pkg/flux/resume.go | 8 +++ pkg/flux/suspend.go | 9 +++ pkg/flux/terraform.go | 68 +++++++++++++++++++++ web/src/Footer.jsx | 109 ++++++++++++++++++++++------------ web/src/TerraformResource.jsx | 8 +-- 6 files changed, 169 insertions(+), 41 deletions(-) create mode 100644 pkg/flux/terraform.go diff --git a/pkg/flux/reconcile.go b/pkg/flux/reconcile.go index 7c4ea26..e52c856 100644 --- a/pkg/flux/reconcile.go +++ b/pkg/flux/reconcile.go @@ -22,6 +22,7 @@ import ( "time" "github.com/bombsimon/logrusr/v4" + tf "github.com/flux-iac/tofu-controller/api/v1alpha2" helmv2beta1 "github.com/fluxcd/helm-controller/api/v2beta1" kustomizationv1 "github.com/fluxcd/kustomize-controller/api/v1" "github.com/fluxcd/pkg/apis/meta" @@ -99,6 +100,12 @@ func NewReconcileCommand(resource string) *reconcileCommand { groupVersion: sourcev1beta2.GroupVersion, kind: sourcev1beta2.HelmChartKind, } + case tf.TerraformKind: + return &reconcileCommand{ + object: terraformAdapter{&tf.Terraform{}}, + groupVersion: tf.GroupVersion, + kind: tf.TerraformKind, + } } return nil @@ -110,6 +117,7 @@ func (r *reconcileCommand) Run(config *rest.Config, namespace, name string) { sourcev1beta2.AddToScheme(scheme) kustomizationv1.AddToScheme(scheme) helmv2beta1.AddToScheme(scheme) + tf.AddToScheme(scheme) log := logrusr.New(logrus.New()) logf.SetLogger(log) diff --git a/pkg/flux/resume.go b/pkg/flux/resume.go index 29a316e..5084de8 100644 --- a/pkg/flux/resume.go +++ b/pkg/flux/resume.go @@ -22,6 +22,7 @@ import ( "fmt" "time" + tf "github.com/flux-iac/tofu-controller/api/v1alpha2" helmv2beta1 "github.com/fluxcd/helm-controller/api/v2beta1" kustomizationv1 "github.com/fluxcd/kustomize-controller/api/v1" "github.com/fluxcd/pkg/apis/meta" @@ -106,6 +107,12 @@ func NewResumeCommand(resource string) *resumeCommand { groupVersion: sourcev1beta2.GroupVersion, list: bucketListAdapter{&sourcev1beta2.BucketList{}}, } + case tf.TerraformKind: + return &resumeCommand{ + kind: tf.TerraformKind, + groupVersion: tf.GroupVersion, + list: &terraformListAdapter{&tf.TerraformList{}}, + } } return nil @@ -117,6 +124,7 @@ func (r *resumeCommand) Run(config *rest.Config, namespace, name string) { sourcev1beta2.AddToScheme(scheme) kustomizationv1.AddToScheme(scheme) helmv2beta1.AddToScheme(scheme) + tf.AddToScheme(scheme) kubeClient, err := client.NewWithWatch(config, client.Options{ Scheme: scheme, diff --git a/pkg/flux/suspend.go b/pkg/flux/suspend.go index b0ea73f..e8ceb81 100644 --- a/pkg/flux/suspend.go +++ b/pkg/flux/suspend.go @@ -21,6 +21,7 @@ import ( "context" "errors" + tf "github.com/flux-iac/tofu-controller/api/v1alpha2" helmv2beta1 "github.com/fluxcd/helm-controller/api/v2beta1" kustomizationv1 "github.com/fluxcd/kustomize-controller/api/v1" sourcev1 "github.com/fluxcd/source-controller/api/v1" @@ -89,6 +90,13 @@ func NewSuspendCommand(resource string) *suspendCommand { object: bucketAdapter{&sourcev1beta2.Bucket{}}, list: bucketListAdapter{&sourcev1beta2.BucketList{}}, } + case tf.TerraformKind: + return &suspendCommand{ + kind: tf.TerraformKind, + groupVersion: tf.GroupVersion, + object: &terraformAdapter{&tf.Terraform{}}, + list: &terraformListAdapter{&tf.TerraformList{}}, + } } return nil @@ -100,6 +108,7 @@ func (s *suspendCommand) Run(config *rest.Config, namespace, name string) { sourcev1beta2.AddToScheme(scheme) kustomizationv1.AddToScheme(scheme) helmv2beta1.AddToScheme(scheme) + tf.AddToScheme(scheme) kubeClient, err := client.NewWithWatch(config, client.Options{ Scheme: scheme, diff --git a/pkg/flux/terraform.go b/pkg/flux/terraform.go new file mode 100644 index 0000000..29be0cf --- /dev/null +++ b/pkg/flux/terraform.go @@ -0,0 +1,68 @@ +package flux + +import ( + "fmt" + + tf "github.com/flux-iac/tofu-controller/api/v1alpha2" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +type terraformAdapter struct { + *tf.Terraform +} + +func (h terraformAdapter) asClientObject() client.Object { + return h.Terraform +} + +func (h terraformAdapter) deepCopyClientObject() client.Object { + return h.Terraform.DeepCopy() +} + +func (obj terraformAdapter) isSuspended() bool { + return obj.Terraform.Spec.Suspend +} + +func (obj terraformAdapter) setSuspended() { + obj.Terraform.Spec.Suspend = true +} + +func (obj terraformAdapter) setUnsuspended() { + obj.Terraform.Spec.Suspend = false +} + +func (obj terraformAdapter) getObservedGeneration() int64 { + return obj.Terraform.Status.ObservedGeneration +} + +func (obj terraformAdapter) isStatic() bool { + return false +} + +func (obj terraformAdapter) lastHandledReconcileRequest() string { + return obj.Status.LastAttemptedRevision +} + +func (obj terraformAdapter) successMessage() string { + return fmt.Sprintf("fetched revision %s", obj.Status.LastAppliedRevision) +} + +type terraformListAdapter struct { + *tf.TerraformList +} + +func (h terraformListAdapter) asClientList() client.ObjectList { + return h.TerraformList +} + +func (h terraformListAdapter) len() int { + return len(h.TerraformList.Items) +} + +func (a terraformListAdapter) item(i int) suspendable { + return &terraformAdapter{&a.TerraformList.Items[i]} +} + +func (a terraformListAdapter) resumeItem(i int) resumable { + return &terraformAdapter{&a.TerraformList.Items[i]} +} diff --git a/web/src/Footer.jsx b/web/src/Footer.jsx index 819013a..3f6afe2 100644 --- a/web/src/Footer.jsx +++ b/web/src/Footer.jsx @@ -1,57 +1,92 @@ -import { ArrowDownIcon, ArrowUpIcon } from '@heroicons/react/24/outline'; -import React, { memo, useMemo, useState } from 'react'; -import { Summary } from './Summary'; -import { ExpandedFooter } from "./ExpandedFooter" +import { ArrowDownIcon, ArrowUpIcon } from "@heroicons/react/24/outline"; +import React, { memo, useMemo, useState } from "react"; +import { Summary } from "./Summary"; +import { ExpandedFooter } from "./ExpandedFooter"; const Footer = memo(function Footer(props) { - const { store, capacitorClient, expanded, selected, targetReference, handleToggle, handleNavigationSelect } = props; + const { + store, + capacitorClient, + expanded, + selected, + targetReference, + handleToggle, + handleNavigationSelect, + } = props; const [fluxState, setFluxState] = useState(store.getState().fluxState); - store.subscribe(() => setFluxState(store.getState().fluxState)) + store.subscribe(() => setFluxState(store.getState().fluxState)); const sources = useMemo(() => { const sources = []; if (fluxState.ociRepositories) { - sources.push(...fluxState.ociRepositories) - sources.push(...fluxState.gitRepositories) - sources.push(...fluxState.buckets) - sources.push(...fluxState.helmRepositories) - sources.push(...fluxState.helmCharts) + sources.push(...fluxState.ociRepositories); + sources.push(...fluxState.gitRepositories); + sources.push(...fluxState.buckets); + sources.push(...fluxState.helmRepositories); + sources.push(...fluxState.helmCharts); } - return [...sources].sort((a, b) => a.metadata.name.localeCompare(b.metadata.name)); + return [...sources].sort((a, b) => + a.metadata.name.localeCompare(b.metadata.name), + ); }, [fluxState]); return ( -
-
+
+
- { !expanded && - <> -
- -
-
- -
-
- -
- - } + className="h-auto w-full cursor-pointer px-16 py-4 flex gap-x-12" + onClick={handleToggle} + > + {!expanded && ( + <> +
+ +
+
+ +
+
+ +
+
+ +
+ + )}
-
+
- {expanded && + {expanded && ( - } + )}
- ) -}) + ); +}); export default Footer; diff --git a/web/src/TerraformResource.jsx b/web/src/TerraformResource.jsx index fc22088..58c0894 100644 --- a/web/src/TerraformResource.jsx +++ b/web/src/TerraformResource.jsx @@ -2,7 +2,7 @@ import React, { useState, useRef, useEffect } from "react"; import { ReadyWidget } from "./ReadyWidget"; import { TerraformResourceWidget } from "./TerraformResourceWidget"; import { ErrorBoundary } from "react-error-boundary"; -import { fallbackRender } from "./FallbackRender" +import { fallbackRender } from "./FallbackRender"; export function TerraformResource(props) { const { capacitorClient, item, targetReference, handleNavigationSelect } = @@ -66,7 +66,7 @@ export function TerraformResource(props) { `Are you sure you want to resume ${item.metadata.name}?`, ) && capacitorClient.resume( - "helmrelease", + "Terraform", item.metadata.namespace, item.metadata.name, ); @@ -76,7 +76,7 @@ export function TerraformResource(props) { `Are you sure you want to suspend ${item.metadata.name}?`, ) && capacitorClient.suspend( - "helmrelease", + "Terraform", item.metadata.namespace, item.metadata.name, ); @@ -89,7 +89,7 @@ export function TerraformResource(props) { className="bg-transparent hover:bg-neutral-100 font-medium text-sm text-neutral-700 py-1 px-2 border border-neutral-300 rounded" onClick={() => capacitorClient.reconcile( - "helmrelease", + "Terraform", item.metadata.namespace, item.metadata.name, )