Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
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
3 changes: 3 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ jobs:
- name: Helm template tests
run: make helm-test

- name: Check release assets
run: make verify-release-manifest

- name: Vet
run: make vet

Expand Down
29 changes: 17 additions & 12 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,17 @@ jobs:
echo "version=${VERSION}" >> "${GITHUB_OUTPUT}"
echo "tag=v${VERSION}" >> "${GITHUB_OUTPUT}"

- name: Set up Go
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version-file: go.mod
cache: true # zizmor: ignore[cache-poisoning]

- name: Build and check release manifests
env:
TAG: ${{ steps.version.outputs.tag }}
run: make verify-release-manifest IMG="${IMAGE}:${TAG}"

- name: Create and push tag
env:
TAG: ${{ steps.version.outputs.tag }}
Expand Down Expand Up @@ -114,12 +125,6 @@ jobs:
exit 1
fi

- name: Set up Go
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version-file: go.mod
cache: true # zizmor: ignore[cache-poisoning]

- name: Build and push container image
env:
TAG: ${{ steps.version.outputs.tag }}
Expand All @@ -130,11 +135,6 @@ jobs:
podman login -u "${ACTOR}" -p "${GH_TOKEN}" ghcr.io
podman push "${IMAGE}:${TAG}"

- name: Build install manifest
env:
TAG: ${{ steps.version.outputs.tag }}
run: make release-manifest IMG="${IMAGE}:${TAG}"

- name: Package and push Helm charts
env:
VERSION: ${{ steps.version.outputs.version }}
Expand Down Expand Up @@ -169,6 +169,11 @@ jobs:
printf 'kubectl apply -f https://github.com/%s/releases/download/%s/install.yaml\n' \
"${GITHUB_REPOSITORY}" "${TAG}"
printf '```\n\n'
printf 'Optional configuration example: [operator-config.yaml](https://github.com/%s/releases/download/%s/operator-config.yaml). ' \
"${GITHUB_REPOSITORY}" "${TAG}"
printf 'Before applying it, check for an existing instance; see the [configuration guide](https://github.com/%s/blob/%s/docs/src/operations/configuration.md).\n' \
"${GITHUB_REPOSITORY}" "${TAG}"
printf '\n'
printf '### Helm\n\n'
printf '```bash\n'
printf 'helm install bootc-operator oci://ghcr.io/%s/charts/bootc-operator --version %s \\\n' \
Expand All @@ -181,4 +186,4 @@ jobs:
--notes-file notes.md \
--generate-notes \
--draft \
install.yaml
install.yaml operator-config.yaml
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ bin/
kubeconfig-*
docs/book/
user-docs
/install.yaml
/operator-config.yaml
chart/bootc-operator/crds/
chart/bootc-operator/charts/
chart/bootc-operator/Chart.lock
Expand Down
21 changes: 16 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -200,11 +200,21 @@ buildimg: ## Build container image.
-t $(IMG) .

.PHONY: release-manifest
release-manifest: kustomize yq ## Build install manifest (override IMG to set the image reference).
release-manifest: kustomize yq ## Build install manifest and optional configuration asset (override IMG).
"$(KUSTOMIZE)" build config/default | \
"$(YQ)" '(select(.kind == "Deployment") | .spec.template.spec.containers[] | select(.name == "manager")).image = "$(IMG)"' | \
"$(YQ)" '(select(.kind == "DaemonSet") | .spec.template.spec.containers[] | select(.name == "daemon")).image = "$(IMG)"' \
> install.yaml
cp config/samples/bootc_v1alpha1_bootcoperatorconfig.yaml operator-config.yaml

.PHONY: verify-release-manifest
verify-release-manifest: release-manifest ## Check the generated release assets.
@cmp -s config/samples/bootc_v1alpha1_bootcoperatorconfig.yaml operator-config.yaml || \
{ echo "operator-config.yaml differs from the validated example"; exit 1; }
@test "$$("$(YQ)" ea '[.] | map(select(.kind == "CustomResourceDefinition" and .metadata.name == "bootcoperatorconfigs.node.bootc.dev")) | length' install.yaml)" -eq 1 || \
{ echo "install.yaml must contain the BootcOperatorConfig CRD exactly once"; exit 1; }
@test "$$("$(YQ)" ea '[.] | map(select(.kind == "BootcOperatorConfig")) | length' install.yaml)" -eq 0 || \
{ echo "install.yaml must not create an administrator-owned BootcOperatorConfig"; exit 1; }

.PHONY: build-update-image
build-update-image: ## Build derived node images for update testing and push to bink registry.
Expand Down Expand Up @@ -281,7 +291,9 @@ start-bink: seed-node-image ## Start a bink cluster (idempotent).
kubectl --kubeconfig $(KUBECONFIG_BINK) wait --for=condition=Ready node/controller --timeout=5m

.PHONY: deploy-bink
deploy-bink: start-bink build-update-image $(if $(RELEASED_OPERATOR_TAG),push-released-operator-image) helm ## Deploy to a bink cluster (requires: buildimg).
deploy-bink: start-bink helm ## Deploy to a bink cluster (requires: buildimg).
$(MAKE) build-update-image
$(if $(RELEASED_OPERATOR_TAG),$(MAKE) push-released-operator-image)
podman push --tls-verify=false $(IMG) localhost:5000/bootc-operator-e2e:latest
helm dependency build ./chart/bootc-operator
kubectl --kubeconfig $(KUBECONFIG_BINK) create namespace $(E2E_PULL_SECRET_NAMESPACE) \
Expand All @@ -294,10 +306,9 @@ deploy-bink: start-bink build-update-image $(if $(RELEASED_OPERATOR_TAG),push-re
--set-string image.tag="$${img##*:}" \
--set-string image.pullPolicy=Always \
--set-json 'pullSecretNamespaces=["$(E2E_PULL_SECRET_NAMESPACE)"]' \
--set-json 'controller.extraArgs=["--allow-insecure-registry","--tag-resolution-interval=10s"]' \
--set-string 'daemon.extraArgs[0]=--bootc-poll-interval=$(E2E_BOOTC_POLL_INTERVAL)'
kubectl --kubeconfig $(KUBECONFIG_BINK) -n bootc-operator rollout restart deployment/bootc-operator-controller
kubectl --kubeconfig $(KUBECONFIG_BINK) -n bootc-operator rollout status deployment/bootc-operator-controller --timeout=3m
KUBECONFIG="$(abspath $(KUBECONFIG_BINK))" \
KUBECTL="$(KUBECTL)" ./hack/deploy-bink.sh

.PHONY: gather-bink
gather-bink: ## Gather diagnostic logs from the bink cluster.
Expand Down
8 changes: 8 additions & 0 deletions PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,12 @@ resources:
kind: BootcNode
path: github.com/jlebon/bootc-operator/api/v1alpha1
version: v1alpha1
- api:
crdVersion: v1
namespaced: false
domain: bootc.dev
group: node
kind: BootcOperatorConfig
path: github.com/bootc-dev/bootc-operator/api/v1alpha1
version: v1alpha1
version: "3"
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ kubectl apply -k https://github.com/bootc-dev/bootc-operator//config/default
This creates the `bootc-operator` namespace and deploys the controller and
daemon. The operator does nothing until you create a BootcNodePool.

To configure the controller and daemon with an optional
`BootcOperatorConfig` resource, see
[Configuring the operator](docs/src/operations/configuration.md). Releases
publish an editable `operator-config.yaml` example separately from
`install.yaml`.

> [!NOTE]
> The operator namespace requires a [Pod Security Admission] exemption for the
> `privileged` level. The daemon DaemonSet runs privileged to execute bootc
Expand Down
89 changes: 89 additions & 0 deletions api/v1alpha1/bootcoperatorconfig_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// SPDX-License-Identifier: Apache-2.0

package v1alpha1

import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

const (
// DefaultTagResolutionPeriodSeconds is the default interval between resolving image tags.
DefaultTagResolutionPeriodSeconds int32 = 300

// DefaultStatusPollPeriodSeconds is the default interval between fallback bootc status polls.
DefaultStatusPollPeriodSeconds int32 = 300
)

// OperatorControllerConfig configures the operator controller at startup.
type OperatorControllerConfig struct {
// allowInsecureRegistry allows falling back to HTTP when accessing image
// registries. Enable this only for trusted registries that do not support TLS.
// +optional
AllowInsecureRegistry *bool `json:"allowInsecureRegistry,omitempty"`

// tagResolutionPeriodSeconds is the interval in seconds between resolving
// image tags to digests. When omitted, use the CLI value or 300 seconds.
// +optional
// +kubebuilder:validation:Minimum=1
TagResolutionPeriodSeconds *int32 `json:"tagResolutionPeriodSeconds,omitempty"`
Comment thread
HarshwardhanPatil07 marked this conversation as resolved.
Resolved

// pullSecretNamespaces lists extra namespaces whose Secrets may be referenced
// by pools. The operator namespace is always allowed. Omission preserves CLI
// settings; an empty list allows no extras. Administrators must grant Secret get/list/watch access
// in each extra namespace separately. Changes require a controller restart.
// +optional
// +listType=set
// +kubebuilder:validation:items:MaxLength=63
// +kubebuilder:validation:items:Pattern=`^[a-z0-9]([-a-z0-9]*[a-z0-9])?$`
PullSecretNamespaces []string `json:"pullSecretNamespaces,omitzero"`
}

// OperatorDaemonConfig configures each node daemon at startup.
type OperatorDaemonConfig struct {
// statusPollPeriodSeconds is the interval in seconds between bootc status
// polls when filesystem notifications are unavailable. When omitted, use the
// CLI value or 300 seconds.
// +optional
// +kubebuilder:validation:Minimum=1
StatusPollPeriodSeconds *int32 `json:"statusPollPeriodSeconds,omitempty"`
}

// BootcOperatorConfigSpec defines operator settings consumed at process startup.
// Restart the affected controller or daemon pods after changing these settings.
type BootcOperatorConfigSpec struct {
// controller configures the controller. Omitted settings use CLI values or built-in defaults.
// +optional
// +kubebuilder:default={}
Controller *OperatorControllerConfig `json:"controller,omitempty"`

// daemon configures all node daemons. Omitted settings use CLI values or built-in defaults.
// +optional
// +kubebuilder:default={}
Daemon *OperatorDaemonConfig `json:"daemon,omitempty"`
}

// +kubebuilder:object:root=true
// +kubebuilder:resource:scope=Cluster

// BootcOperatorConfig configures the controller and node daemons in a cluster.
// The optional resource is administrator-owned and may have any valid name.
// The operator rejects multiple configurations at startup. Changes take effect
// when the affected pods restart; supplied fields take precedence over CLI flags.
type BootcOperatorConfig struct {
metav1.TypeMeta `json:",inline"`

// metadata is standard object metadata.
// +optional
metav1.ObjectMeta `json:"metadata,omitzero"`

// spec defines the configuration consumed when operator processes start.
// +required
Spec BootcOperatorConfigSpec `json:"spec"`
}

// +kubebuilder:object:root=true

// BootcOperatorConfigList contains a list of BootcOperatorConfig.
type BootcOperatorConfigList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitzero"`
Items []BootcOperatorConfig `json:"items"`
}
1 change: 1 addition & 0 deletions api/v1alpha1/groupversion_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&BootcNode{}, &BootcNodeList{},
&BootcNodePool{}, &BootcNodePoolList{},
&BootcOperatorConfig{}, &BootcOperatorConfigList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
Expand Down
133 changes: 133 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.

Loading
Loading