Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/plan pvc support #480

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
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
9 changes: 9 additions & 0 deletions .github/workflows/e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ jobs:
uses: engineerd/setup-kind@v0.5.0
with:
version: v0.11.1
config: ./config/testdata/kind/kind-config.yaml
image: kindest/node:v1.21.1@sha256:fae9a58f17f18f06aeac9772ca8b5ac680ebbed985e266f711d936e91d113bad
- name: Setup Kustomize
uses: fluxcd/pkg/actions/kustomize@main
Expand Down Expand Up @@ -122,6 +123,14 @@ jobs:
kubectl -n tf-system apply -f ./config/testdata/source
kubectl -n tf-system wait gitrepository/helloworld --for=condition=ready --timeout=4m
kubectl -n tf-system wait ocirepository/helloworld-oci --for=condition=ready --timeout=4m
- name: Run planConfig.storage.claimName tests
run: |
kubectl -n tf-system apply -f ./config/testdata/plan-config-claim
kubectl -n tf-system wait terraform/helloworld-plan-config-claim --for=condition=ready --timeout=4m

# delete after tests
kubectl -n tf-system delete -f ./config/testdata/plan-config-claim/test.yaml &&\
kubectl -n tf-system delete -f ./config/testdata/plan-config-claim/pvc.yaml
- name: Run approvePlan tests
run: |
kubectl -n tf-system apply -f ./config/testdata/approve-plan
Expand Down
37 changes: 32 additions & 5 deletions api/v1alpha1/terraform_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,12 @@ const (
CACertSecretName = "tf-controller.tls"
// RunnerTLSSecretName is the name of the secret containing a TLS cert that will be written to
// the namespace in which a terraform runner is created
RunnerTLSSecretName = "terraform-runner.tls"
RunnerLabel = "infra.contrib.fluxcd.io/terraform"
GitRepositoryIndexKey = ".metadata.gitRepository"
BucketIndexKey = ".metadata.bucket"
OCIRepositoryIndexKey = ".metadata.ociRepository"
RunnerTLSSecretName = "terraform-runner.tls"
RunnerLabel = "infra.contrib.fluxcd.io/terraform"
GitRepositoryIndexKey = ".metadata.gitRepository"
BucketIndexKey = ".metadata.bucket"
OCIRepositoryIndexKey = ".metadata.ociRepository"
PlanStorageMountSubDirBase = "terraform_controller"
)

type ReadInputsFromSecretSpec struct {
Expand Down Expand Up @@ -87,6 +88,9 @@ type TerraformSpec struct {
// +optional
Destroy bool `json:"destroy,omitempty"`

// +optional
PlanConfig *PlanConfigSpec `json:"planConfig,omitempty"`

// +optional
BackendConfig *BackendConfigSpec `json:"backendConfig,omitempty"`

Expand Down Expand Up @@ -352,6 +356,17 @@ type TerraformList struct {
Items []Terraform `json:"items"`
}

// PlanConfigSpec is for specifying configuration for Terraform plan related options
type PlanConfigSpec struct {
Storage *PlanStorage `json:"storage,omitempty"`
}

// PlanStorage is for specifying Terraform Plan storage configurations
type PlanStorage struct {
// ClaimName hold a name of a Kubernetes PVC that will hold the tf-runner plan file.
ClaimName string `json:"claimName,omitempty"`
}

// BackendConfigSpec is for specifying configuration for Terraform's Kubernetes backend
type BackendConfigSpec struct {

Expand Down Expand Up @@ -773,6 +788,18 @@ func (in Terraform) GetRetryInterval() time.Duration {
return in.Spec.Interval.Duration
}

func (in Terraform) GetClaimName() string {
if in.Spec.PlanConfig != nil && in.Spec.PlanConfig.Storage != nil {
return in.Spec.PlanConfig.Storage.ClaimName
}

return ""
}

func (in Terraform) GetPlanStorageMountSubDir() string {
return strings.Join([]string{PlanStorageMountSubDirBase, in.Namespace, in.Name}, "/")
}

// GetStatusConditions returns a pointer to the Status.Conditions slice.
func (in *Terraform) GetStatusConditions() *[]metav1.Condition {
return &in.Status.Conditions
Expand Down
40 changes: 40 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

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

14 changes: 14 additions & 0 deletions config/crd/bases/infra.contrib.fluxcd.io_terraforms.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,20 @@ spec:
description: Path to the directory containing Terraform (.tf) files.
Defaults to 'None', which translates to the root path of the SourceRef.
type: string
planConfig:
description: PlanConfigSpec is for specifying configuration for Terraform
plan related options
properties:
storage:
description: PlanStorage is for specifying Terraform Plan storage
configurations
properties:
claimName:
description: ClaimName hold a name of a Kubernetes PVC that
will hold the tf-runner plan file.
type: string
type: object
type: object
readInputsFromSecrets:
items:
properties:
Expand Down
29 changes: 27 additions & 2 deletions config/crd/bases/ocirepositories.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -301,12 +301,14 @@ spec:
type: object
type: array
contentConfigChecksum:
description: 'ContentConfigChecksum is a checksum of all the configurations
description: "ContentConfigChecksum is a checksum of all the configurations
related to the content of the source artifact: - .spec.ignore -
.spec.layerSelector observed in .status.observedGeneration version
of the object. This can be used to determine if the content configuration
has changed and the artifact needs to be rebuilt. It has the format
of `<algo>:<checksum>`, for example: `sha256:<checksum>`.'
of `<algo>:<checksum>`, for example: `sha256:<checksum>`. \n Deprecated:
Replaced with explicit fields for observed artifact content config
in the status."
type: string
lastHandledReconcileAt:
description: LastHandledReconcileAt holds the value of the most recent
Expand All @@ -317,6 +319,29 @@ spec:
description: ObservedGeneration is the last observed generation.
format: int64
type: integer
observedIgnore:
description: ObservedIgnore is the observed exclusion patterns used
for constructing the source artifact.
type: string
observedLayerSelector:
description: ObservedLayerSelector is the observed layer selector
used for constructing the source artifact.
properties:
mediaType:
description: MediaType specifies the OCI media type of the layer
which should be extracted from the OCI Artifact. The first layer
matching this type is selected.
type: string
operation:
description: Operation specifies how the selected layer should
be processed. By default, the layer compressed content is extracted
to storage. When the operation is set to 'copy', the layer compressed
content is persisted to storage as it is.
enum:
- extract
- copy
type: string
type: object
url:
description: URL is the download link for the artifact output of the
last OCI Repository sync.
Expand Down
7 changes: 7 additions & 0 deletions config/testdata/kind/kind-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
extraMounts: # For plan_config_claim
- hostPath: /tmp/plan-config-claim
containerPath: /tmp/plan-config-claim
12 changes: 12 additions & 0 deletions config/testdata/plan-config-claim/pvc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: plan-config-claim-pvc
spec:
storageClassName: standard
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
16 changes: 16 additions & 0 deletions config/testdata/plan-config-claim/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
apiVersion: infra.contrib.fluxcd.io/v1alpha1
kind: Terraform
metadata:
name: helloworld-plan-config-claim
spec:
interval: 10s
approvePlan: "auto"
storeReadablePlan: "human"
path: ./
planConfig:
storage:
claimName: "plan-config-claim-pvc"
sourceRef:
kind: GitRepository
name: helloworld
6 changes: 3 additions & 3 deletions controllers/tf_controller_finalizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ func (r *TerraformReconciler) finalize(ctx context.Context, terraform infrav1.Te
outputSecretName = terraform.Spec.WriteOutputsToSecret.Name
}

traceLog.Info("Finalize the secrets")
finalizeSecretsReply, err := runnerClient.FinalizeSecrets(ctx, &runner.FinalizeSecretsRequest{
traceLog.Info("Finalize storage: secrets, pvc")
finalizeStorageReply, err := runnerClient.FinalizeStorage(ctx, &runner.FinalizeStorageRequest{
Namespace: terraform.Namespace,
Name: terraform.Name,
Workspace: terraform.WorkspaceName(),
Expand All @@ -136,7 +136,7 @@ func (r *TerraformReconciler) finalize(ctx context.Context, terraform infrav1.Te

traceLog.Info("Check for an error")
if err == nil {
log.Info(fmt.Sprintf("finalizing secrets: %s", finalizeSecretsReply.Message))
log.Info(fmt.Sprintf("finalizing secrets: %s", finalizeStorageReply.Message))
}

// Record deleted status
Expand Down
2 changes: 1 addition & 1 deletion controllers/tf_controller_plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ func (r *TerraformReconciler) plan(ctx context.Context, terraform infrav1.Terraf
Revision: revision,
})
if err != nil {
err = fmt.Errorf("error saving plan secret: %s", err)
err = fmt.Errorf("error saving plan: %s", err)
return infrav1.TerraformNotReady(
terraform,
revision,
Expand Down
19 changes: 19 additions & 0 deletions controllers/tf_controller_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,25 @@ func (r *TerraformReconciler) runnerPodSpec(terraform v1alpha1.Terraform, tlsSec
podVolumeMounts = append(podVolumeMounts, terraform.Spec.RunnerPodTemplate.Spec.VolumeMounts...)
}

if terraform.GetClaimName() != "" {
podVolumes = append(podVolumes,
v1.Volume{
Name: terraform.Spec.PlanConfig.Storage.ClaimName,
VolumeSource: v1.VolumeSource{
PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
ClaimName: terraform.Spec.PlanConfig.Storage.ClaimName,
ReadOnly: false,
},
},
})
podVolumeMounts = append(podVolumeMounts, v1.VolumeMount{
Name: terraform.GetClaimName(),
ReadOnly: false,
MountPath: runner.PlanStoragePath,
SubPath: terraform.GetPlanStorageMountSubDir(),
})
}

return v1.PodSpec{
TerminationGracePeriodSeconds: gracefulTermPeriod,
InitContainers: terraform.Spec.RunnerPodTemplate.Spec.InitContainers,
Expand Down
Loading