pulumi / pulumi-kubernetes-operator

A Kubernetes Operator that automates the deployment of Pulumi Stacks
Apache License 2.0
225 stars 54 forks source link

Support for pulumi.com/reconciliation-request #672

Closed EronWright closed 19 hours ago

EronWright commented 1 month ago

One of the features of Stack is support for the Flux on-demand reconciliation protocol (ref), based on pulumi.com/reconciliation-request annotation.

The intention is to force a stack update when the stack is in a ready state, independent of the branch polling and periodic resyncs. As an annotation, the stack generation does not change. The workspace needn't be replaced, which notably happens whenever generation does change. The last update should incorporate the annotation value.

The "prerequisites" feature leverages this feature when the SucceededWithinDuration field on a given prerequisite is set. The field is proactive in nature; for example, given that stack A depends on stack B, a value of 5m on A will force a resync of stack B every 5 minutes even if B doesn't use periodic resync. A more "passive" (assumedly incorrect) interpretation of the feature, would be to check that B was recently synced while relying only on the natural causes of resync. See also: https://github.com/pulumi/pulumi-kubernetes-operator/pull/443

    // SucceededWithinDuration gives a duration within which the prerequisite must have reached a
    // succeeded state; e.g., "1h" means "the prerequisite must be successful, and have become so in
    // the last hour". Fields (should there ever be more than one) are not intended to be mutually
    // exclusive.
    SucceededWithinDuration *metav1.Duration `json:"succeededWithinDuration,omitempty"`
cleverguy25 commented 1 month ago

Added to epic https://github.com/pulumi/pulumi-kubernetes-operator/issues/586

EronWright commented 1 month ago

Confirmed with @rquitales two assumptions:

  1. pulumi.com/reconciliation-request is designed to force a re-sync (i.e. pulumi up), e.g. to update a stack output or to update a Kubernetes Secret.
  2. RequirementSpec.SucceededWithinDuration is designed to assertively re-sync the dependency.
EronWright commented 1 month ago

Another thought: when the prerequisite stack is failing, it would be counter-productive to keep touching its annotation. The prerequisite stack may have a backoff in effect (see https://github.com/pulumi/pulumi-kubernetes-operator/issues/677), and one wouldn't want to interfere.

EronWright commented 20 hours ago

Some example manifests for exercising the prerequisites feature. In this case, we would expect the "child" stacks to force the "parent" stack to be re-synced periodically. This functionality is powered by the pulumi.com/reconciliation-request annotation.

  1. Apply parent stack, and observe that it syncs once and then idles. Note that continueResyncOnCommitMatch is false.
  2. Apply child-1 stack, and observe that it syncs each minute (due to continueResyncOnCommitMatch), and that parent syncs each five minutes (due to succeededWithinDuration on child-1).
  3. Apply child-2 stack, and observe that it syncs each minute (due to continueResyncOnCommitMatch), and that parent syncs more frequently than before, each minute (due to succeededWithinDuration on child-2).

Use kubectl get events --watch to watch for syncs.

parent.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: prereqs
  namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: prereqs:system:auth-delegator
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:auth-delegator
subjects:
- kind: ServiceAccount
  name: prereqs
  namespace: default
---
apiVersion: pulumi.com/v1
kind: Stack
metadata:
  name: parent
  namespace: default
spec:
  serviceAccountName: prereqs
  fluxSource:
    sourceRef:
      apiVersion: source.toolkit.fluxcd.io/v1
      kind: GitRepository
      name: pulumi-examples
    dir: random-yaml/
  stack: parent
  continueResyncOnCommitMatch: false
  destroyOnFinalize: true
  envRefs:
    PULUMI_ACCESS_TOKEN:
      type: Secret
      secret:
        name: pulumi-api-secret
        key: accessToken

child-1.yaml

apiVersion: pulumi.com/v1
kind: Stack
metadata:
  name: child-1
  namespace: default
spec:
  prerequisites:
    - name: parent
      requirement:
        succeededWithinDuration: 5m
  serviceAccountName: prereqs
  fluxSource:
    sourceRef:
      apiVersion: source.toolkit.fluxcd.io/v1
      kind: GitRepository
      name: pulumi-examples
    dir: random-yaml/
  stack: child-1
  continueResyncOnCommitMatch: true
  resyncFrequencySeconds: 60
  destroyOnFinalize: true
  envRefs:
    PULUMI_ACCESS_TOKEN:
      type: Secret
      secret:
        name: pulumi-api-secret
        key: accessToken

child-2.yaml

apiVersion: pulumi.com/v1
kind: Stack
metadata:
  name: child-2
  namespace: default
spec:
  prerequisites:
    - name: parent
      requirement:
        succeededWithinDuration: 1m
  serviceAccountName: prereqs
  fluxSource:
    sourceRef:
      apiVersion: source.toolkit.fluxcd.io/v1
      kind: GitRepository
      name: pulumi-examples
    dir: random-yaml/
  stack: child-2
  continueResyncOnCommitMatch: true
  resyncFrequencySeconds: 60
  destroyOnFinalize: true
  envRefs:
    PULUMI_ACCESS_TOKEN:
      type: Secret
      secret:
        name: pulumi-api-secret
        key: accessToken