mirror of
https://github.com/lukaszraczylo/kubernetes-images-sync-operator.git
synced 2026-06-27 02:34:14 +00:00
Add ability to include/exclude namespaces.
This commit is contained in:
+1
-1
@@ -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
|
# 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,
|
# 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.
|
# 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
|
# Use distroless as minimal base image to package the manager binary
|
||||||
# Refer to https://github.com/GoogleContainerTools/distroless for more details
|
# Refer to https://github.com/GoogleContainerTools/distroless for more details
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ ifeq ($(CURRENT_VERSION),)
|
|||||||
$(error Failed to extract version number)
|
$(error Failed to extract version number)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
IMAGE_VERSION_TAG ?= $(CURRENT_VERSION)
|
||||||
|
|
||||||
# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
|
# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
|
||||||
ifeq (,$(shell go env GOBIN))
|
ifeq (,$(shell go env GOBIN))
|
||||||
GOBIN=$(shell go env GOPATH)/bin
|
GOBIN=$(shell go env GOPATH)/bin
|
||||||
@@ -87,11 +89,11 @@ lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes
|
|||||||
|
|
||||||
.PHONY: build
|
.PHONY: build
|
||||||
build: manifests generate fmt vet ## Build manager binary.
|
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
|
.PHONY: run
|
||||||
run: manifests generate fmt vet ## Run a controller from your host.
|
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.
|
# 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.
|
# (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;
|
cr package --config ../../chart-releaser.yaml;
|
||||||
cd ../helm-charts/; git add -A charts/packages; git fix; git push;
|
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}; 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
|
git fix; git push
|
||||||
|
|
||||||
# go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist
|
# go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist
|
||||||
|
|||||||
@@ -33,9 +33,20 @@ spec:
|
|||||||
# Excludes will remove all images with listed wording from the backup list
|
# Excludes will remove all images with listed wording from the backup list
|
||||||
# excludes:
|
# excludes:
|
||||||
# - nginx
|
# - nginx
|
||||||
|
|
||||||
# Includes will add ONLY images with listed wording to the backup list
|
# Includes will add ONLY images with listed wording to the backup list
|
||||||
includes:
|
includes:
|
||||||
- busybox
|
- 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
|
basePath: /images # base path in the target directory
|
||||||
storage:
|
storage:
|
||||||
target: S3 # file backup is not ready yet
|
target: S3 # file backup is not ready yet
|
||||||
|
|||||||
@@ -28,13 +28,14 @@ import (
|
|||||||
// +kubebuilder:printcolumn:name="Path",type="string",JSONPath=".spec.exportPath"
|
// +kubebuilder:printcolumn:name="Path",type="string",JSONPath=".spec.exportPath"
|
||||||
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
|
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
|
||||||
type ClusterImageSpec struct {
|
type ClusterImageSpec struct {
|
||||||
Image string `json:"image,omitempty"`
|
Image string `json:"image,omitempty"`
|
||||||
Tag string `json:"tag,omitempty"`
|
Tag string `json:"tag,omitempty"`
|
||||||
Sha string `json:"sha,omitempty"`
|
Sha string `json:"sha,omitempty"`
|
||||||
FullName string `json:"fullName,omitempty"` // Because I'm lazy and it's easier to pull that way
|
FullName string `json:"fullName,omitempty"` // Because I'm lazy and it's easier to pull that way
|
||||||
Storage string `json:"storage,omitempty"`
|
Storage string `json:"storage,omitempty"`
|
||||||
ExportName string `json:"exportName"`
|
ExportName string `json:"exportName"`
|
||||||
ExportPath string `json:"exportPath,omitempty"`
|
ExportPath string `json:"exportPath,omitempty"`
|
||||||
|
ImageNamespace string `json:"imageNamespace,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClusterImageStatus defines the observed state of ClusterImage
|
// ClusterImageStatus defines the observed state of ClusterImage
|
||||||
|
|||||||
@@ -54,7 +54,9 @@ type ClusterImageExportSpec struct {
|
|||||||
// Exclude images which contain these strings
|
// Exclude images which contain these strings
|
||||||
Excludes []string `json:"excludes,omitempty"`
|
Excludes []string `json:"excludes,omitempty"`
|
||||||
// Include only images which contain these strings
|
// 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
|
// Base path for the export - both file and S3
|
||||||
// +kubebuilder:validation:MinLength=1
|
// +kubebuilder:validation:MinLength=1
|
||||||
// +kubebuilder:validation:MaxLength=255
|
// +kubebuilder:validation:MaxLength=255
|
||||||
|
|||||||
@@ -124,6 +124,16 @@ func (in *ClusterImageExportSpec) DeepCopyInto(out *ClusterImageExportSpec) {
|
|||||||
*out = make([]string, len(*in))
|
*out = make([]string, len(*in))
|
||||||
copy(*out, *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
|
out.Storage = in.Storage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -10,9 +10,9 @@ description: |
|
|||||||
|
|
||||||
type: application
|
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
|
home: https://github.com/lukaszraczylo/kubernetes-images-sync-operator
|
||||||
|
|
||||||
|
|||||||
@@ -76,6 +76,8 @@ spec:
|
|||||||
type: string
|
type: string
|
||||||
image:
|
image:
|
||||||
type: string
|
type: string
|
||||||
|
imageNamespace:
|
||||||
|
type: string
|
||||||
sha:
|
sha:
|
||||||
type: string
|
type: string
|
||||||
storage:
|
storage:
|
||||||
|
|||||||
@@ -61,6 +61,10 @@ spec:
|
|||||||
createdAt:
|
createdAt:
|
||||||
format: date-time
|
format: date-time
|
||||||
type: string
|
type: string
|
||||||
|
excludedNamespaces:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
excludes:
|
excludes:
|
||||||
description: Exclude images which contain these strings
|
description: Exclude images which contain these strings
|
||||||
items:
|
items:
|
||||||
@@ -75,6 +79,10 @@ spec:
|
|||||||
type: integer
|
type: integer
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
|
namespaces:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
storage:
|
storage:
|
||||||
description: ClusterImageStorageSpec defines the desired state of ClusterImageStorage
|
description: ClusterImageStorageSpec defines the desired state of ClusterImageStorage
|
||||||
properties:
|
properties:
|
||||||
|
|||||||
+1
-1
@@ -11,7 +11,7 @@ cmRaczyloCom:
|
|||||||
- ALL
|
- ALL
|
||||||
image:
|
image:
|
||||||
repository: ghcr.io/lukaszraczylo/kubernetes-images-sync-operator
|
repository: ghcr.io/lukaszraczylo/kubernetes-images-sync-operator
|
||||||
tag: 0.1.5
|
tag: 0.1.7
|
||||||
resources:
|
resources:
|
||||||
limits:
|
limits:
|
||||||
cpu: 500m
|
cpu: 500m
|
||||||
|
|||||||
@@ -61,6 +61,10 @@ spec:
|
|||||||
createdAt:
|
createdAt:
|
||||||
format: date-time
|
format: date-time
|
||||||
type: string
|
type: string
|
||||||
|
excludedNamespaces:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
excludes:
|
excludes:
|
||||||
description: Exclude images which contain these strings
|
description: Exclude images which contain these strings
|
||||||
items:
|
items:
|
||||||
@@ -75,6 +79,10 @@ spec:
|
|||||||
type: integer
|
type: integer
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
|
namespaces:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
storage:
|
storage:
|
||||||
description: ClusterImageStorageSpec defines the desired state of
|
description: ClusterImageStorageSpec defines the desired state of
|
||||||
ClusterImageStorage
|
ClusterImageStorage
|
||||||
|
|||||||
@@ -75,6 +75,8 @@ spec:
|
|||||||
type: string
|
type: string
|
||||||
image:
|
image:
|
||||||
type: string
|
type: string
|
||||||
|
imageNamespace:
|
||||||
|
type: string
|
||||||
sha:
|
sha:
|
||||||
type: string
|
type: string
|
||||||
storage:
|
storage:
|
||||||
|
|||||||
@@ -130,13 +130,14 @@ func (r *ClusterImageExportReconciler) Reconcile(ctx context.Context, req ctrl.R
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Spec: raczylocomv1.ClusterImageSpec{
|
Spec: raczylocomv1.ClusterImageSpec{
|
||||||
Image: image.Image,
|
Image: image.Image,
|
||||||
Tag: image.Tag,
|
Tag: image.Tag,
|
||||||
Sha: image.Sha,
|
Sha: image.Sha,
|
||||||
FullName: image.FullName,
|
FullName: image.FullName,
|
||||||
Storage: clusterImageExport.Spec.Storage.StorageTarget,
|
ImageNamespace: image.ImageNamespace,
|
||||||
ExportName: clusterImageExport.Name,
|
Storage: clusterImageExport.Spec.Storage.StorageTarget,
|
||||||
ExportPath: clusterImageExport.Spec.BasePath,
|
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)
|
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)
|
containersList = shared.RemoveDuplicates(containersList)
|
||||||
l.Info("List of containers in the cluster", "containers", containersList)
|
l.Info("List of containers in the cluster", "containers", containersList)
|
||||||
|
|
||||||
|
|||||||
@@ -5,10 +5,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
var BACKUP_JOB_IMAGE = "ghcr.io/lukaszraczylo/kubernetes-images-sync-worker:1.0.2"
|
||||||
// JOB IMAGES
|
|
||||||
BACKUP_JOB_IMAGE = "ghcr.io/lukaszraczylo/kubernetes-images-sync-worker:1.0.2"
|
|
||||||
|
|
||||||
|
const (
|
||||||
// AVAILABLE STATUSES
|
// AVAILABLE STATUSES
|
||||||
STATUS_PENDING = "PENDING"
|
STATUS_PENDING = "PENDING"
|
||||||
STATUS_STARTING = "STARTING"
|
STATUS_STARTING = "STARTING"
|
||||||
@@ -24,10 +23,11 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Container struct {
|
type Container struct {
|
||||||
Image string `json:"image"`
|
Image string `json:"image"`
|
||||||
Tag string `json:"tag"`
|
Tag string `json:"tag"`
|
||||||
Sha string `json:"sha"`
|
Sha string `json:"sha"`
|
||||||
FullName string `json:"fullName"`
|
FullName string `json:"fullName"`
|
||||||
|
ImageNamespace string `json:"imageNamespace"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ContainersList struct {
|
type ContainersList struct {
|
||||||
@@ -96,3 +96,34 @@ func NormalizeImageName(name string) string {
|
|||||||
// Trim leading and trailing hyphens
|
// Trim leading and trailing hyphens
|
||||||
return strings.Trim(normalized, "-")
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ func processContainerName(containerName string) (Container, error) {
|
|||||||
return cnt, nil
|
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()
|
podSpec := resource.GetPodSpec()
|
||||||
if podSpec == nil {
|
if podSpec == nil {
|
||||||
return fmt.Errorf("nil PodSpec")
|
return fmt.Errorf("nil PodSpec")
|
||||||
@@ -72,13 +72,13 @@ func processContainers[T K8sResource](resource T, containersList *ContainersList
|
|||||||
|
|
||||||
allContainers := append(podSpec.Containers, podSpec.InitContainers...)
|
allContainers := append(podSpec.Containers, podSpec.InitContainers...)
|
||||||
for _, container := range allContainers {
|
for _, container := range allContainers {
|
||||||
if err := processContainer(container.Image, containersList); err != nil {
|
if err := processContainer(container.Image, namespace, containersList); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, container := range podSpec.EphemeralContainers {
|
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
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,11 +87,12 @@ func processContainers[T K8sResource](resource T, containersList *ContainersList
|
|||||||
}
|
}
|
||||||
|
|
||||||
// processContainer handles the processing of a single container image
|
// 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)
|
cnt, err := processContainerName(image)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to process container name: %s - %w", image, err)
|
return fmt.Errorf("failed to process container name: %s - %w", image, err)
|
||||||
}
|
}
|
||||||
|
cnt.ImageNamespace = containerNamespace
|
||||||
containersList.Containers = append(containersList.Containers, cnt)
|
containersList.Containers = append(containersList.Containers, cnt)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -105,25 +106,25 @@ func ListAndProcessResources[T K8sResource, L client.ObjectList](ctx context.Con
|
|||||||
switch typedList := any(list).(type) {
|
switch typedList := any(list).(type) {
|
||||||
case *appsv1.DeploymentList:
|
case *appsv1.DeploymentList:
|
||||||
for i := range typedList.Items {
|
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
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case *batchv1.JobList:
|
case *batchv1.JobList:
|
||||||
for i := range typedList.Items {
|
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
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case *appsv1.DaemonSetList:
|
case *appsv1.DaemonSetList:
|
||||||
for i := range typedList.Items {
|
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
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case *batchv1.CronJobList:
|
case *batchv1.CronJobList:
|
||||||
for i := range typedList.Items {
|
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
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user