Add ability to include/exclude namespaces.

This commit is contained in:
2024-09-05 08:15:05 +01:00
parent 0e34ab7a27
commit 9b1135cb7b
15 changed files with 124 additions and 37 deletions
+1 -1
View File
@@ -22,7 +22,7 @@ COPY internal/ internal/
# was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO
# the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore,
# by leaving it empty we can ensure that the container and binary shipped on it will have the same platform.
RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -ldflags "-X shared.BACKUP_JOB_IMAGE=ghcr.io/lukaszraczylo/kubernetes-images-sync-worker:v${IMAGE_VERSION_TAG}" -a -o manager cmd/main.go
RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -ldflags "-X github.com/lukaszraczylo/kubernetes-images-sync-operator/internal/shared.BACKUP_JOB_IMAGE=ghcr.io/lukaszraczylo/kubernetes-images-sync-worker:${IMAGE_VERSION_TAG}" -a -o manager cmd/main.go
# Use distroless as minimal base image to package the manager binary
# Refer to https://github.com/GoogleContainerTools/distroless for more details
+5 -3
View File
@@ -10,6 +10,8 @@ ifeq ($(CURRENT_VERSION),)
$(error Failed to extract version number)
endif
IMAGE_VERSION_TAG ?= $(CURRENT_VERSION)
# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
ifeq (,$(shell go env GOBIN))
GOBIN=$(shell go env GOPATH)/bin
@@ -87,11 +89,11 @@ lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes
.PHONY: build
build: manifests generate fmt vet ## Build manager binary.
go build -o bin/manager cmd/main.go
go build -ldflags "-X github.com/lukaszraczylo/kubernetes-images-sync-operator/internal/shared.BACKUP_JOB_IMAGE=ghcr.io/lukaszraczylo/kubernetes-images-sync-worker:$(IMAGE_VERSION_TAG)" -o bin/manager cmd/main.go
.PHONY: run
run: manifests generate fmt vet ## Run a controller from your host.
go run ./cmd/main.go
go run -ldflags "-X github.com/lukaszraczylo/kubernetes-images-sync-operator/internal/shared.BACKUP_JOB_IMAGE=ghcr.io/lukaszraczylo/kubernetes-images-sync-worker:$(IMAGE_VERSION_TAG)" ./cmd/main.go
# If you wish to build the manager image targeting other platforms you can use the --platform flag.
# (i.e. docker build --platform linux/arm64). However, you must enable docker buildKit for it.
@@ -231,7 +233,7 @@ release-chart:
cr package --config ../../chart-releaser.yaml;
cd ../helm-charts/; git add -A charts/packages; git fix; git push;
cd ../helm-charts/charts/${CHART_NAME}; cr upload --config ../../chart-releaser.yaml --skip-existing;
cd ../helm-charts/charts/${CHART_NAME}; rm -fr .cr-index; mkdir .cr-index; cr index --config ../../chart-releaser.yaml; cp .cr-index/index.yaml ../../index.yaml; || true
cd ../helm-charts/charts/${CHART_NAME}; rm -fr .cr-index; mkdir .cr-index; cr index --config ../../chart-releaser.yaml; cp .cr-index/index.yaml ../../index.yaml;
git fix; git push
# go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist
+11
View File
@@ -33,9 +33,20 @@ spec:
# Excludes will remove all images with listed wording from the backup list
# excludes:
# - nginx
# Includes will add ONLY images with listed wording to the backup list
includes:
- busybox
# Works only with images within specified namespaces
# namespaces:
# - default
# - longhorn
# Works with all images EXCEPT of the ones within namespaces specified
# excludedNamespaces:
# - my-awesome-namespace
basePath: /images # base path in the target directory
storage:
target: S3 # file backup is not ready yet
+8 -7
View File
@@ -28,13 +28,14 @@ import (
// +kubebuilder:printcolumn:name="Path",type="string",JSONPath=".spec.exportPath"
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
type ClusterImageSpec struct {
Image string `json:"image,omitempty"`
Tag string `json:"tag,omitempty"`
Sha string `json:"sha,omitempty"`
FullName string `json:"fullName,omitempty"` // Because I'm lazy and it's easier to pull that way
Storage string `json:"storage,omitempty"`
ExportName string `json:"exportName"`
ExportPath string `json:"exportPath,omitempty"`
Image string `json:"image,omitempty"`
Tag string `json:"tag,omitempty"`
Sha string `json:"sha,omitempty"`
FullName string `json:"fullName,omitempty"` // Because I'm lazy and it's easier to pull that way
Storage string `json:"storage,omitempty"`
ExportName string `json:"exportName"`
ExportPath string `json:"exportPath,omitempty"`
ImageNamespace string `json:"imageNamespace,omitempty"`
}
// ClusterImageStatus defines the observed state of ClusterImage
@@ -54,7 +54,9 @@ type ClusterImageExportSpec struct {
// Exclude images which contain these strings
Excludes []string `json:"excludes,omitempty"`
// Include only images which contain these strings
Includes []string `json:"includes,omitempty"`
Includes []string `json:"includes,omitempty"`
Namespaces []string `json:"namespaces,omitempty"`
ExcludedNamespaces []string `json:"excludedNamespaces,omitempty"`
// Base path for the export - both file and S3
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=255
@@ -124,6 +124,16 @@ func (in *ClusterImageExportSpec) DeepCopyInto(out *ClusterImageExportSpec) {
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Namespaces != nil {
in, out := &in.Namespaces, &out.Namespaces
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.ExcludedNamespaces != nil {
in, out := &in.ExcludedNamespaces, &out.ExcludedNamespaces
*out = make([]string, len(*in))
copy(*out, *in)
}
out.Storage = in.Storage
}
+2 -2
View File
@@ -10,9 +10,9 @@ description: |
type: application
version: 0.1.5
version: 0.1.7
appVersion: "0.1.5"
appVersion: "0.1.7"
home: https://github.com/lukaszraczylo/kubernetes-images-sync-operator
+2
View File
@@ -76,6 +76,8 @@ spec:
type: string
image:
type: string
imageNamespace:
type: string
sha:
type: string
storage:
@@ -61,6 +61,10 @@ spec:
createdAt:
format: date-time
type: string
excludedNamespaces:
items:
type: string
type: array
excludes:
description: Exclude images which contain these strings
items:
@@ -75,6 +79,10 @@ spec:
type: integer
name:
type: string
namespaces:
items:
type: string
type: array
storage:
description: ClusterImageStorageSpec defines the desired state of ClusterImageStorage
properties:
+1 -1
View File
@@ -11,7 +11,7 @@ cmRaczyloCom:
- ALL
image:
repository: ghcr.io/lukaszraczylo/kubernetes-images-sync-operator
tag: 0.1.5
tag: 0.1.7
resources:
limits:
cpu: 500m
@@ -61,6 +61,10 @@ spec:
createdAt:
format: date-time
type: string
excludedNamespaces:
items:
type: string
type: array
excludes:
description: Exclude images which contain these strings
items:
@@ -75,6 +79,10 @@ spec:
type: integer
name:
type: string
namespaces:
items:
type: string
type: array
storage:
description: ClusterImageStorageSpec defines the desired state of
ClusterImageStorage
@@ -75,6 +75,8 @@ spec:
type: string
image:
type: string
imageNamespace:
type: string
sha:
type: string
storage:
@@ -130,13 +130,14 @@ func (r *ClusterImageExportReconciler) Reconcile(ctx context.Context, req ctrl.R
},
},
Spec: raczylocomv1.ClusterImageSpec{
Image: image.Image,
Tag: image.Tag,
Sha: image.Sha,
FullName: image.FullName,
Storage: clusterImageExport.Spec.Storage.StorageTarget,
ExportName: clusterImageExport.Name,
ExportPath: clusterImageExport.Spec.BasePath,
Image: image.Image,
Tag: image.Tag,
Sha: image.Sha,
FullName: image.FullName,
ImageNamespace: image.ImageNamespace,
Storage: clusterImageExport.Spec.Storage.StorageTarget,
ExportName: clusterImageExport.Name,
ExportPath: clusterImageExport.Spec.BasePath,
},
}
@@ -211,6 +212,14 @@ func (r *ClusterImageExportReconciler) listImagesInCluster(ctx context.Context,
containersList = shared.RemoveExcludedImages(containersList, clusterImageExport.Spec.Excludes)
}
if len(clusterImageExport.Spec.Namespaces) > 0 {
containersList = shared.FilterOnlyFromNamespaces(containersList, clusterImageExport.Spec.Namespaces)
}
if len(clusterImageExport.Spec.ExcludedNamespaces) > 0 {
containersList = shared.FilterOutWholeNamespaces(containersList, clusterImageExport.Spec.ExcludedNamespaces)
}
containersList = shared.RemoveDuplicates(containersList)
l.Info("List of containers in the cluster", "containers", containersList)
+38 -7
View File
@@ -5,10 +5,9 @@ import (
"strings"
)
const (
// JOB IMAGES
BACKUP_JOB_IMAGE = "ghcr.io/lukaszraczylo/kubernetes-images-sync-worker:1.0.2"
var BACKUP_JOB_IMAGE = "ghcr.io/lukaszraczylo/kubernetes-images-sync-worker:1.0.2"
const (
// AVAILABLE STATUSES
STATUS_PENDING = "PENDING"
STATUS_STARTING = "STARTING"
@@ -24,10 +23,11 @@ const (
)
type Container struct {
Image string `json:"image"`
Tag string `json:"tag"`
Sha string `json:"sha"`
FullName string `json:"fullName"`
Image string `json:"image"`
Tag string `json:"tag"`
Sha string `json:"sha"`
FullName string `json:"fullName"`
ImageNamespace string `json:"imageNamespace"`
}
type ContainersList struct {
@@ -96,3 +96,34 @@ func NormalizeImageName(name string) string {
// Trim leading and trailing hyphens
return strings.Trim(normalized, "-")
}
// filterOnlyFromNamespaces filters out containers from namespaces that are not in the list
func FilterOnlyFromNamespaces(containers ContainersList, namespaces []string) ContainersList {
result := ContainersList{}
for _, container := range containers.Containers {
for _, namespace := range namespaces {
if container.ImageNamespace == namespace {
result.Containers = append(result.Containers, container)
}
}
}
return result
}
// filterOutWholeNamespaces filters out containers from namespaces that are in the list
func FilterOutWholeNamespaces(containers ContainersList, namespaces []string) ContainersList {
result := ContainersList{}
for _, container := range containers.Containers {
excluded := false
for _, namespace := range namespaces {
if container.ImageNamespace == namespace {
excluded = true
break
}
}
if !excluded {
result.Containers = append(result.Containers, container)
}
}
return result
}
+9 -8
View File
@@ -64,7 +64,7 @@ func processContainerName(containerName string) (Container, error) {
return cnt, nil
}
func processContainers[T K8sResource](resource T, containersList *ContainersList) error {
func processContainers[T K8sResource](resource T, namespace string, containersList *ContainersList) error {
podSpec := resource.GetPodSpec()
if podSpec == nil {
return fmt.Errorf("nil PodSpec")
@@ -72,13 +72,13 @@ func processContainers[T K8sResource](resource T, containersList *ContainersList
allContainers := append(podSpec.Containers, podSpec.InitContainers...)
for _, container := range allContainers {
if err := processContainer(container.Image, containersList); err != nil {
if err := processContainer(container.Image, namespace, containersList); err != nil {
return err
}
}
for _, container := range podSpec.EphemeralContainers {
if err := processContainer(container.EphemeralContainerCommon.Image, containersList); err != nil {
if err := processContainer(container.EphemeralContainerCommon.Image, namespace, containersList); err != nil {
return err
}
}
@@ -87,11 +87,12 @@ func processContainers[T K8sResource](resource T, containersList *ContainersList
}
// processContainer handles the processing of a single container image
func processContainer(image string, containersList *ContainersList) error {
func processContainer(image string, containerNamespace string, containersList *ContainersList) error {
cnt, err := processContainerName(image)
if err != nil {
return fmt.Errorf("failed to process container name: %s - %w", image, err)
}
cnt.ImageNamespace = containerNamespace
containersList.Containers = append(containersList.Containers, cnt)
return nil
}
@@ -105,25 +106,25 @@ func ListAndProcessResources[T K8sResource, L client.ObjectList](ctx context.Con
switch typedList := any(list).(type) {
case *appsv1.DeploymentList:
for i := range typedList.Items {
if err := processContainers((*DeploymentWrapper)(&typedList.Items[i]), containersList); err != nil {
if err := processContainers((*DeploymentWrapper)(&typedList.Items[i]), typedList.Items[i].Namespace, containersList); err != nil {
return err
}
}
case *batchv1.JobList:
for i := range typedList.Items {
if err := processContainers((*JobWrapper)(&typedList.Items[i]), containersList); err != nil {
if err := processContainers((*JobWrapper)(&typedList.Items[i]), typedList.Items[i].Namespace, containersList); err != nil {
return err
}
}
case *appsv1.DaemonSetList:
for i := range typedList.Items {
if err := processContainers((*DaemonSetWrapper)(&typedList.Items[i]), containersList); err != nil {
if err := processContainers((*DaemonSetWrapper)(&typedList.Items[i]), typedList.Items[i].Namespace, containersList); err != nil {
return err
}
}
case *batchv1.CronJobList:
for i := range typedList.Items {
if err := processContainers((*CronJobWrapper)(&typedList.Items[i]), containersList); err != nil {
if err := processContainers((*CronJobWrapper)(&typedList.Items[i]), typedList.Items[i].Namespace, containersList); err != nil {
return err
}
}