diff --git a/docs/gathered-data.md b/docs/gathered-data.md index 532f845a0d..77078d2922 100644 --- a/docs/gathered-data.md +++ b/docs/gathered-data.md @@ -44,6 +44,13 @@ Location in archive: config/id/ See: docs/insights-archive-sample/config/id +## ClusterImagePruner + +fetches the image pruner configuration + +Location in archive: config/imagepruner/ + + ## ClusterImageRegistry fetches the cluster Image Registry configuration diff --git a/manifests/03-clusterrole.yaml b/manifests/03-clusterrole.yaml index a33f5b0de3..8cadad30b0 100644 --- a/manifests/03-clusterrole.yaml +++ b/manifests/03-clusterrole.yaml @@ -99,6 +99,7 @@ rules: - imageregistry.operator.openshift.io resources: - configs + - imagepruners verbs: - get - list diff --git a/pkg/gather/clusterconfig/clusterconfig.go b/pkg/gather/clusterconfig/clusterconfig.go index 13fc4b2283..0f04e1a505 100644 --- a/pkg/gather/clusterconfig/clusterconfig.go +++ b/pkg/gather/clusterconfig/clusterconfig.go @@ -89,6 +89,7 @@ func (i *Gatherer) Gather(ctx context.Context, recorder record.Interface) error GatherClusterNetwork(i), GatherClusterAuthentication(i), GatherClusterImageRegistry(i), + GatherClusterImagePruner(i), GatherClusterFeatureGates(i), GatherClusterOAuth(i), GatherClusterIngress(i), @@ -344,6 +345,22 @@ func GatherClusterAuthentication(i *Gatherer) func() ([]record.Record, []error) } } +// GatherClusterImagePruner fetches the image pruner configuration +// +// Location in archive: config/imagepruner/ +func GatherClusterImagePruner(i *Gatherer) func() ([]record.Record, []error) { + return func() ([]record.Record, []error) { + pruner, err := i.registryClient.ImagePruners().Get("cluster", metav1.GetOptions{}) + if errors.IsNotFound(err) { + return nil, nil + } + if err != nil { + return nil, []error{err} + } + return []record.Record{{Name: "config/imagepruner", Item: ImagePrunerAnonymizer{pruner}}}, nil + } +} + // GatherClusterImageRegistry fetches the cluster Image Registry configuration // // Location in archive: config/imageregistry/ @@ -559,7 +576,17 @@ func (a FeatureGateAnonymizer) Marshal(_ context.Context) ([]byte, error) { return runtime.Encode(openshiftSerializer, a.FeatureGate) } -// IngressAnonymizer implements serialization with marshalling +// ImagePrunerAnonymizer implements serialization with marshalling +type ImagePrunerAnonymizer struct { + *registryv1.ImagePruner +} + +// Marshal serializes ImagePruner with anonymization +func (a ImagePrunerAnonymizer) Marshal(_ context.Context) ([]byte, error) { + return runtime.Encode(registrySerializer.LegacyCodec(registryv1.SchemeGroupVersion), a.ImagePruner) +} + +// ImageRegistryAnonymizer implements serialization with marshalling type ImageRegistryAnonymizer struct { *registryv1.Config } diff --git a/pkg/gather/clusterconfig/clusterconfig_test.go b/pkg/gather/clusterconfig/clusterconfig_test.go index 197f210d1a..21d9bef32a 100644 --- a/pkg/gather/clusterconfig/clusterconfig_test.go +++ b/pkg/gather/clusterconfig/clusterconfig_test.go @@ -11,6 +11,7 @@ import ( imageregistryv1 "github.com/openshift/api/imageregistry/v1" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/klog" imageregistryfake "github.com/openshift/client-go/imageregistry/clientset/versioned/fake" @@ -92,6 +93,79 @@ func TestConfigMapAnonymizer(t *testing.T) { } +func TestGatherClusterPruner(t *testing.T) { + tests := []struct { + name string + inputObj runtime.Object + expectedRecords int + evalOutput func(t *testing.T, obj *imageregistryv1.ImagePruner) + }{ + { + name: "not found", + inputObj: &imageregistryv1.ImagePruner{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pruner-i-dont-care-about", + }, + }, + }, + { + name: "simple image pruner", + expectedRecords: 1, + inputObj: &imageregistryv1.ImagePruner{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster", + }, + Spec: imageregistryv1.ImagePrunerSpec{ + Schedule: "0 0 * * *", + }, + }, + evalOutput: func(t *testing.T, obj *imageregistryv1.ImagePruner) { + if obj.Name != "cluster" { + t.Errorf("received wrong prunner: %+v", obj) + return + } + if obj.Spec.Schedule != "0 0 * * *" { + t.Errorf("unexpected spec.schedule: %q", obj.Spec.Schedule) + } + }, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + client := imageregistryfake.NewSimpleClientset(test.inputObj) + gatherer := &Gatherer{registryClient: client.ImageregistryV1()} + records, errs := GatherClusterImagePruner(gatherer)() + if len(errs) > 0 { + t.Errorf("unexpected errors: %#v", errs) + return + } + if numRecords := len(records); numRecords != test.expectedRecords { + t.Errorf("expected one record, got %d", numRecords) + return + } + if test.expectedRecords == 0 { + return + } + if expectedRecordName := "config/imagepruner"; records[0].Name != expectedRecordName { + t.Errorf("expected %q record name, got %q", expectedRecordName, records[0].Name) + return + } + item := records[0].Item + itemBytes, err := item.Marshal(context.TODO()) + if err != nil { + t.Fatalf("unable to marshal config: %v", err) + } + var output imageregistryv1.ImagePruner + obj, _, err := registrySerializer.LegacyCodec(imageregistryv1.SchemeGroupVersion).Decode(itemBytes, nil, &output) + if err != nil { + t.Fatalf("failed to decode object: %v", err) + } + test.evalOutput(t, obj.(*imageregistryv1.ImagePruner)) + }) + } + +} + func TestGatherClusterImageRegistry(t *testing.T) { tests := []struct { name string