From f80627f228116b32aab08e4b9378293eedbfb784 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roberto=20Jim=C3=A9nez=20S=C3=A1nchez?= Date: Mon, 11 Aug 2025 12:10:15 +0200 Subject: [PATCH] Provisioning: Store Recent Jobs Temporarilly in Unified Storage (#109392) * Add HistoryJob back to Spec * Generate client * Put back History Jobs * Add a controller to remove historic jobs after some minutes * Start only if Loki is not used * Format code * Update OpenAPI spec * Change log level * Fix condition * Fix staticcheck * Use provisioning identity * Fix registration APIs * Fix readonly issue * Update OpenAPI --- .../pkg/apis/provisioning/v0alpha1/jobs.go | 21 + .../apis/provisioning/v0alpha1/register.go | 30 + .../v0alpha1/zz_generated.deepcopy.go | 61 ++ .../v0alpha1/zz_generated.openapi.go | 96 ++ ...enerated.openapi_violation_exceptions.list | 1 + .../provisioning/v0alpha1/historicjob.go | 211 ++++ .../pkg/generated/applyconfiguration/utils.go | 2 + .../v0alpha1/fake/fake_historicjob.go | 39 + .../v0alpha1/fake/fake_provisioning_client.go | 4 + .../v0alpha1/generated_expansion.go | 2 + .../provisioning/v0alpha1/historicjob.go | 60 ++ .../v0alpha1/provisioning_client.go | 5 + .../informers/externalversions/generic.go | 2 + .../provisioning/v0alpha1/historicjob.go | 88 ++ .../provisioning/v0alpha1/interface.go | 7 + .../v0alpha1/expansion_generated.go | 8 + .../provisioning/v0alpha1/historicjob.go | 56 + .../provisioning/controller/historyjob.go | 89 ++ .../apis/provisioning/jobs/history.go | 133 ++- .../apis/provisioning/jobs/persistentstore.go | 2 + pkg/registry/apis/provisioning/register.go | 54 +- .../provisioning.grafana.app-v0alpha1.json | 959 ++++++++++++++++++ 22 files changed, 1874 insertions(+), 56 deletions(-) create mode 100644 apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1/historicjob.go create mode 100644 apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/fake/fake_historicjob.go create mode 100644 apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/historicjob.go create mode 100644 apps/provisioning/pkg/generated/informers/externalversions/provisioning/v0alpha1/historicjob.go create mode 100644 apps/provisioning/pkg/generated/listers/provisioning/v0alpha1/historicjob.go create mode 100644 pkg/registry/apis/provisioning/controller/historyjob.go diff --git a/apps/provisioning/pkg/apis/provisioning/v0alpha1/jobs.go b/apps/provisioning/pkg/apis/provisioning/v0alpha1/jobs.go index e11ab36d147..814f2e56a3d 100644 --- a/apps/provisioning/pkg/apis/provisioning/v0alpha1/jobs.go +++ b/apps/provisioning/pkg/apis/provisioning/v0alpha1/jobs.go @@ -235,3 +235,24 @@ type JobResourceSummary struct { // This may not be an exhaustive list and recommend looking at the logs for more info Errors []string `json:"errors,omitempty"` } + +// HistoricJob is a history entry of Job. It is used to store Jobs that have been processed. +// +// The repository name and type are stored as labels. +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +genclient +type HistoricJob struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec JobSpec `json:"spec,omitempty"` + Status JobStatus `json:"status,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +type HistoricJobList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + + Items []HistoricJob `json:"items,omitempty"` +} diff --git a/apps/provisioning/pkg/apis/provisioning/v0alpha1/register.go b/apps/provisioning/pkg/apis/provisioning/v0alpha1/register.go index c7db279034c..000ff7f22ff 100644 --- a/apps/provisioning/pkg/apis/provisioning/v0alpha1/register.go +++ b/apps/provisioning/pkg/apis/provisioning/v0alpha1/register.go @@ -87,6 +87,34 @@ var JobResourceInfo = utils.NewResourceInfo(GROUP, VERSION, }, }) +var HistoricJobResourceInfo = utils.NewResourceInfo(GROUP, VERSION, + "historicjobs", "historicjob", "HistoricJob", + func() runtime.Object { return &HistoricJob{} }, // newObj + func() runtime.Object { return &HistoricJobList{} }, // newList + utils.TableColumns{ // Returned by `kubectl get`. Doesn't affect disk storage. + Definition: []metav1.TableColumnDefinition{ + {Name: "Name", Type: "string", Format: "name"}, + {Name: "Created At", Type: "date"}, + {Name: "Action", Type: "string"}, + {Name: "State", Type: "string"}, + {Name: "Message", Type: "string"}, + }, + Reader: func(obj any) ([]interface{}, error) { + m, ok := obj.(*HistoricJob) + if !ok { + return nil, errors.New("expected HistoricJob") + } + + return []interface{}{ + m.Name, // may our may not be nice to read + m.CreationTimestamp.UTC().Format(time.RFC3339), + m.Spec.Action, + m.Status.State, + m.Status.Message, + }, nil + }, + }) + var ( // SchemeGroupVersion is group version used to register these objects SchemeGroupVersion = schema.GroupVersion{Group: GROUP, Version: VERSION} @@ -124,6 +152,8 @@ func AddKnownTypes(gv schema.GroupVersion, scheme *runtime.Scheme) error { &Job{}, &JobList{}, &RefList{}, + &HistoricJob{}, + &HistoricJobList{}, ) return nil } diff --git a/apps/provisioning/pkg/apis/provisioning/v0alpha1/zz_generated.deepcopy.go b/apps/provisioning/pkg/apis/provisioning/v0alpha1/zz_generated.deepcopy.go index c5438fccd0e..174505a880f 100644 --- a/apps/provisioning/pkg/apis/provisioning/v0alpha1/zz_generated.deepcopy.go +++ b/apps/provisioning/pkg/apis/provisioning/v0alpha1/zz_generated.deepcopy.go @@ -237,6 +237,67 @@ func (in *HealthStatus) DeepCopy() *HealthStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HistoricJob) DeepCopyInto(out *HistoricJob) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HistoricJob. +func (in *HistoricJob) DeepCopy() *HistoricJob { + if in == nil { + return nil + } + out := new(HistoricJob) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *HistoricJob) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HistoricJobList) DeepCopyInto(out *HistoricJobList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]HistoricJob, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HistoricJobList. +func (in *HistoricJobList) DeepCopy() *HistoricJobList { + if in == nil { + return nil + } + out := new(HistoricJobList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *HistoricJobList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *HistoryItem) DeepCopyInto(out *HistoryItem) { *out = *in diff --git a/apps/provisioning/pkg/apis/provisioning/v0alpha1/zz_generated.openapi.go b/apps/provisioning/pkg/apis/provisioning/v0alpha1/zz_generated.openapi.go index 19911653735..a05e91483a8 100644 --- a/apps/provisioning/pkg/apis/provisioning/v0alpha1/zz_generated.openapi.go +++ b/apps/provisioning/pkg/apis/provisioning/v0alpha1/zz_generated.openapi.go @@ -25,6 +25,8 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.GitLabRepositoryConfig": schema_pkg_apis_provisioning_v0alpha1_GitLabRepositoryConfig(ref), "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.GitRepositoryConfig": schema_pkg_apis_provisioning_v0alpha1_GitRepositoryConfig(ref), "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.HealthStatus": schema_pkg_apis_provisioning_v0alpha1_HealthStatus(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.HistoricJob": schema_pkg_apis_provisioning_v0alpha1_HistoricJob(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.HistoricJobList": schema_pkg_apis_provisioning_v0alpha1_HistoricJobList(ref), "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.HistoryItem": schema_pkg_apis_provisioning_v0alpha1_HistoryItem(ref), "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.HistoryList": schema_pkg_apis_provisioning_v0alpha1_HistoryList(ref), "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.Job": schema_pkg_apis_provisioning_v0alpha1_Job(ref), @@ -602,6 +604,100 @@ func schema_pkg_apis_provisioning_v0alpha1_HealthStatus(ref common.ReferenceCall } } +func schema_pkg_apis_provisioning_v0alpha1_HistoricJob(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "HistoricJob is a history entry of Job. It is used to store Jobs that have been processed.\n\nThe repository name and type are stored as labels.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.JobSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.JobStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.JobSpec", "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.JobStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_pkg_apis_provisioning_v0alpha1_HistoricJobList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.HistoricJob"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.HistoricJob", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + func schema_pkg_apis_provisioning_v0alpha1_HistoryItem(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/apps/provisioning/pkg/apis/provisioning/v0alpha1/zz_generated.openapi_violation_exceptions.list b/apps/provisioning/pkg/apis/provisioning/v0alpha1/zz_generated.openapi_violation_exceptions.list index a92f95837a2..3060c1f664d 100644 --- a/apps/provisioning/pkg/apis/provisioning/v0alpha1/zz_generated.openapi_violation_exceptions.list +++ b/apps/provisioning/pkg/apis/provisioning/v0alpha1/zz_generated.openapi_violation_exceptions.list @@ -24,3 +24,4 @@ API rule violation: names_match,github.com/grafana/grafana/apps/provisioning/pkg API rule violation: names_match,github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1,ResourceWrapper,URLs API rule violation: names_match,github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1,SyncStatus,JobID API rule violation: names_match,github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1,WebhookResponse,Message +API rule violation: streaming_list_type_json_tags,github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1,HistoricJobList,Items diff --git a/apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1/historicjob.go b/apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1/historicjob.go new file mode 100644 index 00000000000..66db5359b54 --- /dev/null +++ b/apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1/historicjob.go @@ -0,0 +1,211 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v0alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + v1 "k8s.io/client-go/applyconfigurations/meta/v1" +) + +// HistoricJobApplyConfiguration represents a declarative configuration of the HistoricJob type for use +// with apply. +type HistoricJobApplyConfiguration struct { + v1.TypeMetaApplyConfiguration `json:",inline"` + *v1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"` + Spec *JobSpecApplyConfiguration `json:"spec,omitempty"` + Status *JobStatusApplyConfiguration `json:"status,omitempty"` +} + +// HistoricJob constructs a declarative configuration of the HistoricJob type for use with +// apply. +func HistoricJob(name, namespace string) *HistoricJobApplyConfiguration { + b := &HistoricJobApplyConfiguration{} + b.WithName(name) + b.WithNamespace(namespace) + b.WithKind("HistoricJob") + b.WithAPIVersion("provisioning.grafana.app/v0alpha1") + return b +} + +// WithKind sets the Kind field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Kind field is set to the value of the last call. +func (b *HistoricJobApplyConfiguration) WithKind(value string) *HistoricJobApplyConfiguration { + b.TypeMetaApplyConfiguration.Kind = &value + return b +} + +// WithAPIVersion sets the APIVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the APIVersion field is set to the value of the last call. +func (b *HistoricJobApplyConfiguration) WithAPIVersion(value string) *HistoricJobApplyConfiguration { + b.TypeMetaApplyConfiguration.APIVersion = &value + return b +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *HistoricJobApplyConfiguration) WithName(value string) *HistoricJobApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Name = &value + return b +} + +// WithGenerateName sets the GenerateName field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the GenerateName field is set to the value of the last call. +func (b *HistoricJobApplyConfiguration) WithGenerateName(value string) *HistoricJobApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.GenerateName = &value + return b +} + +// WithNamespace sets the Namespace field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Namespace field is set to the value of the last call. +func (b *HistoricJobApplyConfiguration) WithNamespace(value string) *HistoricJobApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Namespace = &value + return b +} + +// WithUID sets the UID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the UID field is set to the value of the last call. +func (b *HistoricJobApplyConfiguration) WithUID(value types.UID) *HistoricJobApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.UID = &value + return b +} + +// WithResourceVersion sets the ResourceVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ResourceVersion field is set to the value of the last call. +func (b *HistoricJobApplyConfiguration) WithResourceVersion(value string) *HistoricJobApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.ResourceVersion = &value + return b +} + +// WithGeneration sets the Generation field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Generation field is set to the value of the last call. +func (b *HistoricJobApplyConfiguration) WithGeneration(value int64) *HistoricJobApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Generation = &value + return b +} + +// WithCreationTimestamp sets the CreationTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the CreationTimestamp field is set to the value of the last call. +func (b *HistoricJobApplyConfiguration) WithCreationTimestamp(value metav1.Time) *HistoricJobApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.CreationTimestamp = &value + return b +} + +// WithDeletionTimestamp sets the DeletionTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionTimestamp field is set to the value of the last call. +func (b *HistoricJobApplyConfiguration) WithDeletionTimestamp(value metav1.Time) *HistoricJobApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.DeletionTimestamp = &value + return b +} + +// WithDeletionGracePeriodSeconds sets the DeletionGracePeriodSeconds field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionGracePeriodSeconds field is set to the value of the last call. +func (b *HistoricJobApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *HistoricJobApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.DeletionGracePeriodSeconds = &value + return b +} + +// WithLabels puts the entries into the Labels field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Labels field, +// overwriting an existing map entries in Labels field with the same key. +func (b *HistoricJobApplyConfiguration) WithLabels(entries map[string]string) *HistoricJobApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.ObjectMetaApplyConfiguration.Labels == nil && len(entries) > 0 { + b.ObjectMetaApplyConfiguration.Labels = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.ObjectMetaApplyConfiguration.Labels[k] = v + } + return b +} + +// WithAnnotations puts the entries into the Annotations field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Annotations field, +// overwriting an existing map entries in Annotations field with the same key. +func (b *HistoricJobApplyConfiguration) WithAnnotations(entries map[string]string) *HistoricJobApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.ObjectMetaApplyConfiguration.Annotations == nil && len(entries) > 0 { + b.ObjectMetaApplyConfiguration.Annotations = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.ObjectMetaApplyConfiguration.Annotations[k] = v + } + return b +} + +// WithOwnerReferences adds the given value to the OwnerReferences field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the OwnerReferences field. +func (b *HistoricJobApplyConfiguration) WithOwnerReferences(values ...*v1.OwnerReferenceApplyConfiguration) *HistoricJobApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + if values[i] == nil { + panic("nil value passed to WithOwnerReferences") + } + b.ObjectMetaApplyConfiguration.OwnerReferences = append(b.ObjectMetaApplyConfiguration.OwnerReferences, *values[i]) + } + return b +} + +// WithFinalizers adds the given value to the Finalizers field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Finalizers field. +func (b *HistoricJobApplyConfiguration) WithFinalizers(values ...string) *HistoricJobApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + b.ObjectMetaApplyConfiguration.Finalizers = append(b.ObjectMetaApplyConfiguration.Finalizers, values[i]) + } + return b +} + +func (b *HistoricJobApplyConfiguration) ensureObjectMetaApplyConfigurationExists() { + if b.ObjectMetaApplyConfiguration == nil { + b.ObjectMetaApplyConfiguration = &v1.ObjectMetaApplyConfiguration{} + } +} + +// WithSpec sets the Spec field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Spec field is set to the value of the last call. +func (b *HistoricJobApplyConfiguration) WithSpec(value *JobSpecApplyConfiguration) *HistoricJobApplyConfiguration { + b.Spec = value + return b +} + +// WithStatus sets the Status field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Status field is set to the value of the last call. +func (b *HistoricJobApplyConfiguration) WithStatus(value *JobStatusApplyConfiguration) *HistoricJobApplyConfiguration { + b.Status = value + return b +} + +// GetName retrieves the value of the Name field in the declarative configuration. +func (b *HistoricJobApplyConfiguration) GetName() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Name +} diff --git a/apps/provisioning/pkg/generated/applyconfiguration/utils.go b/apps/provisioning/pkg/generated/applyconfiguration/utils.go index 6fc85d35cda..7c6cb060f78 100644 --- a/apps/provisioning/pkg/generated/applyconfiguration/utils.go +++ b/apps/provisioning/pkg/generated/applyconfiguration/utils.go @@ -32,6 +32,8 @@ func ForKind(kind schema.GroupVersionKind) interface{} { return &provisioningv0alpha1.GitRepositoryConfigApplyConfiguration{} case v0alpha1.SchemeGroupVersion.WithKind("HealthStatus"): return &provisioningv0alpha1.HealthStatusApplyConfiguration{} + case v0alpha1.SchemeGroupVersion.WithKind("HistoricJob"): + return &provisioningv0alpha1.HistoricJobApplyConfiguration{} case v0alpha1.SchemeGroupVersion.WithKind("Job"): return &provisioningv0alpha1.JobApplyConfiguration{} case v0alpha1.SchemeGroupVersion.WithKind("JobResourceSummary"): diff --git a/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/fake/fake_historicjob.go b/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/fake/fake_historicjob.go new file mode 100644 index 00000000000..94611b64ce0 --- /dev/null +++ b/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/fake/fake_historicjob.go @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" + provisioningv0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1" + typedprovisioningv0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1" + gentype "k8s.io/client-go/gentype" +) + +// fakeHistoricJobs implements HistoricJobInterface +type fakeHistoricJobs struct { + *gentype.FakeClientWithListAndApply[*v0alpha1.HistoricJob, *v0alpha1.HistoricJobList, *provisioningv0alpha1.HistoricJobApplyConfiguration] + Fake *FakeProvisioningV0alpha1 +} + +func newFakeHistoricJobs(fake *FakeProvisioningV0alpha1, namespace string) typedprovisioningv0alpha1.HistoricJobInterface { + return &fakeHistoricJobs{ + gentype.NewFakeClientWithListAndApply[*v0alpha1.HistoricJob, *v0alpha1.HistoricJobList, *provisioningv0alpha1.HistoricJobApplyConfiguration]( + fake.Fake, + namespace, + v0alpha1.SchemeGroupVersion.WithResource("historicjobs"), + v0alpha1.SchemeGroupVersion.WithKind("HistoricJob"), + func() *v0alpha1.HistoricJob { return &v0alpha1.HistoricJob{} }, + func() *v0alpha1.HistoricJobList { return &v0alpha1.HistoricJobList{} }, + func(dst, src *v0alpha1.HistoricJobList) { dst.ListMeta = src.ListMeta }, + func(list *v0alpha1.HistoricJobList) []*v0alpha1.HistoricJob { + return gentype.ToPointerSlice(list.Items) + }, + func(list *v0alpha1.HistoricJobList, items []*v0alpha1.HistoricJob) { + list.Items = gentype.FromPointerSlice(items) + }, + ), + fake, + } +} diff --git a/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/fake/fake_provisioning_client.go b/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/fake/fake_provisioning_client.go index 4a6afceb6de..d6fe94156be 100644 --- a/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/fake/fake_provisioning_client.go +++ b/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/fake/fake_provisioning_client.go @@ -14,6 +14,10 @@ type FakeProvisioningV0alpha1 struct { *testing.Fake } +func (c *FakeProvisioningV0alpha1) HistoricJobs(namespace string) v0alpha1.HistoricJobInterface { + return newFakeHistoricJobs(c, namespace) +} + func (c *FakeProvisioningV0alpha1) Jobs(namespace string) v0alpha1.JobInterface { return newFakeJobs(c, namespace) } diff --git a/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/generated_expansion.go b/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/generated_expansion.go index 9916a25266d..21b7a18b414 100644 --- a/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/generated_expansion.go +++ b/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/generated_expansion.go @@ -4,6 +4,8 @@ package v0alpha1 +type HistoricJobExpansion interface{} + type JobExpansion interface{} type RepositoryExpansion interface{} diff --git a/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/historicjob.go b/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/historicjob.go new file mode 100644 index 00000000000..f59daff7e25 --- /dev/null +++ b/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/historicjob.go @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +// Code generated by client-gen. DO NOT EDIT. + +package v0alpha1 + +import ( + context "context" + + provisioningv0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" + applyconfigurationprovisioningv0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1" + scheme "github.com/grafana/grafana/apps/provisioning/pkg/generated/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + gentype "k8s.io/client-go/gentype" +) + +// HistoricJobsGetter has a method to return a HistoricJobInterface. +// A group's client should implement this interface. +type HistoricJobsGetter interface { + HistoricJobs(namespace string) HistoricJobInterface +} + +// HistoricJobInterface has methods to work with HistoricJob resources. +type HistoricJobInterface interface { + Create(ctx context.Context, historicJob *provisioningv0alpha1.HistoricJob, opts v1.CreateOptions) (*provisioningv0alpha1.HistoricJob, error) + Update(ctx context.Context, historicJob *provisioningv0alpha1.HistoricJob, opts v1.UpdateOptions) (*provisioningv0alpha1.HistoricJob, error) + // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + UpdateStatus(ctx context.Context, historicJob *provisioningv0alpha1.HistoricJob, opts v1.UpdateOptions) (*provisioningv0alpha1.HistoricJob, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*provisioningv0alpha1.HistoricJob, error) + List(ctx context.Context, opts v1.ListOptions) (*provisioningv0alpha1.HistoricJobList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *provisioningv0alpha1.HistoricJob, err error) + Apply(ctx context.Context, historicJob *applyconfigurationprovisioningv0alpha1.HistoricJobApplyConfiguration, opts v1.ApplyOptions) (result *provisioningv0alpha1.HistoricJob, err error) + // Add a +genclient:noStatus comment above the type to avoid generating ApplyStatus(). + ApplyStatus(ctx context.Context, historicJob *applyconfigurationprovisioningv0alpha1.HistoricJobApplyConfiguration, opts v1.ApplyOptions) (result *provisioningv0alpha1.HistoricJob, err error) + HistoricJobExpansion +} + +// historicJobs implements HistoricJobInterface +type historicJobs struct { + *gentype.ClientWithListAndApply[*provisioningv0alpha1.HistoricJob, *provisioningv0alpha1.HistoricJobList, *applyconfigurationprovisioningv0alpha1.HistoricJobApplyConfiguration] +} + +// newHistoricJobs returns a HistoricJobs +func newHistoricJobs(c *ProvisioningV0alpha1Client, namespace string) *historicJobs { + return &historicJobs{ + gentype.NewClientWithListAndApply[*provisioningv0alpha1.HistoricJob, *provisioningv0alpha1.HistoricJobList, *applyconfigurationprovisioningv0alpha1.HistoricJobApplyConfiguration]( + "historicjobs", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *provisioningv0alpha1.HistoricJob { return &provisioningv0alpha1.HistoricJob{} }, + func() *provisioningv0alpha1.HistoricJobList { return &provisioningv0alpha1.HistoricJobList{} }, + ), + } +} diff --git a/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/provisioning_client.go b/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/provisioning_client.go index b260183135e..401bc533cfe 100644 --- a/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/provisioning_client.go +++ b/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/provisioning_client.go @@ -14,6 +14,7 @@ import ( type ProvisioningV0alpha1Interface interface { RESTClient() rest.Interface + HistoricJobsGetter JobsGetter RepositoriesGetter } @@ -23,6 +24,10 @@ type ProvisioningV0alpha1Client struct { restClient rest.Interface } +func (c *ProvisioningV0alpha1Client) HistoricJobs(namespace string) HistoricJobInterface { + return newHistoricJobs(c, namespace) +} + func (c *ProvisioningV0alpha1Client) Jobs(namespace string) JobInterface { return newJobs(c, namespace) } diff --git a/apps/provisioning/pkg/generated/informers/externalversions/generic.go b/apps/provisioning/pkg/generated/informers/externalversions/generic.go index cdbcc682e3d..4a62aab044a 100644 --- a/apps/provisioning/pkg/generated/informers/externalversions/generic.go +++ b/apps/provisioning/pkg/generated/informers/externalversions/generic.go @@ -39,6 +39,8 @@ func (f *genericInformer) Lister() cache.GenericLister { func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { switch resource { // Group=provisioning.grafana.app, Version=v0alpha1 + case v0alpha1.SchemeGroupVersion.WithResource("historicjobs"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Provisioning().V0alpha1().HistoricJobs().Informer()}, nil case v0alpha1.SchemeGroupVersion.WithResource("jobs"): return &genericInformer{resource: resource.GroupResource(), informer: f.Provisioning().V0alpha1().Jobs().Informer()}, nil case v0alpha1.SchemeGroupVersion.WithResource("repositories"): diff --git a/apps/provisioning/pkg/generated/informers/externalversions/provisioning/v0alpha1/historicjob.go b/apps/provisioning/pkg/generated/informers/externalversions/provisioning/v0alpha1/historicjob.go new file mode 100644 index 00000000000..fae7e78c2f6 --- /dev/null +++ b/apps/provisioning/pkg/generated/informers/externalversions/provisioning/v0alpha1/historicjob.go @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +// Code generated by informer-gen. DO NOT EDIT. + +package v0alpha1 + +import ( + context "context" + time "time" + + apisprovisioningv0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" + versioned "github.com/grafana/grafana/apps/provisioning/pkg/generated/clientset/versioned" + internalinterfaces "github.com/grafana/grafana/apps/provisioning/pkg/generated/informers/externalversions/internalinterfaces" + provisioningv0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/generated/listers/provisioning/v0alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// HistoricJobInformer provides access to a shared informer and lister for +// HistoricJobs. +type HistoricJobInformer interface { + Informer() cache.SharedIndexInformer + Lister() provisioningv0alpha1.HistoricJobLister +} + +type historicJobInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewHistoricJobInformer constructs a new informer for HistoricJob type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewHistoricJobInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredHistoricJobInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredHistoricJobInformer constructs a new informer for HistoricJob type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredHistoricJobInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.ProvisioningV0alpha1().HistoricJobs(namespace).List(context.Background(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.ProvisioningV0alpha1().HistoricJobs(namespace).Watch(context.Background(), options) + }, + ListWithContextFunc: func(ctx context.Context, options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.ProvisioningV0alpha1().HistoricJobs(namespace).List(ctx, options) + }, + WatchFuncWithContext: func(ctx context.Context, options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.ProvisioningV0alpha1().HistoricJobs(namespace).Watch(ctx, options) + }, + }, + &apisprovisioningv0alpha1.HistoricJob{}, + resyncPeriod, + indexers, + ) +} + +func (f *historicJobInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredHistoricJobInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *historicJobInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&apisprovisioningv0alpha1.HistoricJob{}, f.defaultInformer) +} + +func (f *historicJobInformer) Lister() provisioningv0alpha1.HistoricJobLister { + return provisioningv0alpha1.NewHistoricJobLister(f.Informer().GetIndexer()) +} diff --git a/apps/provisioning/pkg/generated/informers/externalversions/provisioning/v0alpha1/interface.go b/apps/provisioning/pkg/generated/informers/externalversions/provisioning/v0alpha1/interface.go index 0b7c499f035..cbe2fcefaf3 100644 --- a/apps/provisioning/pkg/generated/informers/externalversions/provisioning/v0alpha1/interface.go +++ b/apps/provisioning/pkg/generated/informers/externalversions/provisioning/v0alpha1/interface.go @@ -10,6 +10,8 @@ import ( // Interface provides access to all the informers in this group version. type Interface interface { + // HistoricJobs returns a HistoricJobInformer. + HistoricJobs() HistoricJobInformer // Jobs returns a JobInformer. Jobs() JobInformer // Repositories returns a RepositoryInformer. @@ -27,6 +29,11 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} } +// HistoricJobs returns a HistoricJobInformer. +func (v *version) HistoricJobs() HistoricJobInformer { + return &historicJobInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + // Jobs returns a JobInformer. func (v *version) Jobs() JobInformer { return &jobInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} diff --git a/apps/provisioning/pkg/generated/listers/provisioning/v0alpha1/expansion_generated.go b/apps/provisioning/pkg/generated/listers/provisioning/v0alpha1/expansion_generated.go index 94ecc5121e1..7f0649542b3 100644 --- a/apps/provisioning/pkg/generated/listers/provisioning/v0alpha1/expansion_generated.go +++ b/apps/provisioning/pkg/generated/listers/provisioning/v0alpha1/expansion_generated.go @@ -4,6 +4,14 @@ package v0alpha1 +// HistoricJobListerExpansion allows custom methods to be added to +// HistoricJobLister. +type HistoricJobListerExpansion interface{} + +// HistoricJobNamespaceListerExpansion allows custom methods to be added to +// HistoricJobNamespaceLister. +type HistoricJobNamespaceListerExpansion interface{} + // JobListerExpansion allows custom methods to be added to // JobLister. type JobListerExpansion interface{} diff --git a/apps/provisioning/pkg/generated/listers/provisioning/v0alpha1/historicjob.go b/apps/provisioning/pkg/generated/listers/provisioning/v0alpha1/historicjob.go new file mode 100644 index 00000000000..56cb38c5515 --- /dev/null +++ b/apps/provisioning/pkg/generated/listers/provisioning/v0alpha1/historicjob.go @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +// Code generated by lister-gen. DO NOT EDIT. + +package v0alpha1 + +import ( + provisioningv0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" + labels "k8s.io/apimachinery/pkg/labels" + listers "k8s.io/client-go/listers" + cache "k8s.io/client-go/tools/cache" +) + +// HistoricJobLister helps list HistoricJobs. +// All objects returned here must be treated as read-only. +type HistoricJobLister interface { + // List lists all HistoricJobs in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*provisioningv0alpha1.HistoricJob, err error) + // HistoricJobs returns an object that can list and get HistoricJobs. + HistoricJobs(namespace string) HistoricJobNamespaceLister + HistoricJobListerExpansion +} + +// historicJobLister implements the HistoricJobLister interface. +type historicJobLister struct { + listers.ResourceIndexer[*provisioningv0alpha1.HistoricJob] +} + +// NewHistoricJobLister returns a new HistoricJobLister. +func NewHistoricJobLister(indexer cache.Indexer) HistoricJobLister { + return &historicJobLister{listers.New[*provisioningv0alpha1.HistoricJob](indexer, provisioningv0alpha1.Resource("historicjob"))} +} + +// HistoricJobs returns an object that can list and get HistoricJobs. +func (s *historicJobLister) HistoricJobs(namespace string) HistoricJobNamespaceLister { + return historicJobNamespaceLister{listers.NewNamespaced[*provisioningv0alpha1.HistoricJob](s.ResourceIndexer, namespace)} +} + +// HistoricJobNamespaceLister helps list and get HistoricJobs. +// All objects returned here must be treated as read-only. +type HistoricJobNamespaceLister interface { + // List lists all HistoricJobs in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*provisioningv0alpha1.HistoricJob, err error) + // Get retrieves the HistoricJob from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*provisioningv0alpha1.HistoricJob, error) + HistoricJobNamespaceListerExpansion +} + +// historicJobNamespaceLister implements the HistoricJobNamespaceLister +// interface. +type historicJobNamespaceLister struct { + listers.ResourceIndexer[*provisioningv0alpha1.HistoricJob] +} diff --git a/pkg/registry/apis/provisioning/controller/historyjob.go b/pkg/registry/apis/provisioning/controller/historyjob.go new file mode 100644 index 00000000000..2e4e13f359d --- /dev/null +++ b/pkg/registry/apis/provisioning/controller/historyjob.go @@ -0,0 +1,89 @@ +package controller + +import ( + "context" + "time" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apiserver/pkg/endpoints/request" + "k8s.io/client-go/tools/cache" + + "github.com/grafana/grafana-app-sdk/logging" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" + client "github.com/grafana/grafana/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1" + informer "github.com/grafana/grafana/apps/provisioning/pkg/generated/informers/externalversions/provisioning/v0alpha1" + "github.com/grafana/grafana/pkg/apimachinery/identity" +) + +const ( + historyJobControllerLoggerName = "provisioning-historyjob-controller" +) + +// HistoryJobController manages the cleanup of old HistoryJob entries. +type HistoryJobController struct { + client client.ProvisioningV0alpha1Interface + logger logging.Logger + expirationTime time.Duration +} + +// NewHistoryJobController creates a new HistoryJobController. +func NewHistoryJobController( + provisioningClient client.ProvisioningV0alpha1Interface, + historyJobInformer informer.HistoricJobInformer, + expirationTime time.Duration, +) (*HistoryJobController, error) { + c := &HistoryJobController{ + client: provisioningClient, + logger: logging.DefaultLogger.With("logger", historyJobControllerLoggerName), + expirationTime: expirationTime, + } + + // Use the resync events from the shared informer to trigger cleanup for each job + _, err := historyJobInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + c.cleanupJob(obj) + }, + UpdateFunc: func(oldObj, newObj interface{}) { + c.cleanupJob(newObj) + }, + }) + if err != nil { + return nil, err + } + + return c, nil +} + +func (c *HistoryJobController) cleanupJob(obj interface{}) { + job, ok := obj.(*provisioning.HistoricJob) + if !ok { + c.logger.Error("Expected HistoricJob but got", "type", obj) + return + } + + age := time.Since(job.CreationTimestamp.Time) + if age > c.expirationTime { + namespace := job.Namespace + ctx, _, err := identity.WithProvisioningIdentity(context.Background(), namespace) + if err != nil { + c.logger.Error("Failed to set provisioning identity for cleanup", "error", err) + return + } + + ctx = request.WithNamespace(ctx, namespace) + err = c.client.HistoricJobs(job.Namespace).Delete(ctx, job.Name, metav1.DeleteOptions{}) + if err != nil && !apierrors.IsNotFound(err) { + c.logger.Error("Failed to delete expired HistoryJob", + "namespace", job.Namespace, + "name", job.Name, + "age", age, + "error", err) + } else { + c.logger.Info("Deleted expired HistoryJob", + "namespace", job.Namespace, + "name", job.Name, + "age", age) + } + } +} diff --git a/pkg/registry/apis/provisioning/jobs/history.go b/pkg/registry/apis/provisioning/jobs/history.go index 0a7897319f9..95fc4b73b3d 100644 --- a/pkg/registry/apis/provisioning/jobs/history.go +++ b/pkg/registry/apis/provisioning/jobs/history.go @@ -3,9 +3,13 @@ package jobs import ( "context" "fmt" - "sync" apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/apis/meta/internalversion" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apiserver/pkg/endpoints/request" + "k8s.io/apiserver/pkg/registry/rest" provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" ) @@ -24,63 +28,100 @@ type History interface { GetJob(ctx context.Context, namespace, repo, uid string) (*provisioning.Job, error) } -// NewJobHistoryCache creates a History client -func NewJobHistoryCache() History { - history := &recentHistory{ - maxJobs: 9, // 10-1 - repoHistory: make(map[string]provisioning.JobList), +// NewStorageBackedHistory creates a History client backed by unified storage +// This should be replaced by loki when running in cloud +func NewStorageBackedHistory(store rest.Storage) (History, error) { + var ok bool + history := &storageBackedHistory{} + history.creator, ok = store.(rest.Creater) + if !ok { + return nil, fmt.Errorf("storage does not implement rest.Creater") } - return history + + history.lister, ok = store.(rest.Lister) + if !ok { + return nil, fmt.Errorf("storage does not implement rest.Lister") + } + + return history, nil } -type recentHistory struct { - maxJobs int - repoMu sync.Mutex - repoHistory map[string]provisioning.JobList +type storageBackedHistory struct { + creator rest.Creater + lister rest.Lister } // Write implements History. -func (h *recentHistory) WriteJob(ctx context.Context, job *provisioning.Job) error { - h.repoMu.Lock() - defer h.repoMu.Unlock() - - copy := job.DeepCopy() - delete(copy.Labels, LabelJobClaim) - - items := []provisioning.Job{*copy} - key := fmt.Sprintf("%s/%s", job.Namespace, job.Spec.Repository) - v, ok := h.repoHistory[key] - if ok { - max := min(len(v.Items), h.maxJobs) - items = append(items, v.Items[0:max]...) +func (s *storageBackedHistory) WriteJob(ctx context.Context, job *provisioning.Job) error { + if job.UID == "" { + return fmt.Errorf("missing UID in job '%s'", job.GetName()) } - h.repoHistory[key] = provisioning.JobList{Items: items} - return nil + if job.Labels == nil { + job.Labels = make(map[string]string) + } + job.Labels[LabelRepository] = job.Spec.Repository + job.Labels[LabelJobOriginalUID] = string(job.UID) + + // Generate a new name based on the input job + job.GenerateName = job.Name + "-" + job.Name = "" + // We also reset the UID as this is not the same object. + job.UID = "" + // We aren't allowed to write with ResourceVersion set. + job.ResourceVersion = "" + + _, err := s.creator.Create(ctx, &provisioning.HistoricJob{ + ObjectMeta: job.ObjectMeta, + Spec: job.Spec, + Status: job.Status, + }, nil, &metav1.CreateOptions{}) + return err } -// Recent implements History. -func (h *recentHistory) RecentJobs(ctx context.Context, namespace, repo string) (*provisioning.JobList, error) { - h.repoMu.Lock() - defer h.repoMu.Unlock() - - rsp := &provisioning.JobList{} - key := fmt.Sprintf("%s/%s", namespace, repo) - v, ok := h.repoHistory[key] - if ok { - rsp.Items = v.Items - } - return rsp, nil -} - -func (h *recentHistory) GetJob(ctx context.Context, namespace, repo, job string) (*provisioning.Job, error) { - jobs, err := h.RecentJobs(ctx, namespace, repo) +func (s *storageBackedHistory) getJobs(ctx context.Context, namespace string, labels labels.Set) (*provisioning.JobList, error) { + ctx = request.WithNamespace(ctx, namespace) + obj, err := s.lister.List(ctx, &internalversion.ListOptions{ + LabelSelector: labels.AsSelector(), + }) if err != nil { return nil, err } - for _, item := range jobs.Items { - if string(item.UID) == job { - return &item, nil - } + + historic, ok := obj.(*provisioning.HistoricJobList) + if !ok { + return nil, fmt.Errorf("expected HistoricJobList, found %T", historic) + } + + jobs := &provisioning.JobList{ + ListMeta: historic.ListMeta, + } + for _, job := range historic.Items { + jobs.Items = append(jobs.Items, provisioning.Job{ + ObjectMeta: job.ObjectMeta, + Spec: job.Spec, + Status: job.Status, + }) + } + return jobs, nil +} + +// Recent implements History. +func (s *storageBackedHistory) RecentJobs(ctx context.Context, namespace, repo string) (*provisioning.JobList, error) { + return s.getJobs(ctx, namespace, labels.Set{ + LabelRepository: repo, + }) +} + +// GetJob implements History. +func (s *storageBackedHistory) GetJob(ctx context.Context, namespace, repo, job string) (*provisioning.Job, error) { + jobs, err := s.getJobs(ctx, namespace, labels.Set{ + LabelJobOriginalUID: job, + }) + if err != nil { + return nil, err + } + if len(jobs.Items) == 1 { + return &jobs.Items[0], nil } return nil, apierrors.NewNotFound(provisioning.JobResourceInfo.GroupResource(), job) } diff --git a/pkg/registry/apis/provisioning/jobs/persistentstore.go b/pkg/registry/apis/provisioning/jobs/persistentstore.go index b25fdff68e2..2649337b76f 100644 --- a/pkg/registry/apis/provisioning/jobs/persistentstore.go +++ b/pkg/registry/apis/provisioning/jobs/persistentstore.go @@ -30,6 +30,8 @@ const ( LabelJobClaim = "provisioning.grafana.app/claim" // LabelRepository contains the repository name as a label. This allows for label selectors to find the archived version of a job. LabelRepository = "provisioning.grafana.app/repository" + // LabelJobOriginalUID contains the Job's original uid as a label. This allows for label selectors to find the archived version of a job. + LabelJobOriginalUID = "provisioning.grafana.app/original-uid" ) var ( diff --git a/pkg/registry/apis/provisioning/register.go b/pkg/registry/apis/provisioning/register.go index ee8ad2dd02e..b741d55cdd2 100644 --- a/pkg/registry/apis/provisioning/register.go +++ b/pkg/registry/apis/provisioning/register.go @@ -99,6 +99,7 @@ type APIBuilder struct { jobs.Queue jobs.Store } + jobHistoryConfig *JobHistoryConfig jobHistory jobs.History tester *RepositoryTester resourceLister resources.ResourceLister @@ -143,12 +144,6 @@ func NewAPIBuilder( git.Mutator(repositorySecrets), github.Mutator(repositorySecrets), } - // Create job history based on configuration - // Default to in-memory cache if no config provided - jobHistory := jobs.NewJobHistoryCache() - if jobHistoryConfig != nil && jobHistoryConfig.Loki != nil { - jobHistory = jobs.NewLokiJobHistory(*jobHistoryConfig.Loki) - } b := &APIBuilder{ mutators: mutators, @@ -167,7 +162,7 @@ func NewAPIBuilder( unified: unified, repositorySecrets: repositorySecrets, access: access, - jobHistory: jobHistory, + jobHistoryConfig: jobHistoryConfig, availableRepositoryTypes: map[provisioning.RepositoryType]bool{ provisioning.LocalRepositoryType: true, provisioning.GitHubRepositoryType: true, @@ -351,7 +346,8 @@ func (b *APIBuilder) GetAuthorizer() authorizer.Authorizer { } return authorizer.DecisionDeny, "viewer role is required", nil - case provisioning.JobResourceInfo.GetName(): + case provisioning.JobResourceInfo.GetName(), + provisioning.HistoricJobResourceInfo.GetName(): // Jobs are shown on the configuration page. if id.GetOrgRole().Includes(identity.RoleAdmin) { return authorizer.DecisionAllow, "", nil @@ -418,13 +414,32 @@ func (b *APIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver.APIGroupI return fmt.Errorf("failed to create job storage: %w", err) } + storage := map[string]rest.Storage{} + // Create job history based on configuration + // Default to in-memory cache if no config provided + var jobHistory jobs.History + if b.jobHistoryConfig != nil && b.jobHistoryConfig.Loki != nil { + jobHistory = jobs.NewLokiJobHistory(*b.jobHistoryConfig.Loki) + } else { + historicJobStore, err := grafanaregistry.NewCompleteRegistryStore(opts.Scheme, provisioning.HistoricJobResourceInfo, opts.OptsGetter) + if err != nil { + return fmt.Errorf("failed to create historic job storage: %w", err) + } + + jobHistory, err = jobs.NewStorageBackedHistory(historicJobStore) + if err != nil { + return fmt.Errorf("failed to create historic job wrapper: %w", err) + } + + storage[provisioning.HistoricJobResourceInfo.StoragePath()] = historicJobStore + } + + b.jobHistory = jobHistory b.jobs, err = jobs.NewJobStore(realJobStore, 30*time.Second) // FIXME: this timeout if err != nil { return fmt.Errorf("failed to create job store: %w", err) } - storage := map[string]rest.Storage{} - // Although we never interact with jobs via the API, we want them to be readable (watchable!) from the API. storage[provisioning.JobResourceInfo.StoragePath()] = readonly.Wrap(realJobStore) @@ -725,6 +740,25 @@ func (b *APIBuilder) GetPostStartHooks() (map[string]genericapiserver.PostStartH } go repoController.Run(postStartHookCtx.Context, repoControllerWorkers) + + // If Loki not used, start the controller for history jobs + if b.jobHistoryConfig == nil || b.jobHistoryConfig.Loki == nil { + // Create HistoryJobController for cleanup of old job history entries + // Separate informer factory for HistoryJob cleanup with resync interval + historyJobExpiration := 30 * time.Second + historyJobInformerFactory := informers.NewSharedInformerFactory(c, historyJobExpiration) + historyJobInformer := historyJobInformerFactory.Provisioning().V0alpha1().HistoricJobs() + go historyJobInformer.Informer().Run(postStartHookCtx.Done()) + _, err = controller.NewHistoryJobController( + b.GetClient(), + historyJobInformer, + historyJobExpiration, + ) + if err != nil { + return fmt.Errorf("create history job controller: %w", err) + } + } + return nil }, } diff --git a/pkg/tests/apis/openapi_snapshots/provisioning.grafana.app-v0alpha1.json b/pkg/tests/apis/openapi_snapshots/provisioning.grafana.app-v0alpha1.json index ab1e77d03b9..bee7f437b4d 100644 --- a/pkg/tests/apis/openapi_snapshots/provisioning.grafana.app-v0alpha1.json +++ b/pkg/tests/apis/openapi_snapshots/provisioning.grafana.app-v0alpha1.json @@ -36,6 +36,836 @@ } } }, + "/apis/provisioning.grafana.app/v0alpha1/namespaces/{namespace}/historicjobs": { + "get": { + "tags": [ + "HistoricJob" + ], + "description": "list or watch objects of kind HistoricJob", + "operationId": "listHistoricJob", + "parameters": [ + { + "name": "allowWatchBookmarks", + "in": "query", + "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored.", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "name": "continue", + "in": "query", + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "fieldSelector", + "in": "query", + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "labelSelector", + "in": "query", + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "limit", + "in": "query", + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "name": "resourceVersion", + "in": "query", + "description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "resourceVersionMatch", + "in": "query", + "description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "sendInitialEvents", + "in": "query", + "description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "name": "timeoutSeconds", + "in": "query", + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "name": "watch", + "in": "query", + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "schema": { + "type": "boolean", + "uniqueItems": true + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJobList" + } + }, + "application/json;stream=watch": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJobList" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJobList" + } + }, + "application/vnd.kubernetes.protobuf;stream=watch": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJobList" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJobList" + } + } + } + } + }, + "x-kubernetes-action": "list", + "x-kubernetes-group-version-kind": { + "group": "provisioning.grafana.app", + "version": "v0alpha1", + "kind": "HistoricJob" + } + }, + "post": { + "tags": [ + "HistoricJob" + ], + "description": "create a HistoricJob", + "operationId": "createHistoricJob", + "parameters": [ + { + "name": "dryRun", + "in": "query", + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "fieldManager", + "in": "query", + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "fieldValidation", + "in": "query", + "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJob" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJob" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJob" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJob" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJob" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJob" + } + } + } + }, + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJob" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJob" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJob" + } + } + } + }, + "202": { + "description": "Accepted", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJob" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJob" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJob" + } + } + } + } + }, + "x-kubernetes-action": "post", + "x-kubernetes-group-version-kind": { + "group": "provisioning.grafana.app", + "version": "v0alpha1", + "kind": "HistoricJob" + } + }, + "delete": { + "tags": [ + "HistoricJob" + ], + "description": "delete collection of HistoricJob", + "operationId": "deletecollectionHistoricJob", + "parameters": [ + { + "name": "continue", + "in": "query", + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "dryRun", + "in": "query", + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "fieldSelector", + "in": "query", + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "gracePeriodSeconds", + "in": "query", + "description": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "name": "ignoreStoreReadErrorWithClusterBreakingPotential", + "in": "query", + "description": "if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "name": "labelSelector", + "in": "query", + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "limit", + "in": "query", + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "name": "orphanDependents", + "in": "query", + "description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "name": "propagationPolicy", + "in": "query", + "description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground.", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "resourceVersion", + "in": "query", + "description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "resourceVersionMatch", + "in": "query", + "description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "sendInitialEvents", + "in": "query", + "description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "name": "timeoutSeconds", + "in": "query", + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "schema": { + "type": "integer", + "uniqueItems": true + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + } + } + } + }, + "x-kubernetes-action": "deletecollection", + "x-kubernetes-group-version-kind": { + "group": "provisioning.grafana.app", + "version": "v0alpha1", + "kind": "HistoricJob" + } + }, + "parameters": [ + { + "name": "namespace", + "in": "path", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "pretty", + "in": "query", + "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ] + }, + "/apis/provisioning.grafana.app/v0alpha1/namespaces/{namespace}/historicjobs/{name}": { + "get": { + "tags": [ + "HistoricJob" + ], + "description": "read the specified HistoricJob", + "operationId": "getHistoricJob", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJob" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJob" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJob" + } + } + } + } + }, + "x-kubernetes-action": "get", + "x-kubernetes-group-version-kind": { + "group": "provisioning.grafana.app", + "version": "v0alpha1", + "kind": "HistoricJob" + } + }, + "put": { + "tags": [ + "HistoricJob" + ], + "description": "replace the specified HistoricJob", + "operationId": "replaceHistoricJob", + "parameters": [ + { + "name": "dryRun", + "in": "query", + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "fieldManager", + "in": "query", + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "fieldValidation", + "in": "query", + "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJob" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJob" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJob" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJob" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJob" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJob" + } + } + } + }, + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJob" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJob" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJob" + } + } + } + } + }, + "x-kubernetes-action": "put", + "x-kubernetes-group-version-kind": { + "group": "provisioning.grafana.app", + "version": "v0alpha1", + "kind": "HistoricJob" + } + }, + "delete": { + "tags": [ + "HistoricJob" + ], + "description": "delete a HistoricJob", + "operationId": "deleteHistoricJob", + "parameters": [ + { + "name": "dryRun", + "in": "query", + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "gracePeriodSeconds", + "in": "query", + "description": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "name": "ignoreStoreReadErrorWithClusterBreakingPotential", + "in": "query", + "description": "if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "name": "orphanDependents", + "in": "query", + "description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "name": "propagationPolicy", + "in": "query", + "description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground.", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + } + } + }, + "202": { + "description": "Accepted", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + } + } + } + }, + "x-kubernetes-action": "delete", + "x-kubernetes-group-version-kind": { + "group": "provisioning.grafana.app", + "version": "v0alpha1", + "kind": "HistoricJob" + } + }, + "patch": { + "tags": [ + "HistoricJob" + ], + "description": "partially update the specified HistoricJob", + "operationId": "updateHistoricJob", + "parameters": [ + { + "name": "dryRun", + "in": "query", + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "fieldManager", + "in": "query", + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch).", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "fieldValidation", + "in": "query", + "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "force", + "in": "query", + "description": "Force is going to \"force\" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests.", + "schema": { + "type": "boolean", + "uniqueItems": true + } + } + ], + "requestBody": { + "content": { + "application/apply-patch+yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + "application/merge-patch+json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + "application/strategic-merge-patch+json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJob" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJob" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJob" + } + } + } + }, + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJob" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJob" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJob" + } + } + } + } + }, + "x-kubernetes-action": "patch", + "x-kubernetes-group-version-kind": { + "group": "provisioning.grafana.app", + "version": "v0alpha1", + "kind": "HistoricJob" + } + }, + "parameters": [ + { + "name": "name", + "in": "path", + "description": "name of the HistoricJob", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "namespace", + "in": "path", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "pretty", + "in": "query", + "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ] + }, "/apis/provisioning.grafana.app/v0alpha1/namespaces/{namespace}/jobs": { "get": { "tags": [ @@ -2802,6 +3632,90 @@ } } }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJob": { + "description": "HistoricJob is a history entry of Job. It is used to store Jobs that have been processed.\n\nThe repository name and type are stored as labels.", + "type": "object", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + } + ] + }, + "spec": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.JobSpec" + } + ] + }, + "status": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.JobStatus" + } + ] + } + }, + "x-kubernetes-group-version-kind": [ + { + "group": "provisioning.grafana.app", + "kind": "HistoricJob", + "version": "v0alpha1" + } + ] + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJobList": { + "type": "object", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "type": "array", + "items": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HistoricJob" + } + ] + } + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" + } + ] + } + }, + "x-kubernetes-group-version-kind": [ + { + "group": "provisioning.grafana.app", + "kind": "HistoricJobList", + "version": "v0alpha1" + } + ] + }, "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Job": { "description": "The repository name and type are stored as labels", "type": "object", @@ -4301,6 +5215,51 @@ } } }, + "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.HistoricJob": { + "description": "HistoricJob is a history entry of Job. It is used to store Jobs that have been processed.\n\nThe repository name and type are stored as labels.", + "type": "object", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {} + }, + "spec": { + "default": {} + }, + "status": { + "default": {} + } + } + }, + "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.HistoricJobList": { + "type": "object", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "type": "array", + "items": { + "default": {} + } + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {} + } + } + }, "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.HistoryItem": { "type": "object", "required": [