diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..ddac962 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,36 @@ +version: 2 + +updates: + # Go modules + - package-ecosystem: gomod + directory: / + schedule: + interval: weekly + day: monday + groups: + go-deps: + patterns: + - "*" + update-types: + - minor + - patch + open-pull-requests-limit: 5 + commit-message: + prefix: "deps" + cooldown: + default-days: 7 + + # GitHub Actions + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + day: monday + groups: + actions: + patterns: + - "*" + commit-message: + prefix: "ci" + cooldown: + default-days: 7 diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 58ef01d..fdf1d42 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -3,12 +3,15 @@ name: CI on: push: branches: [main] + tags: ["v*"] pull_request: branches: [main] env: BINK_VERSION: v0.1.1 +permissions: {} + concurrency: group: ci-${{ github.head_ref || github.ref }} cancel-in-progress: true @@ -16,15 +19,19 @@ concurrency: jobs: unit: runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Set up Go - uses: actions/setup-go@v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: go.mod - cache: true + cache: true # zizmor: ignore[cache-poisoning] - name: Unit tests run: make unit @@ -38,9 +45,16 @@ jobs: e2e: runs-on: ubuntu-latest timeout-minutes: 30 + permissions: + contents: read + packages: write + env: + IMAGE: ghcr.io/${{ github.repository }} steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Download bink release run: | @@ -49,10 +63,10 @@ jobs: sudo chmod +x /usr/local/bin/bink - name: Set up Go - uses: actions/setup-go@v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: go.mod - cache: true + cache: true # zizmor: ignore[cache-poisoning] - name: Set up KVM run: sudo chmod 666 /dev/kvm @@ -81,3 +95,24 @@ jobs: - name: Run e2e tests run: make buildimg deploy-bink e2e V=1 + + - name: Push to GHCR + if: github.event_name == 'push' + env: + ACTOR: ${{ github.actor }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SHA: ${{ github.sha }} + REF: ${{ github.ref }} + REF_NAME: ${{ github.ref_name }} + run: | + podman login -u "${ACTOR}" -p "${GH_TOKEN}" ghcr.io + podman push bootc-operator:dev "${IMAGE}":dev + podman push bootc-operator:dev "${IMAGE}":"${SHA}" + + if [[ "${REF}" == refs/tags/v* ]]; then + podman push bootc-operator:dev "${IMAGE}":"${REF_NAME}" + fi + + if [[ "${REF}" == refs/heads/main ]]; then + podman push bootc-operator:dev "${IMAGE}":latest + fi diff --git a/.github/workflows/dependabot.yml b/.github/workflows/dependabot.yml new file mode 100644 index 0000000..c9146bb --- /dev/null +++ b/.github/workflows/dependabot.yml @@ -0,0 +1,26 @@ +name: Dependabot + +on: pull_request + +permissions: {} + +jobs: + automerge: + runs-on: ubuntu-latest + permissions: + contents: write # Required to merge PR + pull-requests: write # Required to enable auto-merge for PR + if: github.event.pull_request.user.login == 'dependabot[bot]' + steps: + - name: Fetch Dependabot metadata + id: metadata + uses: dependabot/fetch-metadata@25dd0e34f4fe68f24cc83900b1fe3fe149efef98 # v3.1.0 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Enable auto-merge for minor/patch updates + if: steps.metadata.outputs.update-type != 'version-update:semver-major' + run: gh pr merge --auto --rebase "$PR_URL" + env: + PR_URL: ${{ github.event.pull_request.html_url }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml new file mode 100644 index 0000000..7ec0495 --- /dev/null +++ b/.github/workflows/zizmor.yml @@ -0,0 +1,24 @@ +name: Zizmor + +on: + push: + branches: [main] + pull_request: + branches: ["**"] + +permissions: {} + +jobs: + zizmor: + name: Scan GHA workflows + runs-on: ubuntu-24.04 + permissions: + security-events: write # Required to upload SARIF files + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Run zizmor + uses: zizmorcore/zizmor-action@5f14fd08f7cf1cb1609c1e344975f152c7ee938d # v0.5.6 diff --git a/Containerfile b/Containerfile index e12e199..e6a92ad 100644 --- a/Containerfile +++ b/Containerfile @@ -15,7 +15,6 @@ RUN --mount=type=cache,id=gomod,target=/root/go/pkg/mod \ go build -o daemon ./cmd/daemon/ FROM quay.io/fedora/fedora-minimal:44 -WORKDIR / -COPY --from=builder /workspace/manager /workspace/daemon . +COPY --from=builder /workspace/manager /workspace/daemon /usr/local/bin/ USER 65532:65532 -ENTRYPOINT ["/manager"] +ENTRYPOINT ["manager"] diff --git a/config/daemon/daemon.yaml b/config/daemon/daemon.yaml index 0891bc6..16f5234 100644 --- a/config/daemon/daemon.yaml +++ b/config/daemon/daemon.yaml @@ -24,8 +24,8 @@ spec: containers: - name: daemon command: - - /daemon - image: controller:latest + - daemon + image: ghcr.io/jlebon/bootc-operator:latest env: - name: NODE_NAME valueFrom: diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index 11d3753..dc18e6b 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -66,11 +66,11 @@ spec: type: RuntimeDefault containers: - command: - - /manager + - manager args: - --leader-elect - --health-probe-bind-address=:8081 - image: controller:latest + image: ghcr.io/jlebon/bootc-operator:latest name: manager ports: - containerPort: 8081 diff --git a/config/samples/bootc_v1alpha1_bootcnode.yaml b/config/samples/bootc_v1alpha1_bootcnode.yaml deleted file mode 100644 index d83d491..0000000 --- a/config/samples/bootc_v1alpha1_bootcnode.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: node.bootc.dev/v1alpha1 -kind: BootcNode -metadata: - labels: - app.kubernetes.io/name: bootc-operator - app.kubernetes.io/managed-by: kustomize - name: bootcnode-sample -spec: - # TODO(user): Add fields here diff --git a/config/samples/bootc_v1alpha1_bootcnodepool.yaml b/config/samples/bootc_v1alpha1_bootcnodepool.yaml index 8f4c885..47cffa3 100644 --- a/config/samples/bootc_v1alpha1_bootcnodepool.yaml +++ b/config/samples/bootc_v1alpha1_bootcnodepool.yaml @@ -1,9 +1,21 @@ +# This sample pool targets all worker nodes. All optional fields are shown +# commented out with default or example values. apiVersion: node.bootc.dev/v1alpha1 kind: BootcNodePool metadata: - labels: - app.kubernetes.io/name: bootc-operator - app.kubernetes.io/managed-by: kustomize - name: bootcnodepool-sample + name: workers spec: - # TODO(user): Add fields here + nodeSelector: + matchLabels: + node-role.kubernetes.io/worker: "" + image: + ref: ghcr.io/alicefr/bink/node:latest + # rollout: + # maxUnavailable: 1 # default + # paused: false # default + # drainTimeoutSeconds: 300 # example; default: no timeout + # disruption: + # rebootPolicy: AllowSoftReboot # example; default: RebootOnly + # pullSecretRef: # example; default: none + # name: my-pull-secret + # namespace: bootc-operator diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index 2e4d43d..e1f578c 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -1,5 +1,2 @@ -## Append samples of your project ## resources: - bootc_v1alpha1_bootcnodepool.yaml -- bootc_v1alpha1_bootcnode.yaml -# +kubebuilder:scaffold:manifestskustomizesamples diff --git a/test/e2e/e2eutil/env.go b/test/e2e/e2eutil/env.go index 7880d9e..c5442a9 100644 --- a/test/e2e/e2eutil/env.go +++ b/test/e2e/e2eutil/env.go @@ -111,8 +111,8 @@ func WithLabel(key, value string) NodeOption { } // AddNode provisions a worker node via bink, waits for it to be Ready, -// labels it with LabelE2ETest, and registers cleanup to remove it. -// Returns the node name. +// and returns the node name. The node is labeled with LabelE2ETest +// (and any extra labels from WithLabel). func (e *Env) AddNode(t *testing.T, opts ...NodeOption) string { t.Helper() @@ -123,8 +123,12 @@ func (e *Env) AddNode(t *testing.T, opts ...NodeOption) string { nodeName := e.generateNodeName(t) - // Provision the node. + // Provision the node with labels applied at join time. args := []string{"node", "add", nodeName, "--cluster-name", e.clusterName, "--control-plane", "controller"} + args = append(args, "--label", LabelE2ETest+"="+e.testID) + for k, v := range cfg.labels { + args = append(args, "--label", k+"="+v) + } if cfg.memory > 0 { args = append(args, "--memory", fmt.Sprintf("%d", cfg.memory)) } @@ -141,24 +145,6 @@ func (e *Env) AddNode(t *testing.T, opts ...NodeOption) string { // Wait for Ready. waitForNodeReady(t, e.Client, nodeName) - // Label with test ID (replace with --label` once available: - // https://github.com/alicefr/bink/issues/23). - var node corev1.Node - if err := e.Client.Get(context.Background(), client.ObjectKey{Name: nodeName}, &node); err != nil { - t.Fatalf("getting node %q for labeling: %v", nodeName, err) - } - patch := client.StrategicMergeFrom(node.DeepCopy()) - if node.Labels == nil { - node.Labels = map[string]string{} - } - node.Labels[LabelE2ETest] = e.testID - for k, v := range cfg.labels { - node.Labels[k] = v - } - if err := e.Client.Patch(context.Background(), &node, patch); err != nil { - t.Fatalf("labeling node %q: %v", nodeName, err) - } - return nodeName }