From fc3b82569a63ff59af9037fedb7ba3b7b04587bb Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Mon, 10 Oct 2022 14:12:34 +0300 Subject: [PATCH 001/316] added vector agregator statefuulset --- api/v1alpha1/vector_types.go | 13 +- .../observability.kaasops.io_vectors.yaml | 12 ++ .../observability_v1alpha1_vector.yaml | 9 +- .../observability_v1alpha1_vector_agent.yaml | 8 -- controllers/utils.go | 29 ++++ controllers/vector_agent_controller.go | 4 +- controllers/vector_agent_daemonset.go | 4 +- controllers/vector_agent_secret.go | 19 +-- controllers/vector_aggregator_controller.go | 131 ++++++++++++++++++ controllers/vector_aggregator_rbac.go | 17 +++ controllers/vector_aggregator_secret.go | 40 ++++++ controllers/vector_aggregator_service.go | 28 ++++ controllers/vector_aggregator_statefulset.go | 119 ++++++++++++++++ controllers/vector_controller.go | 4 + go.mod | 1 - go.sum | 1 - 16 files changed, 407 insertions(+), 32 deletions(-) delete mode 100644 config/samples/observability_v1alpha1_vector_agent.yaml create mode 100644 controllers/vector_aggregator_controller.go create mode 100644 controllers/vector_aggregator_rbac.go create mode 100644 controllers/vector_aggregator_secret.go create mode 100644 controllers/vector_aggregator_service.go create mode 100644 controllers/vector_aggregator_statefulset.go diff --git a/api/v1alpha1/vector_types.go b/api/v1alpha1/vector_types.go index 650065dc..e232e7a3 100644 --- a/api/v1alpha1/vector_types.go +++ b/api/v1alpha1/vector_types.go @@ -29,6 +29,8 @@ type VectorSpec struct { DisableAggregation bool `json:"disableAggregation,omitempty"` // Vector Agent Agent *VectorAgent `json:"agent,omitempty"` + // Vector Aggregator + Aggregator *VectorAggregator `json:"aggregator,omitempty"` } // VectorStatus defines the observed state of Vector @@ -39,7 +41,16 @@ type VectorStatus struct { // VectorAgent is the Schema for the Vector Agent type VectorAgent struct { - Service bool `json:"service,omitempty"` + // +kubebuilder:default:="timberio/vector:0.24.0-distroless-libc" + Image string `json:"image,omitempty"` + Service bool `json:"service,omitempty"` +} + +// VectorAggregator is the Schema for the Vector Aggregator +type VectorAggregator struct { + // +kubebuilder:default:="timberio/vector:0.24.0-distroless-libc" + Image string `json:"image,omitempty"` + Replicas int `json:"replicas,omitempty"` } //+kubebuilder:object:root=true diff --git a/config/crd/bases/observability.kaasops.io_vectors.yaml b/config/crd/bases/observability.kaasops.io_vectors.yaml index bfff347a..d2caad74 100644 --- a/config/crd/bases/observability.kaasops.io_vectors.yaml +++ b/config/crd/bases/observability.kaasops.io_vectors.yaml @@ -38,9 +38,21 @@ spec: agent: description: Vector Agent properties: + image: + default: timberio/vector:0.24.0-distroless-libc + type: string service: type: boolean type: object + aggregator: + description: Vector Aggregator + properties: + image: + default: timberio/vector:0.24.0-distroless-libc + type: string + replicas: + type: integer + type: object disableAggregation: description: DisableAggregation type: boolean diff --git a/config/samples/observability_v1alpha1_vector.yaml b/config/samples/observability_v1alpha1_vector.yaml index 6016ca4c..6aef4c96 100644 --- a/config/samples/observability_v1alpha1_vector.yaml +++ b/config/samples/observability_v1alpha1_vector.yaml @@ -2,5 +2,12 @@ apiVersion: observability.kaasops.io/v1alpha1 kind: Vector metadata: name: vector-sample + namespace: vector spec: - # TODO(user): Add fields here + disableAggregation: true + agent: + service: true + image: "timberio/vector:0.24.0-distroless-libc" + aggregator: + image: "timberio/vector:0.24.0-distroless-libc" + replicas: 1 diff --git a/config/samples/observability_v1alpha1_vector_agent.yaml b/config/samples/observability_v1alpha1_vector_agent.yaml deleted file mode 100644 index 2fe16a23..00000000 --- a/config/samples/observability_v1alpha1_vector_agent.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: observability.kaasops.io/v1alpha1 -kind: Vector -metadata: - name: vector-sample -spec: - disableAggregation: true - agent: - service: true diff --git a/controllers/utils.go b/controllers/utils.go index aa63261c..3e611d94 100644 --- a/controllers/utils.go +++ b/controllers/utils.go @@ -25,6 +25,10 @@ func (r *VectorReconciler) CreateOrUpdateDaemonSet(daemonSet *appsv1.DaemonSet) return r.reconcileDaemonSet(daemonSet) } +func (r *VectorReconciler) CreateOrUpdateStatefulSet(statefulSet *appsv1.StatefulSet) (*reconcile.Result, error) { + return r.reconcileStatefulSet(statefulSet) +} + func (r *VectorReconciler) CreateOrUpdateServiceAccount(secret *corev1.ServiceAccount) (*reconcile.Result, error) { return r.reconcileServiceAccount(secret) } @@ -112,6 +116,31 @@ func (r *VectorReconciler) reconcileDaemonSet(obj runtime.Object) (*reconcile.Re return nil, nil } +func (r *VectorReconciler) reconcileStatefulSet(obj runtime.Object) (*reconcile.Result, error) { + + existing := &appsv1.StatefulSet{} + desired := obj.(*appsv1.StatefulSet) + + err := r.Create(context.TODO(), desired) + if err != nil && errors.IsAlreadyExists(err) { + err := r.Get(context.TODO(), client.ObjectKeyFromObject(desired), existing) + if err != nil { + return nil, err + } + if !equality.Semantic.DeepEqual(existing, desired) { + existing.Spec = desired.Spec + existing.Labels = desired.Labels + err := r.Update(context.TODO(), existing) + return nil, err + } + } + if err != nil && !errors.IsAlreadyExists(err) { + return nil, err + } + + return nil, nil +} + func (r *VectorReconciler) reconcileServiceAccount(obj runtime.Object) (*reconcile.Result, error) { existing := &corev1.ServiceAccount{} diff --git a/controllers/vector_agent_controller.go b/controllers/vector_agent_controller.go index e03f3674..396f707d 100644 --- a/controllers/vector_agent_controller.go +++ b/controllers/vector_agent_controller.go @@ -17,7 +17,7 @@ func (r *VectorReconciler) ensureVectorAgent(vectorCR *vectorv1alpha1.Vector) (d log.Info("start Reconcile Vector Agent") - if done, result, err = r.ensureVectorRBAC(vectorCR); done { + if done, result, err = r.ensureVectorAgentRBAC(vectorCR); done { return } @@ -38,7 +38,7 @@ func (r *VectorReconciler) ensureVectorAgent(vectorCR *vectorv1alpha1.Vector) (d return } -func (r *VectorReconciler) ensureVectorRBAC(vectorCR *vectorv1alpha1.Vector) (bool, ctrl.Result, error) { +func (r *VectorReconciler) ensureVectorAgentRBAC(vectorCR *vectorv1alpha1.Vector) (bool, ctrl.Result, error) { ctx := context.Background() log := log.FromContext(ctx).WithValues("vector-agent-rbac", vectorCR.Name) diff --git a/controllers/vector_agent_daemonset.go b/controllers/vector_agent_daemonset.go index 82b021af..9a83ec75 100644 --- a/controllers/vector_agent_daemonset.go +++ b/controllers/vector_agent_daemonset.go @@ -8,8 +8,6 @@ import ( vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" ) -var vectorImage = "timberio/vector:0.24.0-distroless-libc" - func (r *VectorReconciler) createVectorAgentDaemonSet(v *vectorv1alpha1.Vector) *appsv1.DaemonSet { labels := labelsForVectorAgent(v.Name) @@ -26,7 +24,7 @@ func (r *VectorReconciler) createVectorAgentDaemonSet(v *vectorv1alpha1.Vector) Containers: []corev1.Container{ { Name: getNameVectorAgent(v), - Image: vectorImage, + Image: v.Spec.Agent.Image, Args: []string{"--config-dir", "/etc/vector/"}, Env: generateVectorAgentEnvs(v), Ports: []corev1.ContainerPort{ diff --git a/controllers/vector_agent_secret.go b/controllers/vector_agent_secret.go index 4d398083..f4657efb 100644 --- a/controllers/vector_agent_secret.go +++ b/controllers/vector_agent_secret.go @@ -15,27 +15,16 @@ api: sources: kubernetes_logs: type: kubernetes_logs - host_metrics: - filesystem: - devices: - excludes: [binfmt_misc] - filesystems: - excludes: [binfmt_misc] - mountPoints: - excludes: ["*/proc/sys/fs/binfmt_misc"] - type: host_metrics - internal_metrics: - type: internal_metrics sinks: - prom_exporter: - type: prometheus_exporter - inputs: [host_metrics, internal_metrics] - address: 0.0.0.0:9090 stdout: type: console inputs: [kubernetes_logs] encoding: codec: json + vector: + type: vector + inputs: [kubernetes_logs] + address: vector-sample-aggregator:6000 ` func (r *VectorReconciler) createVectorAgentSecret(v *vectorv1alpha1.Vector) *corev1.Secret { diff --git a/controllers/vector_aggregator_controller.go b/controllers/vector_aggregator_controller.go new file mode 100644 index 00000000..8c3ba51a --- /dev/null +++ b/controllers/vector_aggregator_controller.go @@ -0,0 +1,131 @@ +package controllers + +import ( + "context" + + vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" + "github.com/kaasops/vector-operator/controllers/label" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/log" +) + +func (r *VectorReconciler) ensureVectorAggregator(vectorCR *vectorv1alpha1.Vector) (done bool, result ctrl.Result, err error) { + ctx := context.Background() + log := log.FromContext(ctx).WithValues("vector-aggregator", vectorCR.Name) + + log.Info("start Reconcile Vector Aggregator") + + if done, result, err = r.ensureVectorRBAC(vectorCR); done { + return + } + + if done, result, err = r.ensureVectorAggregatorService(vectorCR); done { + return + } + + if done, result, err = r.ensureVectorAggregatorSecret(vectorCR); done { + return + } + + if done, result, err = r.ensureVectorAggregatorStatefulSet(vectorCR); done { + return + } + + return +} + +func (r *VectorReconciler) ensureVectorRBAC(vectorCR *vectorv1alpha1.Vector) (bool, ctrl.Result, error) { + ctx := context.Background() + log := log.FromContext(ctx).WithValues("vector-aggregator-rbac", vectorCR.Name) + + log.Info("start Reconcile Vector Aggregator RBAC") + + if done, _, err := r.ensureVectorAggregatorServiceAccount(vectorCR); done { + return ReconcileResult(err) + } + + return ReconcileResult(nil) +} + +func (r *VectorReconciler) ensureVectorAggregatorServiceAccount(vectorCR *vectorv1alpha1.Vector) (bool, ctrl.Result, error) { + vectorAggregatorServiceAccount := r.createVectorAggregatorServiceAccount(vectorCR) + + if err := controllerutil.SetControllerReference(vectorCR, vectorAggregatorServiceAccount, r.Scheme); err != nil { + return ReconcileResult(err) + } + _, err := r.CreateOrUpdateServiceAccount(vectorAggregatorServiceAccount) + + return ReconcileResult(err) +} + +func (r *VectorReconciler) ensureVectorAggregatorService(vectorCR *vectorv1alpha1.Vector) (bool, ctrl.Result, error) { + ctx := context.Background() + log := log.FromContext(ctx).WithValues("vector-aggregator-service", vectorCR.Name) + + log.Info("start Reconcile Vector Aggregator Service") + + vectorAggregatorService := r.createVectorAggregatorService(vectorCR) + + if err := controllerutil.SetControllerReference(vectorCR, vectorAggregatorService, r.Scheme); err != nil { + return ReconcileResult(err) + } + _, err := r.CreateOrUpdateService(vectorAggregatorService) + + return ReconcileResult(err) +} + +func (r *VectorReconciler) ensureVectorAggregatorSecret(vectorCR *vectorv1alpha1.Vector) (bool, ctrl.Result, error) { + ctx := context.Background() + log := log.FromContext(ctx).WithValues("vector-aggregator-secret", vectorCR.Name) + + log.Info("start Reconcile Vector Aggregator Secret") + + vectorAggregatorSecret := r.createVectorAggregatorSecret(vectorCR) + + if err := controllerutil.SetControllerReference(vectorCR, vectorAggregatorSecret, r.Scheme); err != nil { + return ReconcileResult(err) + } + _, err := r.CreateOrUpdateSecret(vectorAggregatorSecret) + + return ReconcileResult(err) +} + +func (r *VectorReconciler) ensureVectorAggregatorStatefulSet(vectorCR *vectorv1alpha1.Vector) (bool, ctrl.Result, error) { + ctx := context.Background() + log := log.FromContext(ctx).WithValues("vector-aggregator-statefulset", vectorCR.Name) + + log.Info("start Reconcile Vector Aggregator StatefulSet") + + vectorAggregatorStatefulSet := r.createVectorAggregatorStatefulSet(vectorCR) + + if err := controllerutil.SetControllerReference(vectorCR, vectorAggregatorStatefulSet, r.Scheme); err != nil { + return ReconcileResult(err) + } + _, err := r.CreateOrUpdateStatefulSet(vectorAggregatorStatefulSet) + + return ReconcileResult(err) +} + +func labelsForVectorAggregator(name string) map[string]string { + return map[string]string{ + label.ManagedByLabelKey: "vector-operator", + label.NameLabelKey: "vector", + label.ComponentLabelKey: "Aggregator", + label.InstanceLabelKey: name, + } +} + +func objectMetaVectorAggregator(v *vectorv1alpha1.Vector, labels map[string]string) metav1.ObjectMeta { + return metav1.ObjectMeta{ + Name: v.Name + "-aggregator", + Namespace: v.Namespace, + Labels: labels, + } +} + +func getNameVectorAggregator(v *vectorv1alpha1.Vector) string { + name := v.Name + "-aggregator" + return name +} diff --git a/controllers/vector_aggregator_rbac.go b/controllers/vector_aggregator_rbac.go new file mode 100644 index 00000000..67511128 --- /dev/null +++ b/controllers/vector_aggregator_rbac.go @@ -0,0 +1,17 @@ +package controllers + +import ( + corev1 "k8s.io/api/core/v1" + + vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" +) + +func (r *VectorReconciler) createVectorAggregatorServiceAccount(v *vectorv1alpha1.Vector) *corev1.ServiceAccount { + labels := labelsForVectorAggregator(v.Name) + + serviceAccount := &corev1.ServiceAccount{ + ObjectMeta: objectMetaVectorAggregator(v, labels), + } + + return serviceAccount +} diff --git a/controllers/vector_aggregator_secret.go b/controllers/vector_aggregator_secret.go new file mode 100644 index 00000000..60bef159 --- /dev/null +++ b/controllers/vector_aggregator_secret.go @@ -0,0 +1,40 @@ +package controllers + +import ( + corev1 "k8s.io/api/core/v1" + + vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" +) + +var vectorAggregatorConfig = ` +data_dir: /vector-data-dir +api: + enabled: true + address: 127.0.0.1:8686 + playground: false +sources: + vector: + address: 0.0.0.0:6000 + type: vector + version: "2" +sinks: + stdout: + type: console + inputs: [vector] + encoding: + codec: json +` + +func (r *VectorReconciler) createVectorAggregatorSecret(v *vectorv1alpha1.Vector) *corev1.Secret { + labels := labelsForVectorAggregator(v.Name) + + config := map[string][]byte{ + "aggregator.yaml": []byte(vectorAggregatorConfig), + } + + secret := &corev1.Secret{ + ObjectMeta: objectMetaVectorAggregator(v, labels), + Data: config, + } + return secret +} diff --git a/controllers/vector_aggregator_service.go b/controllers/vector_aggregator_service.go new file mode 100644 index 00000000..2e500c12 --- /dev/null +++ b/controllers/vector_aggregator_service.go @@ -0,0 +1,28 @@ +package controllers + +import ( + corev1 "k8s.io/api/core/v1" + + vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" + "k8s.io/apimachinery/pkg/util/intstr" +) + +func (r *VectorReconciler) createVectorAggregatorService(v *vectorv1alpha1.Vector) *corev1.Service { + labels := labelsForVectorAggregator(v.Name) + + service := &corev1.Service{ + ObjectMeta: objectMetaVectorAggregator(v, labels), + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: "vector", + Protocol: corev1.Protocol("TCP"), + Port: 6000, + TargetPort: intstr.FromInt(6000), + }, + }, + Selector: labels, + }, + } + return service +} diff --git a/controllers/vector_aggregator_statefulset.go b/controllers/vector_aggregator_statefulset.go new file mode 100644 index 00000000..ce6427fd --- /dev/null +++ b/controllers/vector_aggregator_statefulset.go @@ -0,0 +1,119 @@ +package controllers + +import ( + vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func (r *VectorReconciler) createVectorAggregatorStatefulSet(v *vectorv1alpha1.Vector) *appsv1.StatefulSet { + labels := labelsForVectorAggregator(v.Name) + replicas := int32(v.Spec.Aggregator.Replicas) + statefulset := &appsv1.StatefulSet{ + ObjectMeta: objectMetaVectorAggregator(v, labels), + Spec: appsv1.StatefulSetSpec{ + Selector: &metav1.LabelSelector{MatchLabels: labels}, + Replicas: &replicas, + Template: corev1.PodTemplateSpec{ + ObjectMeta: objectMetaVectorAggregator(v, labels), + Spec: corev1.PodSpec{ + ServiceAccountName: getNameVectorAggregator(v), + Volumes: generateVectorAggregatorVolume(v), + SecurityContext: &corev1.PodSecurityContext{}, + Containers: []corev1.Container{ + { + Name: getNameVectorAggregator(v), + Image: v.Spec.Aggregator.Image, + Args: []string{"--config-dir", "/etc/vector/"}, + Env: generateVectorAggregatorEnvs(v), + Ports: []corev1.ContainerPort{ + { + Name: "vector", + ContainerPort: 6000, + Protocol: "TCP", + }, + }, + VolumeMounts: generateVectorAggregatorVolumeMounts(v), + SecurityContext: &corev1.SecurityContext{}, + }, + }, + }, + }, + }, + } + + return statefulset +} + +func generateVectorAggregatorVolume(v *vectorv1alpha1.Vector) []corev1.Volume { + volume := []corev1.Volume{ + { + Name: "config", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: getNameVectorAggregator(v), + }, + }, + }, + { + Name: "data", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/var/lib/vector", + }, + }, + }, + } + + return volume +} + +func generateVectorAggregatorVolumeMounts(spec *vectorv1alpha1.Vector) []corev1.VolumeMount { + volumeMount := []corev1.VolumeMount{ + { + Name: "config", + MountPath: "/etc/vector/", + }, + { + Name: "data", + MountPath: "/vector-data-dir", + }, + } + + return volumeMount +} + +func generateVectorAggregatorEnvs(spec *vectorv1alpha1.Vector) []corev1.EnvVar { + envs := []corev1.EnvVar{ + { + Name: "VECTOR_SELF_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + APIVersion: "v1", + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "VECTOR_SELF_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + APIVersion: "v1", + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "VECTOR_SELF_POD_NAMESPACE", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + APIVersion: "v1", + FieldPath: "metadata.namespace", + }, + }, + }, + } + + return envs +} diff --git a/controllers/vector_controller.go b/controllers/vector_controller.go index 74598596..18ea580b 100644 --- a/controllers/vector_controller.go +++ b/controllers/vector_controller.go @@ -64,6 +64,10 @@ func (r *VectorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr return result, err } + if done, result, err = r.ensureVectorAggregator(vectorCR); done { + return result, err + } + return ctrl.Result{RequeueAfter: 5 * time.Second}, nil } diff --git a/go.mod b/go.mod index c4556705..d981895a 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,6 @@ go 1.18 require ( github.com/go-logr/logr v1.2.0 - github.com/google/martian v2.1.0+incompatible github.com/onsi/ginkgo v1.16.5 github.com/onsi/gomega v1.18.1 k8s.io/api v0.24.2 diff --git a/go.sum b/go.sum index e31d1d61..c27022bf 100644 --- a/go.sum +++ b/go.sum @@ -235,7 +235,6 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= From be16e06c48942e5e50955b7abac23421a24777f4 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Mon, 10 Oct 2022 16:09:00 +0300 Subject: [PATCH 002/316] exclude vector logs --- controllers/label/label.go | 3 +- controllers/vector_agent_controller.go | 9 +++-- controllers/vector_aggregator_controller.go | 9 +++-- controllers/vector_aggregator_statefulset.go | 39 +------------------- 4 files changed, 13 insertions(+), 47 deletions(-) diff --git a/controllers/label/label.go b/controllers/label/label.go index 13048fe7..3d21f139 100644 --- a/controllers/label/label.go +++ b/controllers/label/label.go @@ -14,7 +14,8 @@ const ( // It's set by helm when installing a release InstanceLabelKey string = "app.kubernetes.io/instance" // VersionLabelKey is Kubernetes recommended label key, it represents the version of the app - VersionLabelKey string = "app.kubernetes.io/version" + VersionLabelKey string = "app.kubernetes.io/version" + VectorExcludeLabel string = "vector.dev/exclude" // PodName is to select pod by name // https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#pod-selector diff --git a/controllers/vector_agent_controller.go b/controllers/vector_agent_controller.go index 396f707d..8f3dae1c 100644 --- a/controllers/vector_agent_controller.go +++ b/controllers/vector_agent_controller.go @@ -140,10 +140,11 @@ func (r *VectorReconciler) ensureVectorAgentDaemonSet(vectorCR *vectorv1alpha1.V func labelsForVectorAgent(name string) map[string]string { return map[string]string{ - label.ManagedByLabelKey: "vector-operator", - label.NameLabelKey: "vector", - label.ComponentLabelKey: "Agent", - label.InstanceLabelKey: name, + label.ManagedByLabelKey: "vector-operator", + label.NameLabelKey: "vector", + label.ComponentLabelKey: "Agent", + label.InstanceLabelKey: name, + label.VectorExcludeLabel: "true", } } diff --git a/controllers/vector_aggregator_controller.go b/controllers/vector_aggregator_controller.go index 8c3ba51a..ed5e2597 100644 --- a/controllers/vector_aggregator_controller.go +++ b/controllers/vector_aggregator_controller.go @@ -110,10 +110,11 @@ func (r *VectorReconciler) ensureVectorAggregatorStatefulSet(vectorCR *vectorv1a func labelsForVectorAggregator(name string) map[string]string { return map[string]string{ - label.ManagedByLabelKey: "vector-operator", - label.NameLabelKey: "vector", - label.ComponentLabelKey: "Aggregator", - label.InstanceLabelKey: name, + label.ManagedByLabelKey: "vector-operator", + label.NameLabelKey: "vector", + label.ComponentLabelKey: "Aggregator", + label.InstanceLabelKey: name, + label.VectorExcludeLabel: "true", } } diff --git a/controllers/vector_aggregator_statefulset.go b/controllers/vector_aggregator_statefulset.go index ce6427fd..232b7e92 100644 --- a/controllers/vector_aggregator_statefulset.go +++ b/controllers/vector_aggregator_statefulset.go @@ -26,7 +26,6 @@ func (r *VectorReconciler) createVectorAggregatorStatefulSet(v *vectorv1alpha1.V Name: getNameVectorAggregator(v), Image: v.Spec.Aggregator.Image, Args: []string{"--config-dir", "/etc/vector/"}, - Env: generateVectorAggregatorEnvs(v), Ports: []corev1.ContainerPort{ { Name: "vector", @@ -59,9 +58,7 @@ func generateVectorAggregatorVolume(v *vectorv1alpha1.Vector) []corev1.Volume { { Name: "data", VolumeSource: corev1.VolumeSource{ - HostPath: &corev1.HostPathVolumeSource{ - Path: "/var/lib/vector", - }, + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, } @@ -83,37 +80,3 @@ func generateVectorAggregatorVolumeMounts(spec *vectorv1alpha1.Vector) []corev1. return volumeMount } - -func generateVectorAggregatorEnvs(spec *vectorv1alpha1.Vector) []corev1.EnvVar { - envs := []corev1.EnvVar{ - { - Name: "VECTOR_SELF_NODE_NAME", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - APIVersion: "v1", - FieldPath: "spec.nodeName", - }, - }, - }, - { - Name: "VECTOR_SELF_POD_NAME", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - APIVersion: "v1", - FieldPath: "metadata.name", - }, - }, - }, - { - Name: "VECTOR_SELF_POD_NAMESPACE", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - APIVersion: "v1", - FieldPath: "metadata.namespace", - }, - }, - }, - } - - return envs -} From 80cc58c5eeb6c23cf43fdcac605360d990bd6432 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Thu, 13 Oct 2022 11:53:21 +0300 Subject: [PATCH 003/316] aggregator defatult false --- api/v1alpha1/vector_types.go | 1 + config/crd/bases/observability.kaasops.io_vectors.yaml | 2 ++ controllers/vector_controller.go | 6 ++++-- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/api/v1alpha1/vector_types.go b/api/v1alpha1/vector_types.go index e232e7a3..3b031373 100644 --- a/api/v1alpha1/vector_types.go +++ b/api/v1alpha1/vector_types.go @@ -48,6 +48,7 @@ type VectorAgent struct { // VectorAggregator is the Schema for the Vector Aggregator type VectorAggregator struct { + Enable bool `json:"enable,omitempty"` // +kubebuilder:default:="timberio/vector:0.24.0-distroless-libc" Image string `json:"image,omitempty"` Replicas int `json:"replicas,omitempty"` diff --git a/config/crd/bases/observability.kaasops.io_vectors.yaml b/config/crd/bases/observability.kaasops.io_vectors.yaml index d2caad74..330a3227 100644 --- a/config/crd/bases/observability.kaasops.io_vectors.yaml +++ b/config/crd/bases/observability.kaasops.io_vectors.yaml @@ -47,6 +47,8 @@ spec: aggregator: description: Vector Aggregator properties: + enable: + type: boolean image: default: timberio/vector:0.24.0-distroless-libc type: string diff --git a/controllers/vector_controller.go b/controllers/vector_controller.go index 18ea580b..29a40413 100644 --- a/controllers/vector_controller.go +++ b/controllers/vector_controller.go @@ -64,8 +64,10 @@ func (r *VectorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr return result, err } - if done, result, err = r.ensureVectorAggregator(vectorCR); done { - return result, err + if vectorCR.Spec.Aggregator.Enable { + if done, result, err = r.ensureVectorAggregator(vectorCR); done { + return result, err + } } return ctrl.Result{RequeueAfter: 5 * time.Second}, nil From 509d0896abea97db94df47d67e349be02534282e Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Thu, 13 Oct 2022 11:59:24 +0300 Subject: [PATCH 004/316] init VectorPipeline api --- PROJECT | 9 ++ api/v1alpha1/vectorpipeline_types.go | 64 ++++++++++ api/v1alpha1/zz_generated.deepcopy.go | 109 ++++++++++++++++++ config/crd/kustomization.yaml | 3 + .../cainjection_in_vectorpipelines.yaml | 7 ++ .../patches/webhook_in_vectorpipelines.yaml | 16 +++ config/rbac/vectorpipeline_editor_role.yaml | 24 ++++ config/rbac/vectorpipeline_viewer_role.yaml | 20 ++++ config/samples/kustomization.yaml | 1 + ...observability_v1alpha1_vectorpipeline.yaml | 6 + controllers/vectorpipeline_controller.go | 62 ++++++++++ main.go | 7 ++ 12 files changed, 328 insertions(+) create mode 100644 api/v1alpha1/vectorpipeline_types.go create mode 100644 config/crd/patches/cainjection_in_vectorpipelines.yaml create mode 100644 config/crd/patches/webhook_in_vectorpipelines.yaml create mode 100644 config/rbac/vectorpipeline_editor_role.yaml create mode 100644 config/rbac/vectorpipeline_viewer_role.yaml create mode 100644 config/samples/observability_v1alpha1_vectorpipeline.yaml create mode 100644 controllers/vectorpipeline_controller.go diff --git a/PROJECT b/PROJECT index 4794c3a9..05c036cc 100644 --- a/PROJECT +++ b/PROJECT @@ -16,4 +16,13 @@ resources: kind: Vector path: github.com/kaasops/vector-operator/api/v1alpha1 version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: kaasops.io + group: observability + kind: VectorPipeline + path: github.com/kaasops/vector-operator/api/v1alpha1 + version: v1alpha1 version: "3" diff --git a/api/v1alpha1/vectorpipeline_types.go b/api/v1alpha1/vectorpipeline_types.go new file mode 100644 index 00000000..6a1c831e --- /dev/null +++ b/api/v1alpha1/vectorpipeline_types.go @@ -0,0 +1,64 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// VectorPipelineSpec defines the desired state of VectorPipeline +type VectorPipelineSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // Foo is an example field of VectorPipeline. Edit vectorpipeline_types.go to remove/update + Foo string `json:"foo,omitempty"` +} + +// VectorPipelineStatus defines the observed state of VectorPipeline +type VectorPipelineStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// VectorPipeline is the Schema for the vectorpipelines API +type VectorPipeline struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec VectorPipelineSpec `json:"spec,omitempty"` + Status VectorPipelineStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// VectorPipelineList contains a list of VectorPipeline +type VectorPipelineList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []VectorPipeline `json:"items"` +} + +func init() { + SchemeBuilder.Register(&VectorPipeline{}, &VectorPipelineList{}) +} diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index c0f11c0e..d395d47b 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -67,6 +67,21 @@ func (in *VectorAgent) DeepCopy() *VectorAgent { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VectorAggregator) DeepCopyInto(out *VectorAggregator) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VectorAggregator. +func (in *VectorAggregator) DeepCopy() *VectorAggregator { + if in == nil { + return nil + } + out := new(VectorAggregator) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VectorList) DeepCopyInto(out *VectorList) { *out = *in @@ -99,6 +114,95 @@ func (in *VectorList) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VectorPipeline) DeepCopyInto(out *VectorPipeline) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VectorPipeline. +func (in *VectorPipeline) DeepCopy() *VectorPipeline { + if in == nil { + return nil + } + out := new(VectorPipeline) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *VectorPipeline) 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 *VectorPipelineList) DeepCopyInto(out *VectorPipelineList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]VectorPipeline, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VectorPipelineList. +func (in *VectorPipelineList) DeepCopy() *VectorPipelineList { + if in == nil { + return nil + } + out := new(VectorPipelineList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *VectorPipelineList) 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 *VectorPipelineSpec) DeepCopyInto(out *VectorPipelineSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VectorPipelineSpec. +func (in *VectorPipelineSpec) DeepCopy() *VectorPipelineSpec { + if in == nil { + return nil + } + out := new(VectorPipelineSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VectorPipelineStatus) DeepCopyInto(out *VectorPipelineStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VectorPipelineStatus. +func (in *VectorPipelineStatus) DeepCopy() *VectorPipelineStatus { + if in == nil { + return nil + } + out := new(VectorPipelineStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VectorSpec) DeepCopyInto(out *VectorSpec) { *out = *in @@ -107,6 +211,11 @@ func (in *VectorSpec) DeepCopyInto(out *VectorSpec) { *out = new(VectorAgent) **out = **in } + if in.Aggregator != nil { + in, out := &in.Aggregator, &out.Aggregator + *out = new(VectorAggregator) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VectorSpec. diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index a236a16d..0121c8b1 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -3,17 +3,20 @@ # It should be run by config/default resources: - bases/observability.kaasops.io_vectors.yaml +- bases/observability.kaasops.io_vectorpipelines.yaml #+kubebuilder:scaffold:crdkustomizeresource patchesStrategicMerge: # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. # patches here are for enabling the conversion webhook for each CRD #- patches/webhook_in_vectors.yaml +#- patches/webhook_in_vectorpipelines.yaml #+kubebuilder:scaffold:crdkustomizewebhookpatch # [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. # patches here are for enabling the CA injection for each CRD #- patches/cainjection_in_vectors.yaml +#- patches/cainjection_in_vectorpipelines.yaml #+kubebuilder:scaffold:crdkustomizecainjectionpatch # the following config is for teaching kustomize how to do kustomization for CRDs. diff --git a/config/crd/patches/cainjection_in_vectorpipelines.yaml b/config/crd/patches/cainjection_in_vectorpipelines.yaml new file mode 100644 index 00000000..a6890ec8 --- /dev/null +++ b/config/crd/patches/cainjection_in_vectorpipelines.yaml @@ -0,0 +1,7 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: vectorpipelines.observability.kaasops.io diff --git a/config/crd/patches/webhook_in_vectorpipelines.yaml b/config/crd/patches/webhook_in_vectorpipelines.yaml new file mode 100644 index 00000000..b34cf2f8 --- /dev/null +++ b/config/crd/patches/webhook_in_vectorpipelines.yaml @@ -0,0 +1,16 @@ +# The following patch enables a conversion webhook for the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: vectorpipelines.observability.kaasops.io +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + namespace: system + name: webhook-service + path: /convert + conversionReviewVersions: + - v1 diff --git a/config/rbac/vectorpipeline_editor_role.yaml b/config/rbac/vectorpipeline_editor_role.yaml new file mode 100644 index 00000000..cdf5850a --- /dev/null +++ b/config/rbac/vectorpipeline_editor_role.yaml @@ -0,0 +1,24 @@ +# permissions for end users to edit vectorpipelines. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: vectorpipeline-editor-role +rules: +- apiGroups: + - observability.kaasops.io + resources: + - vectorpipelines + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - observability.kaasops.io + resources: + - vectorpipelines/status + verbs: + - get diff --git a/config/rbac/vectorpipeline_viewer_role.yaml b/config/rbac/vectorpipeline_viewer_role.yaml new file mode 100644 index 00000000..485298ca --- /dev/null +++ b/config/rbac/vectorpipeline_viewer_role.yaml @@ -0,0 +1,20 @@ +# permissions for end users to view vectorpipelines. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: vectorpipeline-viewer-role +rules: +- apiGroups: + - observability.kaasops.io + resources: + - vectorpipelines + verbs: + - get + - list + - watch +- apiGroups: + - observability.kaasops.io + resources: + - vectorpipelines/status + verbs: + - get diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index 1de27a2e..ab24709f 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -1,4 +1,5 @@ ## Append samples you want in your CSV to this file as resources ## resources: - observability_v1alpha1_vector.yaml +- observability_v1alpha1_vectorpipeline.yaml #+kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/samples/observability_v1alpha1_vectorpipeline.yaml b/config/samples/observability_v1alpha1_vectorpipeline.yaml new file mode 100644 index 00000000..312d3aa7 --- /dev/null +++ b/config/samples/observability_v1alpha1_vectorpipeline.yaml @@ -0,0 +1,6 @@ +apiVersion: observability.kaasops.io/v1alpha1 +kind: VectorPipeline +metadata: + name: vectorpipeline-sample +spec: + # TODO(user): Add fields here diff --git a/controllers/vectorpipeline_controller.go b/controllers/vectorpipeline_controller.go new file mode 100644 index 00000000..13f4b773 --- /dev/null +++ b/controllers/vectorpipeline_controller.go @@ -0,0 +1,62 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controllers + +import ( + "context" + + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" + + observabilityv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" +) + +// VectorPipelineReconciler reconciles a VectorPipeline object +type VectorPipelineReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +//+kubebuilder:rbac:groups=observability.kaasops.io,resources=vectorpipelines,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=observability.kaasops.io,resources=vectorpipelines/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=observability.kaasops.io,resources=vectorpipelines/finalizers,verbs=update + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the VectorPipeline object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.12.2/pkg/reconcile +func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + _ = log.FromContext(ctx) + + // TODO(user): your logic here + + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *VectorPipelineReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&observabilityv1alpha1.VectorPipeline{}). + Complete(r) +} diff --git a/main.go b/main.go index 2fecdfc8..5bed1890 100644 --- a/main.go +++ b/main.go @@ -96,6 +96,13 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "Vector") os.Exit(1) } + if err = (&controllers.VectorPipelineReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "VectorPipeline") + os.Exit(1) + } //+kubebuilder:scaffold:builder if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { From 1bebed2fb3fb62a6ad9ad766f219e7d580cce15a Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Mon, 17 Oct 2022 11:15:32 +0300 Subject: [PATCH 005/316] Vector pipeline api reconcile Vector pipeline api reconcile --- .vscode/launch.json | 15 ++ api/v1alpha1/vector_types.go | 10 +- api/v1alpha1/vectorpipeline_types.go | 36 +++- api/v1alpha1/zz_generated.deepcopy.go | 128 +++++++++++++- ...ervability.kaasops.io_vectorpipelines.yaml | 99 +++++++++++ .../observability.kaasops.io_vectors.yaml | 21 +-- config/rbac/role.yaml | 26 +++ .../observability_v1alpha1_vector.yaml | 7 +- ...observability_v1alpha1_vectorpipeline.yaml | 62 ++++++- controllers/{ => factory/helper}/helper.go | 2 +- controllers/{ => factory/k8sutils}/utils.go | 84 ++++----- controllers/{ => factory}/label/label.go | 0 controllers/factory/vector/config_build.go | 112 ++++++++++++ controllers/factory/vector/types.go | 17 ++ .../vectoragent/vector_agent_config.go | 44 +++++ .../vectoragent/vector_agent_controller.go | 164 ++++++++++++++++++ .../vectoragent}/vector_agent_daemonset.go | 6 +- .../vectoragent}/vector_agent_rbac.go | 8 +- .../vectoragent}/vector_agent_service.go | 4 +- .../factory/vectorpipeline/vectorpipeline.go | 38 ++++ controllers/vector_agent_controller.go | 162 ----------------- controllers/vector_agent_secret.go | 42 ----- controllers/vector_aggregator_controller.go | 132 -------------- controllers/vector_aggregator_rbac.go | 17 -- controllers/vector_aggregator_secret.go | 40 ----- controllers/vector_aggregator_service.go | 28 --- controllers/vector_aggregator_statefulset.go | 82 --------- controllers/vector_controller.go | 23 ++- controllers/vectorpipeline_controller.go | 27 ++- go.mod | 2 +- 30 files changed, 835 insertions(+), 603 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 config/crd/bases/observability.kaasops.io_vectorpipelines.yaml rename controllers/{ => factory/helper}/helper.go (90%) rename controllers/{ => factory/k8sutils}/utils.go (53%) rename controllers/{ => factory}/label/label.go (100%) create mode 100644 controllers/factory/vector/config_build.go create mode 100644 controllers/factory/vector/types.go create mode 100644 controllers/factory/vectoragent/vector_agent_config.go create mode 100644 controllers/factory/vectoragent/vector_agent_controller.go rename controllers/{ => factory/vectoragent}/vector_agent_daemonset.go (95%) rename controllers/{ => factory/vectoragent}/vector_agent_rbac.go (75%) rename controllers/{ => factory/vectoragent}/vector_agent_service.go (82%) create mode 100644 controllers/factory/vectorpipeline/vectorpipeline.go delete mode 100644 controllers/vector_agent_controller.go delete mode 100644 controllers/vector_agent_secret.go delete mode 100644 controllers/vector_aggregator_controller.go delete mode 100644 controllers/vector_aggregator_rbac.go delete mode 100644 controllers/vector_aggregator_secret.go delete mode 100644 controllers/vector_aggregator_service.go delete mode 100644 controllers/vector_aggregator_statefulset.go diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..6deb9a6b --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Package", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "main.go" + } + ] +} \ No newline at end of file diff --git a/api/v1alpha1/vector_types.go b/api/v1alpha1/vector_types.go index 3b031373..d9704da7 100644 --- a/api/v1alpha1/vector_types.go +++ b/api/v1alpha1/vector_types.go @@ -26,11 +26,11 @@ import ( // VectorSpec defines the desired state of Vector type VectorSpec struct { // DisableAggregation - DisableAggregation bool `json:"disableAggregation,omitempty"` + // DisableAggregation bool `json:"disableAggregation,omitempty"` // Vector Agent Agent *VectorAgent `json:"agent,omitempty"` // Vector Aggregator - Aggregator *VectorAggregator `json:"aggregator,omitempty"` + // Aggregator *VectorAggregator `json:"aggregator,omitempty"` } // VectorStatus defines the observed state of Vector @@ -42,8 +42,10 @@ type VectorStatus struct { // VectorAgent is the Schema for the Vector Agent type VectorAgent struct { // +kubebuilder:default:="timberio/vector:0.24.0-distroless-libc" - Image string `json:"image,omitempty"` - Service bool `json:"service,omitempty"` + Image string `json:"image,omitempty"` + DataDir string `json:"dataDir,omitempty"` + ApiEnabled bool `json:"ApiEnabled,omitempty"` + Service bool `json:"service,omitempty"` } // VectorAggregator is the Schema for the Vector Aggregator diff --git a/api/v1alpha1/vectorpipeline_types.go b/api/v1alpha1/vectorpipeline_types.go index 6a1c831e..f7216988 100644 --- a/api/v1alpha1/vectorpipeline_types.go +++ b/api/v1alpha1/vectorpipeline_types.go @@ -25,11 +25,39 @@ import ( // VectorPipelineSpec defines the desired state of VectorPipeline type VectorPipelineSpec struct { - // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - // Important: Run "make" to regenerate code after modifying this file + Source map[string]SourceSpec `json:"source,omitempty"` + Transforms map[string]TransformSpec `json:"transforms,omitempty"` + Sink map[string]SinkSpec `json:"sinks,omitempty"` +} + +type TransformSpec struct { + Type string `json:"type,omitempty"` + Inputs []string `json:"inputs,omitempty"` + Condition *Condition `json:"condition,omitempty"` +} + +type Condition struct { + Type string `json:"type,omitempty"` + Source string `json:"source,omitempty"` +} + +type SourceSpec struct { + Type string `json:"type,omitempty"` + ExtraLabelSelector string `json:"extra_label_selector,omitempty"` + ExtraFieldSelector string `json:"extra_field_selector,omitempty"` +} + +type SinkSpec struct { + Type string `json:"type,omitempty"` + Address string `json:"address,omitempty"` + Inputs []string `json:"inputs,omitempty"` + Encoding *Encoding `json:"encoding,omitempty"` + Rate *int32 `json:"rate,omitempty"` + PrintIntervalSecs int32 `json:"print_interval_secs,omitempty"` +} - // Foo is an example field of VectorPipeline. Edit vectorpipeline_types.go to remove/update - Foo string `json:"foo,omitempty"` +type Encoding struct { + Codec string `json:"codec,omitempty"` } // VectorPipelineStatus defines the observed state of VectorPipeline diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index d395d47b..c51b2b8a 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -25,6 +25,106 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Condition) DeepCopyInto(out *Condition) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Condition. +func (in *Condition) DeepCopy() *Condition { + if in == nil { + return nil + } + out := new(Condition) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Encoding) DeepCopyInto(out *Encoding) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Encoding. +func (in *Encoding) DeepCopy() *Encoding { + if in == nil { + return nil + } + out := new(Encoding) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SinkSpec) DeepCopyInto(out *SinkSpec) { + *out = *in + if in.Inputs != nil { + in, out := &in.Inputs, &out.Inputs + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Encoding != nil { + in, out := &in.Encoding, &out.Encoding + *out = new(Encoding) + **out = **in + } + if in.Rate != nil { + in, out := &in.Rate, &out.Rate + *out = new(int32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SinkSpec. +func (in *SinkSpec) DeepCopy() *SinkSpec { + if in == nil { + return nil + } + out := new(SinkSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SourceSpec) DeepCopyInto(out *SourceSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SourceSpec. +func (in *SourceSpec) DeepCopy() *SourceSpec { + if in == nil { + return nil + } + out := new(SourceSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TransformSpec) DeepCopyInto(out *TransformSpec) { + *out = *in + if in.Inputs != nil { + in, out := &in.Inputs, &out.Inputs + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Condition != nil { + in, out := &in.Condition, &out.Condition + *out = new(Condition) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TransformSpec. +func (in *TransformSpec) DeepCopy() *TransformSpec { + if in == nil { + return nil + } + out := new(TransformSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Vector) DeepCopyInto(out *Vector) { *out = *in @@ -119,7 +219,7 @@ func (in *VectorPipeline) DeepCopyInto(out *VectorPipeline) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec + in.Spec.DeepCopyInto(&out.Spec) out.Status = in.Status } @@ -176,6 +276,27 @@ func (in *VectorPipelineList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VectorPipelineSpec) DeepCopyInto(out *VectorPipelineSpec) { *out = *in + if in.Source != nil { + in, out := &in.Source, &out.Source + *out = make(map[string]SourceSpec, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Transforms != nil { + in, out := &in.Transforms, &out.Transforms + *out = make(map[string]TransformSpec, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } + if in.Sink != nil { + in, out := &in.Sink, &out.Sink + *out = make(map[string]SinkSpec, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VectorPipelineSpec. @@ -211,11 +332,6 @@ func (in *VectorSpec) DeepCopyInto(out *VectorSpec) { *out = new(VectorAgent) **out = **in } - if in.Aggregator != nil { - in, out := &in.Aggregator, &out.Aggregator - *out = new(VectorAggregator) - **out = **in - } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VectorSpec. diff --git a/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml b/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml new file mode 100644 index 00000000..c6c317f1 --- /dev/null +++ b/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml @@ -0,0 +1,99 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.2 + creationTimestamp: null + name: vectorpipelines.observability.kaasops.io +spec: + group: observability.kaasops.io + names: + kind: VectorPipeline + listKind: VectorPipelineList + plural: vectorpipelines + singular: vectorpipeline + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: VectorPipeline is the Schema for the vectorpipelines API + 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: + type: object + spec: + description: VectorPipelineSpec defines the desired state of VectorPipeline + properties: + sinks: + additionalProperties: + properties: + address: + type: string + encoding: + properties: + codec: + type: string + type: object + inputs: + items: + type: string + type: array + print_interval_secs: + format: int32 + type: integer + rate: + format: int32 + type: integer + type: + type: string + type: object + type: object + source: + additionalProperties: + properties: + extra_field_selector: + type: string + extra_label_selector: + type: string + type: + type: string + type: object + type: object + transforms: + additionalProperties: + properties: + condition: + properties: + source: + type: string + type: + type: string + type: object + inputs: + items: + type: string + type: array + type: + type: string + type: object + type: object + type: object + status: + description: VectorPipelineStatus defines the observed state of VectorPipeline + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/bases/observability.kaasops.io_vectors.yaml b/config/crd/bases/observability.kaasops.io_vectors.yaml index 330a3227..f24190e7 100644 --- a/config/crd/bases/observability.kaasops.io_vectors.yaml +++ b/config/crd/bases/observability.kaasops.io_vectors.yaml @@ -36,28 +36,19 @@ spec: description: VectorSpec defines the desired state of Vector properties: agent: - description: Vector Agent + description: DisableAggregation DisableAggregation bool `json:"disableAggregation,omitempty"` + Vector Agent properties: + ApiEnabled: + type: boolean + dataDir: + type: string image: default: timberio/vector:0.24.0-distroless-libc type: string service: type: boolean type: object - aggregator: - description: Vector Aggregator - properties: - enable: - type: boolean - image: - default: timberio/vector:0.24.0-distroless-libc - type: string - replicas: - type: integer - type: object - disableAggregation: - description: DisableAggregation - type: boolean type: object status: description: VectorStatus defines the observed state of Vector diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 187fbbb9..9b633876 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -5,6 +5,32 @@ metadata: creationTimestamp: null name: manager-role rules: +- apiGroups: + - observability.kaasops.io + resources: + - vectorpipelines + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - observability.kaasops.io + resources: + - vectorpipelines/finalizers + verbs: + - update +- apiGroups: + - observability.kaasops.io + resources: + - vectorpipelines/status + verbs: + - get + - patch + - update - apiGroups: - observability.kaasops.io resources: diff --git a/config/samples/observability_v1alpha1_vector.yaml b/config/samples/observability_v1alpha1_vector.yaml index 6aef4c96..b667b124 100644 --- a/config/samples/observability_v1alpha1_vector.yaml +++ b/config/samples/observability_v1alpha1_vector.yaml @@ -2,12 +2,9 @@ apiVersion: observability.kaasops.io/v1alpha1 kind: Vector metadata: name: vector-sample - namespace: vector + namespace: vector-operator-system spec: - disableAggregation: true agent: service: true image: "timberio/vector:0.24.0-distroless-libc" - aggregator: - image: "timberio/vector:0.24.0-distroless-libc" - replicas: 1 + diff --git a/config/samples/observability_v1alpha1_vectorpipeline.yaml b/config/samples/observability_v1alpha1_vectorpipeline.yaml index 312d3aa7..d56874f5 100644 --- a/config/samples/observability_v1alpha1_vectorpipeline.yaml +++ b/config/samples/observability_v1alpha1_vectorpipeline.yaml @@ -3,4 +3,64 @@ kind: VectorPipeline metadata: name: vectorpipeline-sample spec: - # TODO(user): Add fields here + source: + test1: + type: "kubernetes_logs" + extra_label_selector: "app!=testdeployment" + transforms: + test: + type: "filter" + inputs: + - test1 + condition: + type: "vrl" + source: ".status != 200" + sinks: + test2: + type: "console" + encoding: + codec: "json" + inputs: + - test1 +--- +apiVersion: observability.kaasops.io/v1alpha1 +kind: VectorPipeline +metadata: + name: vectorpipeline-sample1 + namespace: kube-system +spec: + source: + test1: + type: "kubernetes_logs" + extra_label_selector: "app!=testdeployment2" + sinks: + test2: + type: "console" + encoding: + codec: "json" + inputs: + - test1 +# --- +# apiVersion: observability.kaasops.io/v1alpha1 +# kind: VectorPipeline +# metadata: +# name: vectorpipeline-sample2 +# namespace: default +# spec: +# source: +# labelSelector: +# app: "testdeployment2" +# sinks: +# type: "console" +# --- +# apiVersion: observability.kaasops.io/v1alpha1 +# kind: VectorPipeline +# metadata: +# name: vectorpipeline-sample3 +# namespace: default +# spec: +# source: +# labelSelector: +# app: "testdeployment2" +# sinks: +# type: "console" \ No newline at end of file diff --git a/controllers/helper.go b/controllers/factory/helper/helper.go similarity index 90% rename from controllers/helper.go rename to controllers/factory/helper/helper.go index d57c68fb..5247a1ef 100644 --- a/controllers/helper.go +++ b/controllers/factory/helper/helper.go @@ -1,4 +1,4 @@ -package controllers +package helper import ctrl "sigs.k8s.io/controller-runtime" diff --git a/controllers/utils.go b/controllers/factory/k8sutils/utils.go similarity index 53% rename from controllers/utils.go rename to controllers/factory/k8sutils/utils.go index 3e611d94..d34c444e 100644 --- a/controllers/utils.go +++ b/controllers/factory/k8sutils/utils.go @@ -1,4 +1,4 @@ -package controllers +package k8sutils import ( "context" @@ -13,49 +13,49 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" ) -func (r *VectorReconciler) CreateOrUpdateService(svc *corev1.Service) (*reconcile.Result, error) { - return r.reconcileService(svc) +func CreateOrUpdateService(svc *corev1.Service, c client.Client) (*reconcile.Result, error) { + return reconcileService(svc, c) } -func (r *VectorReconciler) CreateOrUpdateSecret(secret *corev1.Secret) (*reconcile.Result, error) { - return r.reconcileSecret(secret) +func CreateOrUpdateSecret(secret *corev1.Secret, c client.Client) (*reconcile.Result, error) { + return reconcileSecret(secret, c) } -func (r *VectorReconciler) CreateOrUpdateDaemonSet(daemonSet *appsv1.DaemonSet) (*reconcile.Result, error) { - return r.reconcileDaemonSet(daemonSet) +func CreateOrUpdateDaemonSet(daemonSet *appsv1.DaemonSet, c client.Client) (*reconcile.Result, error) { + return reconcileDaemonSet(daemonSet, c) } -func (r *VectorReconciler) CreateOrUpdateStatefulSet(statefulSet *appsv1.StatefulSet) (*reconcile.Result, error) { - return r.reconcileStatefulSet(statefulSet) +func CreateOrUpdateStatefulSet(statefulSet *appsv1.StatefulSet, c client.Client) (*reconcile.Result, error) { + return reconcileStatefulSet(statefulSet, c) } -func (r *VectorReconciler) CreateOrUpdateServiceAccount(secret *corev1.ServiceAccount) (*reconcile.Result, error) { - return r.reconcileServiceAccount(secret) +func CreateOrUpdateServiceAccount(secret *corev1.ServiceAccount, c client.Client) (*reconcile.Result, error) { + return reconcileServiceAccount(secret, c) } -func (r *VectorReconciler) CreateOrUpdateClusterRole(secret *rbacv1.ClusterRole) (*reconcile.Result, error) { - return r.reconcileClusterRole(secret) +func CreateOrUpdateClusterRole(secret *rbacv1.ClusterRole, c client.Client) (*reconcile.Result, error) { + return reconcileClusterRole(secret, c) } -func (r *VectorReconciler) CreateOrUpdateClusterRoleBinding(secret *rbacv1.ClusterRoleBinding) (*reconcile.Result, error) { - return r.reconcileClusterRoleBinding(secret) +func CreateOrUpdateClusterRoleBinding(secret *rbacv1.ClusterRoleBinding, c client.Client) (*reconcile.Result, error) { + return reconcileClusterRoleBinding(secret, c) } -func (r *VectorReconciler) reconcileService(obj runtime.Object) (*reconcile.Result, error) { +func reconcileService(obj runtime.Object, c client.Client) (*reconcile.Result, error) { existing := &corev1.Service{} desired := obj.(*corev1.Service) - err := r.Create(context.TODO(), desired) + err := c.Create(context.TODO(), desired) if err != nil && errors.IsAlreadyExists(err) { - err := r.Get(context.TODO(), client.ObjectKeyFromObject(desired), existing) + err := c.Get(context.TODO(), client.ObjectKeyFromObject(desired), existing) if err != nil { return nil, err } if !equality.Semantic.DeepEqual(existing, desired) { existing.Spec = desired.Spec existing.Labels = desired.Labels - err := r.Update(context.TODO(), existing) + err := c.Update(context.TODO(), existing) return nil, err } } @@ -66,21 +66,21 @@ func (r *VectorReconciler) reconcileService(obj runtime.Object) (*reconcile.Resu return nil, nil } -func (r *VectorReconciler) reconcileSecret(obj runtime.Object) (*reconcile.Result, error) { +func reconcileSecret(obj runtime.Object, c client.Client) (*reconcile.Result, error) { existing := &corev1.Secret{} desired := obj.(*corev1.Secret) - err := r.Create(context.TODO(), desired) + err := c.Create(context.TODO(), desired) if err != nil && errors.IsAlreadyExists(err) { - err := r.Get(context.TODO(), client.ObjectKeyFromObject(desired), existing) + err := c.Get(context.TODO(), client.ObjectKeyFromObject(desired), existing) if err != nil { return nil, err } if !equality.Semantic.DeepEqual(existing, desired) { existing.Data = desired.Data existing.Labels = desired.Labels - err := r.Update(context.TODO(), existing) + err := c.Update(context.TODO(), existing) return nil, err } } @@ -91,21 +91,21 @@ func (r *VectorReconciler) reconcileSecret(obj runtime.Object) (*reconcile.Resul return nil, nil } -func (r *VectorReconciler) reconcileDaemonSet(obj runtime.Object) (*reconcile.Result, error) { +func reconcileDaemonSet(obj runtime.Object, c client.Client) (*reconcile.Result, error) { existing := &appsv1.DaemonSet{} desired := obj.(*appsv1.DaemonSet) - err := r.Create(context.TODO(), desired) + err := c.Create(context.TODO(), desired) if err != nil && errors.IsAlreadyExists(err) { - err := r.Get(context.TODO(), client.ObjectKeyFromObject(desired), existing) + err := c.Get(context.TODO(), client.ObjectKeyFromObject(desired), existing) if err != nil { return nil, err } if !equality.Semantic.DeepEqual(existing, desired) { existing.Spec = desired.Spec existing.Labels = desired.Labels - err := r.Update(context.TODO(), existing) + err := c.Update(context.TODO(), existing) return nil, err } } @@ -116,21 +116,21 @@ func (r *VectorReconciler) reconcileDaemonSet(obj runtime.Object) (*reconcile.Re return nil, nil } -func (r *VectorReconciler) reconcileStatefulSet(obj runtime.Object) (*reconcile.Result, error) { +func reconcileStatefulSet(obj runtime.Object, c client.Client) (*reconcile.Result, error) { existing := &appsv1.StatefulSet{} desired := obj.(*appsv1.StatefulSet) - err := r.Create(context.TODO(), desired) + err := c.Create(context.TODO(), desired) if err != nil && errors.IsAlreadyExists(err) { - err := r.Get(context.TODO(), client.ObjectKeyFromObject(desired), existing) + err := c.Get(context.TODO(), client.ObjectKeyFromObject(desired), existing) if err != nil { return nil, err } if !equality.Semantic.DeepEqual(existing, desired) { existing.Spec = desired.Spec existing.Labels = desired.Labels - err := r.Update(context.TODO(), existing) + err := c.Update(context.TODO(), existing) return nil, err } } @@ -141,14 +141,14 @@ func (r *VectorReconciler) reconcileStatefulSet(obj runtime.Object) (*reconcile. return nil, nil } -func (r *VectorReconciler) reconcileServiceAccount(obj runtime.Object) (*reconcile.Result, error) { +func reconcileServiceAccount(obj runtime.Object, c client.Client) (*reconcile.Result, error) { existing := &corev1.ServiceAccount{} desired := obj.(*corev1.ServiceAccount) - err := r.Create(context.TODO(), desired) + err := c.Create(context.TODO(), desired) if err != nil && errors.IsAlreadyExists(err) { - err := r.Get(context.TODO(), client.ObjectKeyFromObject(desired), existing) + err := c.Get(context.TODO(), client.ObjectKeyFromObject(desired), existing) if err != nil { return nil, err } @@ -160,20 +160,20 @@ func (r *VectorReconciler) reconcileServiceAccount(obj runtime.Object) (*reconci return nil, nil } -func (r *VectorReconciler) reconcileClusterRole(obj runtime.Object) (*reconcile.Result, error) { +func reconcileClusterRole(obj runtime.Object, c client.Client) (*reconcile.Result, error) { existing := &rbacv1.ClusterRole{} desired := obj.(*rbacv1.ClusterRole) - err := r.Create(context.TODO(), desired) + err := c.Create(context.TODO(), desired) if err != nil && errors.IsAlreadyExists(err) { - err := r.Get(context.TODO(), client.ObjectKeyFromObject(desired), existing) + err := c.Get(context.TODO(), client.ObjectKeyFromObject(desired), existing) if err != nil { return nil, err } if !equality.Semantic.DeepEqual(existing, desired) { existing.Rules = desired.Rules - err := r.Update(context.TODO(), existing) + err := c.Update(context.TODO(), existing) return nil, err } } @@ -184,21 +184,21 @@ func (r *VectorReconciler) reconcileClusterRole(obj runtime.Object) (*reconcile. return nil, nil } -func (r *VectorReconciler) reconcileClusterRoleBinding(obj runtime.Object) (*reconcile.Result, error) { +func reconcileClusterRoleBinding(obj runtime.Object, c client.Client) (*reconcile.Result, error) { existing := &rbacv1.ClusterRoleBinding{} desired := obj.(*rbacv1.ClusterRoleBinding) - err := r.Create(context.TODO(), desired) + err := c.Create(context.TODO(), desired) if err != nil && errors.IsAlreadyExists(err) { - err := r.Get(context.TODO(), client.ObjectKeyFromObject(desired), existing) + err := c.Get(context.TODO(), client.ObjectKeyFromObject(desired), existing) if err != nil { return nil, err } if !equality.Semantic.DeepEqual(existing, desired) { existing.RoleRef = desired.RoleRef existing.Subjects = desired.Subjects - err := r.Update(context.TODO(), existing) + err := c.Update(context.TODO(), existing) return nil, err } } diff --git a/controllers/label/label.go b/controllers/factory/label/label.go similarity index 100% rename from controllers/label/label.go rename to controllers/factory/label/label.go diff --git a/controllers/factory/vector/config_build.go b/controllers/factory/vector/config_build.go new file mode 100644 index 00000000..f2064bb6 --- /dev/null +++ b/controllers/factory/vector/config_build.go @@ -0,0 +1,112 @@ +package vector + +import ( + "encoding/json" + + "github.com/kaasops/vector-operator/api/v1alpha1" +) + +var ( + sourceDefault = v1alpha1.SourceSpec{ + Type: "kubernetes_logs", + } + + rate int32 = 100 + sinkDefault = v1alpha1.SinkSpec{ + Type: "blackhole", + Inputs: []string{"defaultSource"}, + Rate: &rate, + PrintIntervalSecs: 60, + } +) + +func GenerateConfig( + cr *v1alpha1.Vector, + vp map[string]*v1alpha1.VectorPipeline, +) ([]byte, error) { + cfg := NewVectorConfig(cr.Spec.Agent.DataDir, cr.Spec.Agent.ApiEnabled) + sources, transforms, sinks := getComponents(vp) + if len(sources) == 0 { + sources = map[string]*v1alpha1.SourceSpec{ + "defaultSource": &sourceDefault, + } + } + if len(sinks) == 0 { + sinks = map[string]*v1alpha1.SinkSpec{ + "defaultSink": &sinkDefault, + } + } + + cfg.Sinks = sinks + cfg.Sources = sources + cfg.Transforms = transforms + + return vectorConfigToJson(cfg) +} + +func NewVectorConfig(dataDir string, apiEnabled bool) *VectorConfig { + sources := make(map[string]*v1alpha1.SourceSpec) + sinks := make(map[string]*v1alpha1.SinkSpec) + + return &VectorConfig{ + DataDir: dataDir, + Api: &ApiSpec{ + Enabled: &apiEnabled, + }, + Sources: sources, + Sinks: sinks, + } +} + +func getComponents(vps map[string]*v1alpha1.VectorPipeline) (map[string]*v1alpha1.SourceSpec, map[string]*v1alpha1.TransformSpec, map[string]*v1alpha1.SinkSpec) { + sources := make(map[string]*v1alpha1.SourceSpec) + transforms := make(map[string]*v1alpha1.TransformSpec) + sinks := make(map[string]*v1alpha1.SinkSpec) + + for name, vp := range vps { + for sourceName, source := range vp.Spec.Source { + sources[name+"-"+sourceName+"-source"] = &source + } + + for sinkName, sink := range vp.Spec.Sink { + inputs := make([]string, 0) + for _, i := range sink.Inputs { + newInput := name + "-" + i + "-source" + inputs = append(inputs, newInput) + } + + sink.Inputs = inputs + sinks[name+"-"+sinkName+"-source"] = &sink + } + + for transformName, transform := range vp.Spec.Transforms { + inputs := make([]string, 0) + for _, i := range transform.Inputs { + newInput := name + "-" + i + "-source" + inputs = append(inputs, newInput) + } + + transform.Inputs = inputs + transforms[name+"-"+transformName+"-transform"] = &transform + + } + } + return sources, transforms, sinks +} + +// func createKeyValuePairs(m map[string]string) string { +// b := new(bytes.Buffer) +// for key, value := range m { +// fmt.Fprintf(b, "%s=\"%s\",", key, value) +// } +// return b.String() +// } + +func vectorConfigToJson(conf *VectorConfig) ([]byte, error) { + data, err := json.Marshal(conf) + if err != nil { + return nil, err + } + + return data, nil +} diff --git a/controllers/factory/vector/types.go b/controllers/factory/vector/types.go new file mode 100644 index 00000000..6440c56f --- /dev/null +++ b/controllers/factory/vector/types.go @@ -0,0 +1,17 @@ +package vector + +import "github.com/kaasops/vector-operator/api/v1alpha1" + +type VectorConfig struct { + DataDir string `json:"data_dir,omitempty"` + Api *ApiSpec `json:"api,omitempty"` + Sources map[string]*v1alpha1.SourceSpec `json:"sources,omitempty"` + Transforms map[string]*v1alpha1.TransformSpec `json:"transforms,omitempty"` + Sinks map[string]*v1alpha1.SinkSpec `json:"sinks,omitempty"` +} + +type ApiSpec struct { + Enabled *bool `json:"enabled,omitempty"` + Address *string `json:"address,omitempty"` + Playground *bool `json:"playground,omitempty"` +} diff --git a/controllers/factory/vectoragent/vector_agent_config.go b/controllers/factory/vectoragent/vector_agent_config.go new file mode 100644 index 00000000..6e2868a4 --- /dev/null +++ b/controllers/factory/vectoragent/vector_agent_config.go @@ -0,0 +1,44 @@ +package vectoragent + +import ( + "context" + + corev1 "k8s.io/api/core/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + + vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" + "github.com/kaasops/vector-operator/controllers/factory/vector" + "github.com/kaasops/vector-operator/controllers/factory/vectorpipeline" +) + +func createVectorAgentConfig(ctx context.Context, v *vectorv1alpha1.Vector, c client.Client) (*corev1.Secret, error) { + cfg, err := getConfig(ctx, v, c) + if err != nil { + return nil, err + } + + labels := labelsForVectorAgent(v.Name) + config := map[string][]byte{ + "agent.json": cfg, + } + + secret := &corev1.Secret{ + ObjectMeta: objectMetaVectorAgent(v, labels), + Data: config, + } + + return secret, nil +} + +func getConfig(ctx context.Context, v *vectorv1alpha1.Vector, c client.Client) ([]byte, error) { + vps, err := vectorpipeline.Select(ctx, c) + if err != nil { + return nil, err + } + cfg, err := vector.GenerateConfig(v, vps) + if err != nil { + return nil, err + } + + return cfg, nil +} diff --git a/controllers/factory/vectoragent/vector_agent_controller.go b/controllers/factory/vectoragent/vector_agent_controller.go new file mode 100644 index 00000000..68c1578d --- /dev/null +++ b/controllers/factory/vectoragent/vector_agent_controller.go @@ -0,0 +1,164 @@ +package vectoragent + +import ( + "context" + + vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" + "github.com/kaasops/vector-operator/controllers/factory/helper" + "github.com/kaasops/vector-operator/controllers/factory/k8sutils" + "github.com/kaasops/vector-operator/controllers/factory/label" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/pointer" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" +) + +func EnsureVectorAgent(vectorCR *vectorv1alpha1.Vector, rclient client.Client) (done bool, result ctrl.Result, err error) { + ctx := context.Background() + log := log.FromContext(ctx).WithValues("vector-agent", vectorCR.Name) + + log.Info("start Reconcile Vector Agent") + + if done, result, err = ensureVectorAgentRBAC(vectorCR, rclient); done { + return + } + + if vectorCR.Spec.Agent.Service { + if done, result, err = ensureVectorAgentService(vectorCR, rclient); done { + return + } + } + + if done, result, err = ensureVectorAgentConfig(vectorCR, rclient); done { + return + } + + if done, result, err = ensureVectorAgentDaemonSet(vectorCR, rclient); done { + return + } + + return +} + +func ensureVectorAgentRBAC(vectorCR *vectorv1alpha1.Vector, rclient client.Client) (bool, ctrl.Result, error) { + ctx := context.Background() + log := log.FromContext(ctx).WithValues("vector-agent-rbac", vectorCR.Name) + + log.Info("start Reconcile Vector Agent RBAC") + + if done, _, err := ensureVectorAgentServiceAccount(vectorCR, rclient); done { + return helper.ReconcileResult(err) + } + if done, _, err := ensureVectorAgentClusterRole(vectorCR, rclient); done { + return helper.ReconcileResult(err) + } + if done, _, err := ensureVectorAgentClusterRoleBinding(vectorCR, rclient); done { + return helper.ReconcileResult(err) + } + + return helper.ReconcileResult(nil) +} + +func ensureVectorAgentServiceAccount(vectorCR *vectorv1alpha1.Vector, rclient client.Client) (bool, ctrl.Result, error) { + vectorAgentServiceAccount := createVectorAgentServiceAccount(vectorCR) + + _, err := k8sutils.CreateOrUpdateServiceAccount(vectorAgentServiceAccount, rclient) + + return helper.ReconcileResult(err) +} + +func ensureVectorAgentClusterRole(vectorCR *vectorv1alpha1.Vector, rclient client.Client) (bool, ctrl.Result, error) { + vectorAgentClusterRole := createVectorAgentClusterRole(vectorCR) + + _, err := k8sutils.CreateOrUpdateClusterRole(vectorAgentClusterRole, rclient) + + return helper.ReconcileResult(err) +} + +func ensureVectorAgentClusterRoleBinding(vectorCR *vectorv1alpha1.Vector, rclient client.Client) (bool, ctrl.Result, error) { + vectorAgentClusterRoleBinding := createVectorAgentClusterRoleBinding(vectorCR) + + _, err := k8sutils.CreateOrUpdateClusterRoleBinding(vectorAgentClusterRoleBinding, rclient) + + return helper.ReconcileResult(err) +} + +func ensureVectorAgentService(vectorCR *vectorv1alpha1.Vector, rclient client.Client) (bool, ctrl.Result, error) { + ctx := context.Background() + log := log.FromContext(ctx).WithValues("vector-agent-service", vectorCR.Name) + + log.Info("start Reconcile Vector Agent Service") + + vectorAgentService := createVectorAgentService(vectorCR) + + _, err := k8sutils.CreateOrUpdateService(vectorAgentService, rclient) + + return helper.ReconcileResult(err) +} + +func ensureVectorAgentConfig(vectorCR *vectorv1alpha1.Vector, rclient client.Client) (bool, ctrl.Result, error) { + ctx := context.Background() + log := log.FromContext(ctx).WithValues("vector-agent-secret", vectorCR.Name) + + log.Info("start Reconcile Vector Agent Secret") + + vectorAgentSecret, err := createVectorAgentConfig(ctx, vectorCR, rclient) + if err != nil { + return helper.ReconcileResult(err) + } + + _, err = k8sutils.CreateOrUpdateSecret(vectorAgentSecret, rclient) + + return helper.ReconcileResult(err) +} + +func ensureVectorAgentDaemonSet(vectorCR *vectorv1alpha1.Vector, rclient client.Client) (bool, ctrl.Result, error) { + ctx := context.Background() + log := log.FromContext(ctx).WithValues("vector-agent-daemon-set", vectorCR.Name) + + log.Info("start Reconcile Vector Agent DaemonSet") + + vectorAgentDaemonSet := createVectorAgentDaemonSet(vectorCR) + + _, err := k8sutils.CreateOrUpdateDaemonSet(vectorAgentDaemonSet, rclient) + + return helper.ReconcileResult(err) +} + +func labelsForVectorAgent(name string) map[string]string { + return map[string]string{ + label.ManagedByLabelKey: "vector-operator", + label.NameLabelKey: "vector", + label.ComponentLabelKey: "Agent", + label.InstanceLabelKey: name, + label.VectorExcludeLabel: "true", + } +} + +func objectMetaVectorAgent(v *vectorv1alpha1.Vector, labels map[string]string) metav1.ObjectMeta { + return metav1.ObjectMeta{ + Name: v.Name + "-agent", + Namespace: v.Namespace, + Labels: labels, + OwnerReferences: getControllerReference(v), + } +} + +func getNameVectorAgent(v *vectorv1alpha1.Vector) string { + name := v.Name + "-agent" + return name +} + +func getControllerReference(owner *vectorv1alpha1.Vector) []metav1.OwnerReference { + return []metav1.OwnerReference{ + { + APIVersion: owner.APIVersion, + Kind: owner.Kind, + Name: owner.GetName(), + UID: owner.GetUID(), + BlockOwnerDeletion: pointer.BoolPtr(true), + Controller: pointer.BoolPtr(true), + }, + } +} diff --git a/controllers/vector_agent_daemonset.go b/controllers/factory/vectoragent/vector_agent_daemonset.go similarity index 95% rename from controllers/vector_agent_daemonset.go rename to controllers/factory/vectoragent/vector_agent_daemonset.go index 9a83ec75..57309d29 100644 --- a/controllers/vector_agent_daemonset.go +++ b/controllers/factory/vectoragent/vector_agent_daemonset.go @@ -1,4 +1,4 @@ -package controllers +package vectoragent import ( appsv1 "k8s.io/api/apps/v1" @@ -8,7 +8,7 @@ import ( vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" ) -func (r *VectorReconciler) createVectorAgentDaemonSet(v *vectorv1alpha1.Vector) *appsv1.DaemonSet { +func createVectorAgentDaemonSet(v *vectorv1alpha1.Vector) *appsv1.DaemonSet { labels := labelsForVectorAgent(v.Name) daemonset := &appsv1.DaemonSet{ @@ -25,7 +25,7 @@ func (r *VectorReconciler) createVectorAgentDaemonSet(v *vectorv1alpha1.Vector) { Name: getNameVectorAgent(v), Image: v.Spec.Agent.Image, - Args: []string{"--config-dir", "/etc/vector/"}, + Args: []string{"--config-dir", "/etc/vector/", "--watch-config"}, Env: generateVectorAgentEnvs(v), Ports: []corev1.ContainerPort{ { diff --git a/controllers/vector_agent_rbac.go b/controllers/factory/vectoragent/vector_agent_rbac.go similarity index 75% rename from controllers/vector_agent_rbac.go rename to controllers/factory/vectoragent/vector_agent_rbac.go index f72137ae..6ea637b4 100644 --- a/controllers/vector_agent_rbac.go +++ b/controllers/factory/vectoragent/vector_agent_rbac.go @@ -1,4 +1,4 @@ -package controllers +package vectoragent import ( corev1 "k8s.io/api/core/v1" @@ -7,7 +7,7 @@ import ( vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" ) -func (r *VectorReconciler) createVectorAgentServiceAccount(v *vectorv1alpha1.Vector) *corev1.ServiceAccount { +func createVectorAgentServiceAccount(v *vectorv1alpha1.Vector) *corev1.ServiceAccount { labels := labelsForVectorAgent(v.Name) serviceAccount := &corev1.ServiceAccount{ @@ -17,7 +17,7 @@ func (r *VectorReconciler) createVectorAgentServiceAccount(v *vectorv1alpha1.Vec return serviceAccount } -func (r *VectorReconciler) createVectorAgentClusterRole(v *vectorv1alpha1.Vector) *rbacv1.ClusterRole { +func createVectorAgentClusterRole(v *vectorv1alpha1.Vector) *rbacv1.ClusterRole { labels := labelsForVectorAgent(v.Name) clusterRole := &rbacv1.ClusterRole{ @@ -34,7 +34,7 @@ func (r *VectorReconciler) createVectorAgentClusterRole(v *vectorv1alpha1.Vector return clusterRole } -func (r *VectorReconciler) createVectorAgentClusterRoleBinding(v *vectorv1alpha1.Vector) *rbacv1.ClusterRoleBinding { +func createVectorAgentClusterRoleBinding(v *vectorv1alpha1.Vector) *rbacv1.ClusterRoleBinding { labels := labelsForVectorAgent(v.Name) clusterRoleBinding := &rbacv1.ClusterRoleBinding{ diff --git a/controllers/vector_agent_service.go b/controllers/factory/vectoragent/vector_agent_service.go similarity index 82% rename from controllers/vector_agent_service.go rename to controllers/factory/vectoragent/vector_agent_service.go index 44060865..c30909bd 100644 --- a/controllers/vector_agent_service.go +++ b/controllers/factory/vectoragent/vector_agent_service.go @@ -1,4 +1,4 @@ -package controllers +package vectoragent import ( corev1 "k8s.io/api/core/v1" @@ -7,7 +7,7 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" ) -func (r *VectorReconciler) createVectorAgentService(v *vectorv1alpha1.Vector) *corev1.Service { +func createVectorAgentService(v *vectorv1alpha1.Vector) *corev1.Service { labels := labelsForVectorAgent(v.Name) service := &corev1.Service{ diff --git a/controllers/factory/vectorpipeline/vectorpipeline.go b/controllers/factory/vectorpipeline/vectorpipeline.go new file mode 100644 index 00000000..11dc31ea --- /dev/null +++ b/controllers/factory/vectorpipeline/vectorpipeline.go @@ -0,0 +1,38 @@ +package vectorpipeline + +import ( + "context" + + vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func Select(ctx context.Context, rclient client.Client) (map[string]*vectorv1alpha1.VectorPipeline, error) { + + res := make(map[string]*vectorv1alpha1.VectorPipeline) + + var vectorPipelinesCombined []vectorv1alpha1.VectorPipeline + + objlist := vectorv1alpha1.VectorPipelineList{} + err := rclient.List(ctx, &objlist) + if err != nil { + return nil, err + } + + for _, item := range objlist.Items { + if !item.DeletionTimestamp.IsZero() { + continue + } + vectorPipelinesCombined = append(vectorPipelinesCombined, item) + } + + for _, vectorPipeline := range vectorPipelinesCombined { + m := vectorPipeline.DeepCopy() + res[generateName(&vectorPipeline)] = m + } + return res, nil +} + +func generateName(pipelineCR *vectorv1alpha1.VectorPipeline) string { + return pipelineCR.Namespace + "-" + pipelineCR.Name +} diff --git a/controllers/vector_agent_controller.go b/controllers/vector_agent_controller.go deleted file mode 100644 index 8f3dae1c..00000000 --- a/controllers/vector_agent_controller.go +++ /dev/null @@ -1,162 +0,0 @@ -package controllers - -import ( - "context" - - vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" - "github.com/kaasops/vector-operator/controllers/label" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - "sigs.k8s.io/controller-runtime/pkg/log" -) - -func (r *VectorReconciler) ensureVectorAgent(vectorCR *vectorv1alpha1.Vector) (done bool, result ctrl.Result, err error) { - ctx := context.Background() - log := log.FromContext(ctx).WithValues("vector-agent", vectorCR.Name) - - log.Info("start Reconcile Vector Agent") - - if done, result, err = r.ensureVectorAgentRBAC(vectorCR); done { - return - } - - if vectorCR.Spec.Agent.Service { - if done, result, err = r.ensureVectorAgentService(vectorCR); done { - return - } - } - - if done, result, err = r.ensureVectorAgentSecret(vectorCR); done { - return - } - - if done, result, err = r.ensureVectorAgentDaemonSet(vectorCR); done { - return - } - - return -} - -func (r *VectorReconciler) ensureVectorAgentRBAC(vectorCR *vectorv1alpha1.Vector) (bool, ctrl.Result, error) { - ctx := context.Background() - log := log.FromContext(ctx).WithValues("vector-agent-rbac", vectorCR.Name) - - log.Info("start Reconcile Vector Agent RBAC") - - if done, _, err := r.ensureVectorAgentServiceAccount(vectorCR); done { - return ReconcileResult(err) - } - if done, _, err := r.ensureVectorAgentClusterRole(vectorCR); done { - return ReconcileResult(err) - } - if done, _, err := r.ensureVectorAgentClusterRoleBinding(vectorCR); done { - return ReconcileResult(err) - } - - return ReconcileResult(nil) -} - -func (r *VectorReconciler) ensureVectorAgentServiceAccount(vectorCR *vectorv1alpha1.Vector) (bool, ctrl.Result, error) { - vectorAgentServiceAccount := r.createVectorAgentServiceAccount(vectorCR) - - if err := controllerutil.SetControllerReference(vectorCR, vectorAgentServiceAccount, r.Scheme); err != nil { - return ReconcileResult(err) - } - _, err := r.CreateOrUpdateServiceAccount(vectorAgentServiceAccount) - - return ReconcileResult(err) -} - -func (r *VectorReconciler) ensureVectorAgentClusterRole(vectorCR *vectorv1alpha1.Vector) (bool, ctrl.Result, error) { - vectorAgentClusterRole := r.createVectorAgentClusterRole(vectorCR) - - if err := controllerutil.SetControllerReference(vectorCR, vectorAgentClusterRole, r.Scheme); err != nil { - return ReconcileResult(err) - } - _, err := r.CreateOrUpdateClusterRole(vectorAgentClusterRole) - - return ReconcileResult(err) -} - -func (r *VectorReconciler) ensureVectorAgentClusterRoleBinding(vectorCR *vectorv1alpha1.Vector) (bool, ctrl.Result, error) { - vectorAgentClusterRoleBinding := r.createVectorAgentClusterRoleBinding(vectorCR) - - if err := controllerutil.SetControllerReference(vectorCR, vectorAgentClusterRoleBinding, r.Scheme); err != nil { - return ReconcileResult(err) - } - _, err := r.CreateOrUpdateClusterRoleBinding(vectorAgentClusterRoleBinding) - - return ReconcileResult(err) -} - -func (r *VectorReconciler) ensureVectorAgentService(vectorCR *vectorv1alpha1.Vector) (bool, ctrl.Result, error) { - ctx := context.Background() - log := log.FromContext(ctx).WithValues("vector-agent-service", vectorCR.Name) - - log.Info("start Reconcile Vector Agent Service") - - vectorAgentService := r.createVectorAgentService(vectorCR) - - if err := controllerutil.SetControllerReference(vectorCR, vectorAgentService, r.Scheme); err != nil { - return ReconcileResult(err) - } - _, err := r.CreateOrUpdateService(vectorAgentService) - - return ReconcileResult(err) -} - -func (r *VectorReconciler) ensureVectorAgentSecret(vectorCR *vectorv1alpha1.Vector) (bool, ctrl.Result, error) { - ctx := context.Background() - log := log.FromContext(ctx).WithValues("vector-agent-secret", vectorCR.Name) - - log.Info("start Reconcile Vector Agent Secret") - - vectorAgentSecret := r.createVectorAgentSecret(vectorCR) - - if err := controllerutil.SetControllerReference(vectorCR, vectorAgentSecret, r.Scheme); err != nil { - return ReconcileResult(err) - } - _, err := r.CreateOrUpdateSecret(vectorAgentSecret) - - return ReconcileResult(err) -} - -func (r *VectorReconciler) ensureVectorAgentDaemonSet(vectorCR *vectorv1alpha1.Vector) (bool, ctrl.Result, error) { - ctx := context.Background() - log := log.FromContext(ctx).WithValues("vector-agent-daemon-set", vectorCR.Name) - - log.Info("start Reconcile Vector Agent DaemonSet") - - vectorAgentDaemonSet := r.createVectorAgentDaemonSet(vectorCR) - - if err := controllerutil.SetControllerReference(vectorCR, vectorAgentDaemonSet, r.Scheme); err != nil { - return ReconcileResult(err) - } - _, err := r.CreateOrUpdateDaemonSet(vectorAgentDaemonSet) - - return ReconcileResult(err) -} - -func labelsForVectorAgent(name string) map[string]string { - return map[string]string{ - label.ManagedByLabelKey: "vector-operator", - label.NameLabelKey: "vector", - label.ComponentLabelKey: "Agent", - label.InstanceLabelKey: name, - label.VectorExcludeLabel: "true", - } -} - -func objectMetaVectorAgent(v *vectorv1alpha1.Vector, labels map[string]string) metav1.ObjectMeta { - return metav1.ObjectMeta{ - Name: v.Name + "-agent", - Namespace: v.Namespace, - Labels: labels, - } -} - -func getNameVectorAgent(v *vectorv1alpha1.Vector) string { - name := v.Name + "-agent" - return name -} diff --git a/controllers/vector_agent_secret.go b/controllers/vector_agent_secret.go deleted file mode 100644 index f4657efb..00000000 --- a/controllers/vector_agent_secret.go +++ /dev/null @@ -1,42 +0,0 @@ -package controllers - -import ( - corev1 "k8s.io/api/core/v1" - - vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" -) - -var vectorAgentConfig = ` -data_dir: /vector-data-dir -api: - enabled: true - address: 127.0.0.1:8686 - playground: false -sources: - kubernetes_logs: - type: kubernetes_logs -sinks: - stdout: - type: console - inputs: [kubernetes_logs] - encoding: - codec: json - vector: - type: vector - inputs: [kubernetes_logs] - address: vector-sample-aggregator:6000 -` - -func (r *VectorReconciler) createVectorAgentSecret(v *vectorv1alpha1.Vector) *corev1.Secret { - labels := labelsForVectorAgent(v.Name) - - config := map[string][]byte{ - "agent.yaml": []byte(vectorAgentConfig), - } - - secret := &corev1.Secret{ - ObjectMeta: objectMetaVectorAgent(v, labels), - Data: config, - } - return secret -} diff --git a/controllers/vector_aggregator_controller.go b/controllers/vector_aggregator_controller.go deleted file mode 100644 index ed5e2597..00000000 --- a/controllers/vector_aggregator_controller.go +++ /dev/null @@ -1,132 +0,0 @@ -package controllers - -import ( - "context" - - vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" - "github.com/kaasops/vector-operator/controllers/label" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - "sigs.k8s.io/controller-runtime/pkg/log" -) - -func (r *VectorReconciler) ensureVectorAggregator(vectorCR *vectorv1alpha1.Vector) (done bool, result ctrl.Result, err error) { - ctx := context.Background() - log := log.FromContext(ctx).WithValues("vector-aggregator", vectorCR.Name) - - log.Info("start Reconcile Vector Aggregator") - - if done, result, err = r.ensureVectorRBAC(vectorCR); done { - return - } - - if done, result, err = r.ensureVectorAggregatorService(vectorCR); done { - return - } - - if done, result, err = r.ensureVectorAggregatorSecret(vectorCR); done { - return - } - - if done, result, err = r.ensureVectorAggregatorStatefulSet(vectorCR); done { - return - } - - return -} - -func (r *VectorReconciler) ensureVectorRBAC(vectorCR *vectorv1alpha1.Vector) (bool, ctrl.Result, error) { - ctx := context.Background() - log := log.FromContext(ctx).WithValues("vector-aggregator-rbac", vectorCR.Name) - - log.Info("start Reconcile Vector Aggregator RBAC") - - if done, _, err := r.ensureVectorAggregatorServiceAccount(vectorCR); done { - return ReconcileResult(err) - } - - return ReconcileResult(nil) -} - -func (r *VectorReconciler) ensureVectorAggregatorServiceAccount(vectorCR *vectorv1alpha1.Vector) (bool, ctrl.Result, error) { - vectorAggregatorServiceAccount := r.createVectorAggregatorServiceAccount(vectorCR) - - if err := controllerutil.SetControllerReference(vectorCR, vectorAggregatorServiceAccount, r.Scheme); err != nil { - return ReconcileResult(err) - } - _, err := r.CreateOrUpdateServiceAccount(vectorAggregatorServiceAccount) - - return ReconcileResult(err) -} - -func (r *VectorReconciler) ensureVectorAggregatorService(vectorCR *vectorv1alpha1.Vector) (bool, ctrl.Result, error) { - ctx := context.Background() - log := log.FromContext(ctx).WithValues("vector-aggregator-service", vectorCR.Name) - - log.Info("start Reconcile Vector Aggregator Service") - - vectorAggregatorService := r.createVectorAggregatorService(vectorCR) - - if err := controllerutil.SetControllerReference(vectorCR, vectorAggregatorService, r.Scheme); err != nil { - return ReconcileResult(err) - } - _, err := r.CreateOrUpdateService(vectorAggregatorService) - - return ReconcileResult(err) -} - -func (r *VectorReconciler) ensureVectorAggregatorSecret(vectorCR *vectorv1alpha1.Vector) (bool, ctrl.Result, error) { - ctx := context.Background() - log := log.FromContext(ctx).WithValues("vector-aggregator-secret", vectorCR.Name) - - log.Info("start Reconcile Vector Aggregator Secret") - - vectorAggregatorSecret := r.createVectorAggregatorSecret(vectorCR) - - if err := controllerutil.SetControllerReference(vectorCR, vectorAggregatorSecret, r.Scheme); err != nil { - return ReconcileResult(err) - } - _, err := r.CreateOrUpdateSecret(vectorAggregatorSecret) - - return ReconcileResult(err) -} - -func (r *VectorReconciler) ensureVectorAggregatorStatefulSet(vectorCR *vectorv1alpha1.Vector) (bool, ctrl.Result, error) { - ctx := context.Background() - log := log.FromContext(ctx).WithValues("vector-aggregator-statefulset", vectorCR.Name) - - log.Info("start Reconcile Vector Aggregator StatefulSet") - - vectorAggregatorStatefulSet := r.createVectorAggregatorStatefulSet(vectorCR) - - if err := controllerutil.SetControllerReference(vectorCR, vectorAggregatorStatefulSet, r.Scheme); err != nil { - return ReconcileResult(err) - } - _, err := r.CreateOrUpdateStatefulSet(vectorAggregatorStatefulSet) - - return ReconcileResult(err) -} - -func labelsForVectorAggregator(name string) map[string]string { - return map[string]string{ - label.ManagedByLabelKey: "vector-operator", - label.NameLabelKey: "vector", - label.ComponentLabelKey: "Aggregator", - label.InstanceLabelKey: name, - label.VectorExcludeLabel: "true", - } -} - -func objectMetaVectorAggregator(v *vectorv1alpha1.Vector, labels map[string]string) metav1.ObjectMeta { - return metav1.ObjectMeta{ - Name: v.Name + "-aggregator", - Namespace: v.Namespace, - Labels: labels, - } -} - -func getNameVectorAggregator(v *vectorv1alpha1.Vector) string { - name := v.Name + "-aggregator" - return name -} diff --git a/controllers/vector_aggregator_rbac.go b/controllers/vector_aggregator_rbac.go deleted file mode 100644 index 67511128..00000000 --- a/controllers/vector_aggregator_rbac.go +++ /dev/null @@ -1,17 +0,0 @@ -package controllers - -import ( - corev1 "k8s.io/api/core/v1" - - vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" -) - -func (r *VectorReconciler) createVectorAggregatorServiceAccount(v *vectorv1alpha1.Vector) *corev1.ServiceAccount { - labels := labelsForVectorAggregator(v.Name) - - serviceAccount := &corev1.ServiceAccount{ - ObjectMeta: objectMetaVectorAggregator(v, labels), - } - - return serviceAccount -} diff --git a/controllers/vector_aggregator_secret.go b/controllers/vector_aggregator_secret.go deleted file mode 100644 index 60bef159..00000000 --- a/controllers/vector_aggregator_secret.go +++ /dev/null @@ -1,40 +0,0 @@ -package controllers - -import ( - corev1 "k8s.io/api/core/v1" - - vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" -) - -var vectorAggregatorConfig = ` -data_dir: /vector-data-dir -api: - enabled: true - address: 127.0.0.1:8686 - playground: false -sources: - vector: - address: 0.0.0.0:6000 - type: vector - version: "2" -sinks: - stdout: - type: console - inputs: [vector] - encoding: - codec: json -` - -func (r *VectorReconciler) createVectorAggregatorSecret(v *vectorv1alpha1.Vector) *corev1.Secret { - labels := labelsForVectorAggregator(v.Name) - - config := map[string][]byte{ - "aggregator.yaml": []byte(vectorAggregatorConfig), - } - - secret := &corev1.Secret{ - ObjectMeta: objectMetaVectorAggregator(v, labels), - Data: config, - } - return secret -} diff --git a/controllers/vector_aggregator_service.go b/controllers/vector_aggregator_service.go deleted file mode 100644 index 2e500c12..00000000 --- a/controllers/vector_aggregator_service.go +++ /dev/null @@ -1,28 +0,0 @@ -package controllers - -import ( - corev1 "k8s.io/api/core/v1" - - vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" - "k8s.io/apimachinery/pkg/util/intstr" -) - -func (r *VectorReconciler) createVectorAggregatorService(v *vectorv1alpha1.Vector) *corev1.Service { - labels := labelsForVectorAggregator(v.Name) - - service := &corev1.Service{ - ObjectMeta: objectMetaVectorAggregator(v, labels), - Spec: corev1.ServiceSpec{ - Ports: []corev1.ServicePort{ - { - Name: "vector", - Protocol: corev1.Protocol("TCP"), - Port: 6000, - TargetPort: intstr.FromInt(6000), - }, - }, - Selector: labels, - }, - } - return service -} diff --git a/controllers/vector_aggregator_statefulset.go b/controllers/vector_aggregator_statefulset.go deleted file mode 100644 index 232b7e92..00000000 --- a/controllers/vector_aggregator_statefulset.go +++ /dev/null @@ -1,82 +0,0 @@ -package controllers - -import ( - vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func (r *VectorReconciler) createVectorAggregatorStatefulSet(v *vectorv1alpha1.Vector) *appsv1.StatefulSet { - labels := labelsForVectorAggregator(v.Name) - replicas := int32(v.Spec.Aggregator.Replicas) - statefulset := &appsv1.StatefulSet{ - ObjectMeta: objectMetaVectorAggregator(v, labels), - Spec: appsv1.StatefulSetSpec{ - Selector: &metav1.LabelSelector{MatchLabels: labels}, - Replicas: &replicas, - Template: corev1.PodTemplateSpec{ - ObjectMeta: objectMetaVectorAggregator(v, labels), - Spec: corev1.PodSpec{ - ServiceAccountName: getNameVectorAggregator(v), - Volumes: generateVectorAggregatorVolume(v), - SecurityContext: &corev1.PodSecurityContext{}, - Containers: []corev1.Container{ - { - Name: getNameVectorAggregator(v), - Image: v.Spec.Aggregator.Image, - Args: []string{"--config-dir", "/etc/vector/"}, - Ports: []corev1.ContainerPort{ - { - Name: "vector", - ContainerPort: 6000, - Protocol: "TCP", - }, - }, - VolumeMounts: generateVectorAggregatorVolumeMounts(v), - SecurityContext: &corev1.SecurityContext{}, - }, - }, - }, - }, - }, - } - - return statefulset -} - -func generateVectorAggregatorVolume(v *vectorv1alpha1.Vector) []corev1.Volume { - volume := []corev1.Volume{ - { - Name: "config", - VolumeSource: corev1.VolumeSource{ - Secret: &corev1.SecretVolumeSource{ - SecretName: getNameVectorAggregator(v), - }, - }, - }, - { - Name: "data", - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, - }, - }, - } - - return volume -} - -func generateVectorAggregatorVolumeMounts(spec *vectorv1alpha1.Vector) []corev1.VolumeMount { - volumeMount := []corev1.VolumeMount{ - { - Name: "config", - MountPath: "/etc/vector/", - }, - { - Name: "data", - MountPath: "/vector-data-dir", - }, - } - - return volumeMount -} diff --git a/controllers/vector_controller.go b/controllers/vector_controller.go index 29a40413..d3c5c9a9 100644 --- a/controllers/vector_controller.go +++ b/controllers/vector_controller.go @@ -20,6 +20,7 @@ import ( "context" "time" + "github.com/kaasops/vector-operator/controllers/factory/vectoragent" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" @@ -34,6 +35,8 @@ import ( type VectorReconciler struct { client.Client Scheme *runtime.Scheme + // Config *VectorConfig + // Status *VectorPipelineReconcileStatus } //+kubebuilder:rbac:groups=observability.kaasops.io,resources=vectors,verbs=get;list;watch;create;update;patch;delete @@ -60,17 +63,11 @@ func (r *VectorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr return result, err } - if done, result, err = r.ensureVectorAgent(vectorCR); done { - return result, err + if vectorCR.Spec.Agent.DataDir == "" { + vectorCR.Spec.Agent.DataDir = "/vector-data-dir" } - if vectorCR.Spec.Aggregator.Enable { - if done, result, err = r.ensureVectorAggregator(vectorCR); done { - return result, err - } - } - - return ctrl.Result{RequeueAfter: 5 * time.Second}, nil + return CreateOrUpdateVector(ctx, vectorCR, r.Client) } func (r *VectorReconciler) findVectorCustomResourceInstance(ctx context.Context, log logr.Logger, req ctrl.Request) (*vectorv1alpha1.Vector, bool, ctrl.Result, error) { @@ -99,3 +96,11 @@ func (r *VectorReconciler) SetupWithManager(mgr ctrl.Manager) error { For(&vectorv1alpha1.Vector{}). Complete(r) } + +func CreateOrUpdateVector(ctx context.Context, vector *vectorv1alpha1.Vector, rclient client.Client) (ctrl.Result, error) { + if done, result, err := vectoragent.EnsureVectorAgent(vector, rclient); done { + return result, err + } + + return ctrl.Result{RequeueAfter: 60 * time.Second}, nil +} diff --git a/controllers/vectorpipeline_controller.go b/controllers/vectorpipeline_controller.go index 13f4b773..11c83e69 100644 --- a/controllers/vectorpipeline_controller.go +++ b/controllers/vectorpipeline_controller.go @@ -24,7 +24,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" - observabilityv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" + vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" ) // VectorPipelineReconciler reconciles a VectorPipeline object @@ -48,8 +48,29 @@ type VectorPipelineReconciler struct { // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.12.2/pkg/reconcile func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { _ = log.FromContext(ctx) + log := log.FromContext(ctx).WithValues("VectorPipeline", req.NamespacedName) - // TODO(user): your logic here + log.Info("start Reconcile VectorPipeline") + + vectorInstances := &vectorv1alpha1.VectorList{} + err := r.List(ctx, vectorInstances) + if err != nil { + return ctrl.Result{}, err + } + + if len(vectorInstances.Items) == 0 { + log.Info("Vertors not found") + return ctrl.Result{}, nil + } + + for _, vector := range vectorInstances.Items { + if vector.DeletionTimestamp != nil { + continue + } + currentVector := &vector + CreateOrUpdateVector(ctx, currentVector, r.Client) + + } return ctrl.Result{}, nil } @@ -57,6 +78,6 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque // SetupWithManager sets up the controller with the Manager. func (r *VectorPipelineReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). - For(&observabilityv1alpha1.VectorPipeline{}). + For(&vectorv1alpha1.VectorPipeline{}). Complete(r) } diff --git a/go.mod b/go.mod index d981895a..f5a02560 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/go-logr/logr v1.2.0 github.com/onsi/ginkgo v1.16.5 github.com/onsi/gomega v1.18.1 + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b k8s.io/api v0.24.2 k8s.io/apimachinery v0.24.2 k8s.io/client-go v0.24.2 @@ -71,7 +72,6 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect k8s.io/apiextensions-apiserver v0.24.2 // indirect k8s.io/component-base v0.24.2 // indirect k8s.io/klog/v2 v2.60.1 // indirect From 524b6fee1f22584890a36ab8d6bf39e4b0080090 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Wed, 19 Oct 2022 17:34:05 +0300 Subject: [PATCH 006/316] Custom api for all objects (#6) * custom api for all components in vector config --- api/v1alpha1/vectorpipeline_types.go | 40 +----- api/v1alpha1/zz_generated.deepcopy.go | 128 ++--------------- ...ervability.kaasops.io_vectorpipelines.yaml | 52 +------ .../observability_v1alpha1_vector.yaml | 2 +- ...observability_v1alpha1_vectorpipeline.yaml | 21 ++- controllers/factory/vector/config_build.go | 130 +++++++++++------- controllers/factory/vector/types.go | 12 +- go.mod | 4 +- 8 files changed, 126 insertions(+), 263 deletions(-) diff --git a/api/v1alpha1/vectorpipeline_types.go b/api/v1alpha1/vectorpipeline_types.go index f7216988..8d5b4a36 100644 --- a/api/v1alpha1/vectorpipeline_types.go +++ b/api/v1alpha1/vectorpipeline_types.go @@ -18,6 +18,7 @@ package v1alpha1 import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" ) // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! @@ -25,39 +26,12 @@ import ( // VectorPipelineSpec defines the desired state of VectorPipeline type VectorPipelineSpec struct { - Source map[string]SourceSpec `json:"source,omitempty"` - Transforms map[string]TransformSpec `json:"transforms,omitempty"` - Sink map[string]SinkSpec `json:"sinks,omitempty"` -} - -type TransformSpec struct { - Type string `json:"type,omitempty"` - Inputs []string `json:"inputs,omitempty"` - Condition *Condition `json:"condition,omitempty"` -} - -type Condition struct { - Type string `json:"type,omitempty"` - Source string `json:"source,omitempty"` -} - -type SourceSpec struct { - Type string `json:"type,omitempty"` - ExtraLabelSelector string `json:"extra_label_selector,omitempty"` - ExtraFieldSelector string `json:"extra_field_selector,omitempty"` -} - -type SinkSpec struct { - Type string `json:"type,omitempty"` - Address string `json:"address,omitempty"` - Inputs []string `json:"inputs,omitempty"` - Encoding *Encoding `json:"encoding,omitempty"` - Rate *int32 `json:"rate,omitempty"` - PrintIntervalSecs int32 `json:"print_interval_secs,omitempty"` -} - -type Encoding struct { - Codec string `json:"codec,omitempty"` + // +kubebuilder:pruning:PreserveUnknownFields + Sources *runtime.RawExtension `json:"sources,omitempty"` + // +kubebuilder:pruning:PreserveUnknownFields + Transforms *runtime.RawExtension `json:"transforms,omitempty"` + // +kubebuilder:pruning:PreserveUnknownFields + Sinks *runtime.RawExtension `json:"sinks,omitempty"` } // VectorPipelineStatus defines the observed state of VectorPipeline diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index c51b2b8a..7740556c 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -22,109 +22,9 @@ limitations under the License. package v1alpha1 import ( - runtime "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime" ) -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Condition) DeepCopyInto(out *Condition) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Condition. -func (in *Condition) DeepCopy() *Condition { - if in == nil { - return nil - } - out := new(Condition) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Encoding) DeepCopyInto(out *Encoding) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Encoding. -func (in *Encoding) DeepCopy() *Encoding { - if in == nil { - return nil - } - out := new(Encoding) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SinkSpec) DeepCopyInto(out *SinkSpec) { - *out = *in - if in.Inputs != nil { - in, out := &in.Inputs, &out.Inputs - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.Encoding != nil { - in, out := &in.Encoding, &out.Encoding - *out = new(Encoding) - **out = **in - } - if in.Rate != nil { - in, out := &in.Rate, &out.Rate - *out = new(int32) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SinkSpec. -func (in *SinkSpec) DeepCopy() *SinkSpec { - if in == nil { - return nil - } - out := new(SinkSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SourceSpec) DeepCopyInto(out *SourceSpec) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SourceSpec. -func (in *SourceSpec) DeepCopy() *SourceSpec { - if in == nil { - return nil - } - out := new(SourceSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TransformSpec) DeepCopyInto(out *TransformSpec) { - *out = *in - if in.Inputs != nil { - in, out := &in.Inputs, &out.Inputs - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.Condition != nil { - in, out := &in.Condition, &out.Condition - *out = new(Condition) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TransformSpec. -func (in *TransformSpec) DeepCopy() *TransformSpec { - if in == nil { - return nil - } - out := new(TransformSpec) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Vector) DeepCopyInto(out *Vector) { *out = *in @@ -276,26 +176,20 @@ func (in *VectorPipelineList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VectorPipelineSpec) DeepCopyInto(out *VectorPipelineSpec) { *out = *in - if in.Source != nil { - in, out := &in.Source, &out.Source - *out = make(map[string]SourceSpec, len(*in)) - for key, val := range *in { - (*out)[key] = val - } + if in.Sources != nil { + in, out := &in.Sources, &out.Sources + *out = new(runtime.RawExtension) + (*in).DeepCopyInto(*out) } if in.Transforms != nil { in, out := &in.Transforms, &out.Transforms - *out = make(map[string]TransformSpec, len(*in)) - for key, val := range *in { - (*out)[key] = *val.DeepCopy() - } + *out = new(runtime.RawExtension) + (*in).DeepCopyInto(*out) } - if in.Sink != nil { - in, out := &in.Sink, &out.Sink - *out = make(map[string]SinkSpec, len(*in)) - for key, val := range *in { - (*out)[key] = *val.DeepCopy() - } + if in.Sinks != nil { + in, out := &in.Sinks, &out.Sinks + *out = new(runtime.RawExtension) + (*in).DeepCopyInto(*out) } } diff --git a/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml b/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml index c6c317f1..74844416 100644 --- a/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml +++ b/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml @@ -36,58 +36,14 @@ spec: description: VectorPipelineSpec defines the desired state of VectorPipeline properties: sinks: - additionalProperties: - properties: - address: - type: string - encoding: - properties: - codec: - type: string - type: object - inputs: - items: - type: string - type: array - print_interval_secs: - format: int32 - type: integer - rate: - format: int32 - type: integer - type: - type: string - type: object type: object - source: - additionalProperties: - properties: - extra_field_selector: - type: string - extra_label_selector: - type: string - type: - type: string - type: object + x-kubernetes-preserve-unknown-fields: true + sources: type: object + x-kubernetes-preserve-unknown-fields: true transforms: - additionalProperties: - properties: - condition: - properties: - source: - type: string - type: - type: string - type: object - inputs: - items: - type: string - type: array - type: - type: string - type: object type: object + x-kubernetes-preserve-unknown-fields: true type: object status: description: VectorPipelineStatus defines the observed state of VectorPipeline diff --git a/config/samples/observability_v1alpha1_vector.yaml b/config/samples/observability_v1alpha1_vector.yaml index b667b124..3a7845c3 100644 --- a/config/samples/observability_v1alpha1_vector.yaml +++ b/config/samples/observability_v1alpha1_vector.yaml @@ -2,7 +2,7 @@ apiVersion: observability.kaasops.io/v1alpha1 kind: Vector metadata: name: vector-sample - namespace: vector-operator-system + namespace: vector spec: agent: service: true diff --git a/config/samples/observability_v1alpha1_vectorpipeline.yaml b/config/samples/observability_v1alpha1_vectorpipeline.yaml index d56874f5..9b043123 100644 --- a/config/samples/observability_v1alpha1_vectorpipeline.yaml +++ b/config/samples/observability_v1alpha1_vectorpipeline.yaml @@ -3,25 +3,36 @@ kind: VectorPipeline metadata: name: vectorpipeline-sample spec: - source: + sources: test1: type: "kubernetes_logs" extra_label_selector: "app!=testdeployment" + test2: + type: "kubernetes_logs" + extra_label_selector: "app!=testdeployment1" transforms: - test: + test11: type: "filter" inputs: - test1 + - test2 + condition: + type: "vrl" + source: ".status != 200" + test33: + type: "filter" + inputs: + - test11 condition: type: "vrl" source: ".status != 200" sinks: - test2: + test222: type: "console" encoding: codec: "json" inputs: - - test1 + - test33 --- apiVersion: observability.kaasops.io/v1alpha1 kind: VectorPipeline @@ -29,7 +40,7 @@ metadata: name: vectorpipeline-sample1 namespace: kube-system spec: - source: + sources: test1: type: "kubernetes_logs" extra_label_selector: "app!=testdeployment2" diff --git a/controllers/factory/vector/config_build.go b/controllers/factory/vector/config_build.go index f2064bb6..98f37b7b 100644 --- a/controllers/factory/vector/config_build.go +++ b/controllers/factory/vector/config_build.go @@ -2,21 +2,23 @@ package vector import ( "encoding/json" + "log" "github.com/kaasops/vector-operator/api/v1alpha1" + "k8s.io/apimachinery/pkg/runtime" ) var ( - sourceDefault = v1alpha1.SourceSpec{ - Type: "kubernetes_logs", + sourceDefault = map[string]interface{}{ + "type": "kubernetes_logs", } rate int32 = 100 - sinkDefault = v1alpha1.SinkSpec{ - Type: "blackhole", - Inputs: []string{"defaultSource"}, - Rate: &rate, - PrintIntervalSecs: 60, + sinkDefault = map[string]interface{}{ + "type": "blackhole", + "inputs": []string{"defaultSource"}, + "rate": rate, + "printIntervalSecs": 60, } ) @@ -25,16 +27,15 @@ func GenerateConfig( vp map[string]*v1alpha1.VectorPipeline, ) ([]byte, error) { cfg := NewVectorConfig(cr.Spec.Agent.DataDir, cr.Spec.Agent.ApiEnabled) - sources, transforms, sinks := getComponents(vp) + sources, transforms, sinks, err := getComponents(vp) + if err != nil { + log.Fatal(err) + } if len(sources) == 0 { - sources = map[string]*v1alpha1.SourceSpec{ - "defaultSource": &sourceDefault, - } + sources = sourceDefault } if len(sinks) == 0 { - sinks = map[string]*v1alpha1.SinkSpec{ - "defaultSink": &sinkDefault, - } + sinks = sinkDefault } cfg.Sinks = sinks @@ -45,8 +46,8 @@ func GenerateConfig( } func NewVectorConfig(dataDir string, apiEnabled bool) *VectorConfig { - sources := make(map[string]*v1alpha1.SourceSpec) - sinks := make(map[string]*v1alpha1.SinkSpec) + sources := make(map[string]interface{}) + sinks := make(map[string]interface{}) return &VectorConfig{ DataDir: dataDir, @@ -58,50 +59,46 @@ func NewVectorConfig(dataDir string, apiEnabled bool) *VectorConfig { } } -func getComponents(vps map[string]*v1alpha1.VectorPipeline) (map[string]*v1alpha1.SourceSpec, map[string]*v1alpha1.TransformSpec, map[string]*v1alpha1.SinkSpec) { - sources := make(map[string]*v1alpha1.SourceSpec) - transforms := make(map[string]*v1alpha1.TransformSpec) - sinks := make(map[string]*v1alpha1.SinkSpec) +func getComponents(vps map[string]*v1alpha1.VectorPipeline) (map[string]interface{}, map[string]interface{}, map[string]interface{}, error) { + sources := make(map[string]interface{}) + transforms := make(map[string]interface{}) + sinks := make(map[string]interface{}) for name, vp := range vps { - for sourceName, source := range vp.Spec.Source { - sources[name+"-"+sourceName+"-source"] = &source + if vp.Spec.Sources != nil { + data, err := decode(vp.Spec.Sources) + if err != nil { + return nil, nil, nil, err + } + vp_sources := uniqWithPrefix(data, name) + for source_k, source_v := range vp_sources { + sources[source_k] = source_v + } } - - for sinkName, sink := range vp.Spec.Sink { - inputs := make([]string, 0) - for _, i := range sink.Inputs { - newInput := name + "-" + i + "-source" - inputs = append(inputs, newInput) + if vp.Spec.Transforms != nil { + data, err := decode(vp.Spec.Transforms) + if err != nil { + return nil, nil, nil, err + } + vp_transforms := uniqWithPrefix(data, name) + for transform_k, transform_v := range vp_transforms { + transforms[transform_k] = transform_v } - - sink.Inputs = inputs - sinks[name+"-"+sinkName+"-source"] = &sink } - - for transformName, transform := range vp.Spec.Transforms { - inputs := make([]string, 0) - for _, i := range transform.Inputs { - newInput := name + "-" + i + "-source" - inputs = append(inputs, newInput) + if vp.Spec.Sinks != nil { + data, err := decode(vp.Spec.Sinks) + if err != nil { + return nil, nil, nil, err + } + vp_sinks := uniqWithPrefix(data, name) + for sink_k, sink_v := range vp_sinks { + sinks[sink_k] = sink_v } - - transform.Inputs = inputs - transforms[name+"-"+transformName+"-transform"] = &transform - } } - return sources, transforms, sinks + return sources, transforms, sinks, nil } -// func createKeyValuePairs(m map[string]string) string { -// b := new(bytes.Buffer) -// for key, value := range m { -// fmt.Fprintf(b, "%s=\"%s\",", key, value) -// } -// return b.String() -// } - func vectorConfigToJson(conf *VectorConfig) ([]byte, error) { data, err := json.Marshal(conf) if err != nil { @@ -110,3 +107,36 @@ func vectorConfigToJson(conf *VectorConfig) ([]byte, error) { return data, nil } + +func uniqWithPrefix(in map[string]interface{}, prefix string) map[string]interface{} { + out := make(map[string]interface{}) + for k_in, v_in := range in { + spec := v_in.(map[string]interface{}) + out[addPrefix(prefix, k_in)] = spec + for k_spec, v_spec := range spec { + if k_spec == "inputs" { + inputs := make([]string, 0) + for _, i := range v_spec.([]interface{}) { + newInput := addPrefix(prefix, i.(string)) + inputs = append(inputs, newInput) + } + spec[k_spec] = inputs + continue + } + } + } + return out +} + +func addPrefix(prefix string, name string) string { + return prefix + "-" + name +} + +func decode(data *runtime.RawExtension) (map[string]interface{}, error) { + out := make(map[string]interface{}) + err := json.Unmarshal(data.Raw, &out) + if err != nil { + return nil, err + } + return out, nil +} diff --git a/controllers/factory/vector/types.go b/controllers/factory/vector/types.go index 6440c56f..c273d9b6 100644 --- a/controllers/factory/vector/types.go +++ b/controllers/factory/vector/types.go @@ -1,13 +1,11 @@ package vector -import "github.com/kaasops/vector-operator/api/v1alpha1" - type VectorConfig struct { - DataDir string `json:"data_dir,omitempty"` - Api *ApiSpec `json:"api,omitempty"` - Sources map[string]*v1alpha1.SourceSpec `json:"sources,omitempty"` - Transforms map[string]*v1alpha1.TransformSpec `json:"transforms,omitempty"` - Sinks map[string]*v1alpha1.SinkSpec `json:"sinks,omitempty"` + DataDir string `json:"data_dir,omitempty"` + Api *ApiSpec `json:"api,omitempty"` + Sources map[string]interface{} `json:"sources,omitempty"` + Transforms map[string]interface{} `json:"transforms,omitempty"` + Sinks map[string]interface{} `json:"sinks,omitempty"` } type ApiSpec struct { diff --git a/go.mod b/go.mod index f5a02560..3eb591f8 100644 --- a/go.mod +++ b/go.mod @@ -6,10 +6,10 @@ require ( github.com/go-logr/logr v1.2.0 github.com/onsi/ginkgo v1.16.5 github.com/onsi/gomega v1.18.1 - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b k8s.io/api v0.24.2 k8s.io/apimachinery v0.24.2 k8s.io/client-go v0.24.2 + k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 sigs.k8s.io/controller-runtime v0.12.2 ) @@ -72,11 +72,11 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect k8s.io/apiextensions-apiserver v0.24.2 // indirect k8s.io/component-base v0.24.2 // indirect k8s.io/klog/v2 v2.60.1 // indirect k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42 // indirect - k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect sigs.k8s.io/yaml v1.3.0 // indirect From c2bc376e6378380eccf3952cf87a2fe93b69ed1a Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Thu, 20 Oct 2022 09:15:03 +0300 Subject: [PATCH 007/316] disable cache for K8s resources Signed-off-by: Zemtsov Vladimir --- main.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/main.go b/main.go index 5bed1890..ac07ebd3 100644 --- a/main.go +++ b/main.go @@ -22,7 +22,10 @@ import ( // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) // to ensure that exec-entrypoint and run can make use of them. + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" _ "k8s.io/client-go/plugin/pkg/client/auth" + "sigs.k8s.io/controller-runtime/pkg/client" "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" @@ -72,6 +75,8 @@ func main() { HealthProbeBindAddress: probeAddr, LeaderElection: enableLeaderElection, LeaderElectionID: "79cbe7f3.kaasops.io", + ClientDisableCacheFor: []client.Object{&corev1.Secret{}, &corev1.ConfigMap{}, &corev1.Pod{}, &appsv1.Deployment{}, + &appsv1.StatefulSet{}}, // LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily // when the Manager ends. This requires the binary to immediately end when the // Manager is stopped, otherwise, this setting is unsafe. Setting this significantly From ee447a1edb37e1b28a4895e2026d0185d87455b8 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Thu, 20 Oct 2022 09:16:01 +0300 Subject: [PATCH 008/316] add vscode to gitignore Signed-off-by: Zemtsov Vladimir --- .gitignore | 1 + .vscode/launch.json | 15 --------------- 2 files changed, 1 insertion(+), 15 deletions(-) delete mode 100644 .vscode/launch.json diff --git a/.gitignore b/.gitignore index c0a7a54c..c2b8826e 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,4 @@ testbin/* *.swp *.swo *~ +.vscode \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 6deb9a6b..00000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "Launch Package", - "type": "go", - "request": "launch", - "mode": "auto", - "program": "main.go" - } - ] -} \ No newline at end of file From af1eb6cf23e1744eb972960908404adb619d495b Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Thu, 20 Oct 2022 09:17:24 +0300 Subject: [PATCH 009/316] Add K8s Tolerations to vector agent Signed-off-by: Zemtsov Vladimir --- api/v1alpha1/vector_types.go | 10 ++++++---- .../factory/vectoragent/vector_agent_daemonset.go | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/api/v1alpha1/vector_types.go b/api/v1alpha1/vector_types.go index d9704da7..3fd76030 100644 --- a/api/v1alpha1/vector_types.go +++ b/api/v1alpha1/vector_types.go @@ -17,6 +17,7 @@ limitations under the License. package v1alpha1 import ( + v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -42,10 +43,11 @@ type VectorStatus struct { // VectorAgent is the Schema for the Vector Agent type VectorAgent struct { // +kubebuilder:default:="timberio/vector:0.24.0-distroless-libc" - Image string `json:"image,omitempty"` - DataDir string `json:"dataDir,omitempty"` - ApiEnabled bool `json:"ApiEnabled,omitempty"` - Service bool `json:"service,omitempty"` + Image string `json:"image,omitempty"` + DataDir string `json:"dataDir,omitempty"` + ApiEnabled bool `json:"ApiEnabled,omitempty"` + Service bool `json:"service,omitempty"` + Tolerations []v1.Toleration `json:"tolerations,omitempty"` } // VectorAggregator is the Schema for the Vector Aggregator diff --git a/controllers/factory/vectoragent/vector_agent_daemonset.go b/controllers/factory/vectoragent/vector_agent_daemonset.go index 57309d29..f06e4804 100644 --- a/controllers/factory/vectoragent/vector_agent_daemonset.go +++ b/controllers/factory/vectoragent/vector_agent_daemonset.go @@ -21,6 +21,7 @@ func createVectorAgentDaemonSet(v *vectorv1alpha1.Vector) *appsv1.DaemonSet { ServiceAccountName: getNameVectorAgent(v), Volumes: generateVectorAgentVolume(v), SecurityContext: &corev1.PodSecurityContext{}, + Tolerations: v.Spec.Agent.Tolerations, Containers: []corev1.Container{ { Name: getNameVectorAgent(v), From 1a0dcf17c3946d2696925a582669044a3d336585 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Thu, 20 Oct 2022 09:54:48 +0300 Subject: [PATCH 010/316] Push tolerations crd in vector agent Signed-off-by: Zemtsov Vladimir --- .../observability.kaasops.io_vectors.yaml | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/config/crd/bases/observability.kaasops.io_vectors.yaml b/config/crd/bases/observability.kaasops.io_vectors.yaml index f24190e7..6ed6924c 100644 --- a/config/crd/bases/observability.kaasops.io_vectors.yaml +++ b/config/crd/bases/observability.kaasops.io_vectors.yaml @@ -48,6 +48,46 @@ spec: type: string service: type: boolean + tolerations: + items: + description: The pod this Toleration is attached to tolerates + any taint that matches the triple using + the matching operator . + properties: + effect: + description: Effect indicates the taint effect to match. + Empty means match all taint effects. When specified, allowed + values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies + to. Empty means match all taint keys. If the key is empty, + operator must be Exists; this combination means to match + all values and all keys. + type: string + operator: + description: Operator represents a key's relationship to + the value. Valid operators are Exists and Equal. Defaults + to Equal. Exists is equivalent to wildcard for value, + so that a pod can tolerate all taints of a particular + category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of + time the toleration (which must be of effect NoExecute, + otherwise this field is ignored) tolerates the taint. + By default, it is not set, which means tolerate the taint + forever (do not evict). Zero and negative values will + be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches + to. If the operator is Exists, the value should be empty, + otherwise just a regular string. + type: string + type: object + type: array type: object type: object status: From c2fa240dc184be286ea6ca31df4bc2d453bfdda0 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Sat, 22 Oct 2022 15:27:51 +0300 Subject: [PATCH 011/316] Add configcheck & Cleanup Signed-off-by: Zemtsov Vladimir --- api/v1alpha1/vector_types.go | 3 +- api/v1alpha1/vectorpipeline_types.go | 3 +- api/v1alpha1/zz_generated.deepcopy.go | 24 ++- ...ervability.kaasops.io_vectorpipelines.yaml | 3 + .../observability.kaasops.io_vectors.yaml | 3 + .../{vector => config}/config_build.go | 62 ++++--- .../factory/config/configcheck/configcheck.go | 154 ++++++++++++++++ .../config/configcheck/configcheck_config.go | 25 +++ .../config/configcheck/configcheck_pod.go | 170 ++++++++++++++++++ .../config/configcheck/configcheck_rbac.go | 20 +++ controllers/factory/k8sutils/utils.go | 21 +++ controllers/factory/vector/vector.go | 15 ++ .../vectoragent/vector_agent_config.go | 30 ++-- .../vectoragent/vector_agent_controller.go | 66 ++++--- .../factory/vectorpipeline/vectorpipeline.go | 26 ++- controllers/vector_controller.go | 24 +-- controllers/vectorpipeline_controller.go | 72 +++++++- 17 files changed, 623 insertions(+), 98 deletions(-) rename controllers/factory/{vector => config}/config_build.go (66%) create mode 100644 controllers/factory/config/configcheck/configcheck.go create mode 100644 controllers/factory/config/configcheck/configcheck_config.go create mode 100644 controllers/factory/config/configcheck/configcheck_pod.go create mode 100644 controllers/factory/config/configcheck/configcheck_rbac.go create mode 100644 controllers/factory/vector/vector.go diff --git a/api/v1alpha1/vector_types.go b/api/v1alpha1/vector_types.go index 3fd76030..c955b8bf 100644 --- a/api/v1alpha1/vector_types.go +++ b/api/v1alpha1/vector_types.go @@ -36,8 +36,7 @@ type VectorSpec struct { // VectorStatus defines the observed state of Vector type VectorStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file + ConfigCheckResult *bool `json:"configCheckResult,omitempty"` } // VectorAgent is the Schema for the Vector Agent diff --git a/api/v1alpha1/vectorpipeline_types.go b/api/v1alpha1/vectorpipeline_types.go index 8d5b4a36..7bd66bdc 100644 --- a/api/v1alpha1/vectorpipeline_types.go +++ b/api/v1alpha1/vectorpipeline_types.go @@ -36,8 +36,7 @@ type VectorPipelineSpec struct { // VectorPipelineStatus defines the observed state of VectorPipeline type VectorPipelineStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file + ConfigCheckResult *bool `json:"configCheckResult,omitempty"` } //+kubebuilder:object:root=true diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 7740556c..b72ba00d 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -22,6 +22,7 @@ limitations under the License. package v1alpha1 import ( + "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" ) @@ -31,7 +32,7 @@ func (in *Vector) DeepCopyInto(out *Vector) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status + in.Status.DeepCopyInto(&out.Status) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Vector. @@ -55,6 +56,13 @@ func (in *Vector) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VectorAgent) DeepCopyInto(out *VectorAgent) { *out = *in + if in.Tolerations != nil { + in, out := &in.Tolerations, &out.Tolerations + *out = make([]v1.Toleration, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VectorAgent. @@ -120,7 +128,7 @@ func (in *VectorPipeline) DeepCopyInto(out *VectorPipeline) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status + in.Status.DeepCopyInto(&out.Status) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VectorPipeline. @@ -206,6 +214,11 @@ func (in *VectorPipelineSpec) DeepCopy() *VectorPipelineSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VectorPipelineStatus) DeepCopyInto(out *VectorPipelineStatus) { *out = *in + if in.ConfigCheckResult != nil { + in, out := &in.ConfigCheckResult, &out.ConfigCheckResult + *out = new(bool) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VectorPipelineStatus. @@ -224,7 +237,7 @@ func (in *VectorSpec) DeepCopyInto(out *VectorSpec) { if in.Agent != nil { in, out := &in.Agent, &out.Agent *out = new(VectorAgent) - **out = **in + (*in).DeepCopyInto(*out) } } @@ -241,6 +254,11 @@ func (in *VectorSpec) DeepCopy() *VectorSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VectorStatus) DeepCopyInto(out *VectorStatus) { *out = *in + if in.ConfigCheckResult != nil { + in, out := &in.ConfigCheckResult, &out.ConfigCheckResult + *out = new(bool) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VectorStatus. diff --git a/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml b/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml index 74844416..35b81682 100644 --- a/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml +++ b/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml @@ -47,6 +47,9 @@ spec: type: object status: description: VectorPipelineStatus defines the observed state of VectorPipeline + properties: + configCheckResult: + type: boolean type: object type: object served: true diff --git a/config/crd/bases/observability.kaasops.io_vectors.yaml b/config/crd/bases/observability.kaasops.io_vectors.yaml index 6ed6924c..bcf4a695 100644 --- a/config/crd/bases/observability.kaasops.io_vectors.yaml +++ b/config/crd/bases/observability.kaasops.io_vectors.yaml @@ -92,6 +92,9 @@ spec: type: object status: description: VectorStatus defines the observed state of Vector + properties: + configCheckResult: + type: boolean type: object type: object served: true diff --git a/controllers/factory/vector/config_build.go b/controllers/factory/config/config_build.go similarity index 66% rename from controllers/factory/vector/config_build.go rename to controllers/factory/config/config_build.go index 98f37b7b..5dead386 100644 --- a/controllers/factory/vector/config_build.go +++ b/controllers/factory/config/config_build.go @@ -1,33 +1,55 @@ -package vector +package config import ( + "context" "encoding/json" "log" - "github.com/kaasops/vector-operator/api/v1alpha1" + vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" + "github.com/kaasops/vector-operator/controllers/factory/vector" + "github.com/kaasops/vector-operator/controllers/factory/vectorpipeline" "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/client" ) var ( sourceDefault = map[string]interface{}{ - "type": "kubernetes_logs", + "defaultSource": map[string]string{ + "type": "kubernetes_logs", + }, } rate int32 = 100 sinkDefault = map[string]interface{}{ - "type": "blackhole", - "inputs": []string{"defaultSource"}, - "rate": rate, - "printIntervalSecs": 60, + "defaultSink": map[string]interface{}{ + "type": "blackhole", + "inputs": []string{"defaultSource"}, + "rate": rate, + "print_interval_secs": 60, + }, } ) +func Get(ctx context.Context, v *vectorv1alpha1.Vector, c client.Client) ([]byte, error) { + vps, err := vectorpipeline.SelectSucceesed(ctx, c) + if err != nil { + return nil, err + } + + cfg, err := GenerateConfig(v, vps) + if err != nil { + return nil, err + } + + return cfg, nil +} + func GenerateConfig( - cr *v1alpha1.Vector, - vp map[string]*v1alpha1.VectorPipeline, + v *vectorv1alpha1.Vector, + vps map[string]*vectorv1alpha1.VectorPipeline, ) ([]byte, error) { - cfg := NewVectorConfig(cr.Spec.Agent.DataDir, cr.Spec.Agent.ApiEnabled) - sources, transforms, sinks, err := getComponents(vp) + cfg := vector.New(v.Spec.Agent.DataDir, v.Spec.Agent.ApiEnabled) + sources, transforms, sinks, err := getComponents(vps) if err != nil { log.Fatal(err) } @@ -45,21 +67,7 @@ func GenerateConfig( return vectorConfigToJson(cfg) } -func NewVectorConfig(dataDir string, apiEnabled bool) *VectorConfig { - sources := make(map[string]interface{}) - sinks := make(map[string]interface{}) - - return &VectorConfig{ - DataDir: dataDir, - Api: &ApiSpec{ - Enabled: &apiEnabled, - }, - Sources: sources, - Sinks: sinks, - } -} - -func getComponents(vps map[string]*v1alpha1.VectorPipeline) (map[string]interface{}, map[string]interface{}, map[string]interface{}, error) { +func getComponents(vps map[string]*vectorv1alpha1.VectorPipeline) (map[string]interface{}, map[string]interface{}, map[string]interface{}, error) { sources := make(map[string]interface{}) transforms := make(map[string]interface{}) sinks := make(map[string]interface{}) @@ -99,7 +107,7 @@ func getComponents(vps map[string]*v1alpha1.VectorPipeline) (map[string]interfac return sources, transforms, sinks, nil } -func vectorConfigToJson(conf *VectorConfig) ([]byte, error) { +func vectorConfigToJson(conf *vector.VectorConfig) ([]byte, error) { data, err := json.Marshal(conf) if err != nil { return nil, err diff --git a/controllers/factory/config/configcheck/configcheck.go b/controllers/factory/config/configcheck/configcheck.go new file mode 100644 index 00000000..c181f291 --- /dev/null +++ b/controllers/factory/config/configcheck/configcheck.go @@ -0,0 +1,154 @@ +package configcheck + +import ( + "context" + "errors" + "math/rand" + "time" + + "github.com/kaasops/vector-operator/controllers/factory/helper" + "github.com/kaasops/vector-operator/controllers/factory/k8sutils" + "github.com/kaasops/vector-operator/controllers/factory/label" + corev1 "k8s.io/api/core/v1" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" +) + +var ( + ErrConfigCheck = errors.New("Config Check finish with errors") +) + +func Run( + cfg []byte, + c client.Client, + name, + namespace, + image string, +) error { + log := log.FromContext(context.TODO()).WithValues("Vector ConfigCheck", name) + + log.Info("start ConfigCheck Vector") + + err := ensureVectorConfigCheckRBAC(c, namespace) + if err != nil { + return err + } + + hash := randStringRunes() + + err = ensureVectorConfigCheckConfig(c, cfg, name, namespace, hash) + if err != nil { + return err + } + + err = ensureVectorConfigCheckPod(c, name, namespace, image, hash) + if err != nil { + return err + } + + return nil +} + +func ensureVectorConfigCheckRBAC(c client.Client, ns string) error { + // ctx := context.Background() + // log := log.FromContext(ctx).WithValues("vector-config-check-rbac", "ConfigCheck") + + // log.Info("start Reconcile Vector Config Check RBAC") + + if done, _, err := ensureVectorConfigCheckServiceAccount(c, ns); done { + return err + } + + return nil +} + +func ensureVectorConfigCheckServiceAccount(c client.Client, ns string) (bool, ctrl.Result, error) { + vectorAgentServiceAccount := createVectorConfigCheckServiceAccount(ns) + + _, err := k8sutils.CreateOrUpdateServiceAccount(vectorAgentServiceAccount, c) + + return helper.ReconcileResult(err) +} +func ensureVectorConfigCheckConfig(c client.Client, cfg []byte, name, ns, hash string) error { + // ctx := context.Background() + // log := log.FromContext(ctx).WithValues("vector-config-check-secret", "ConfigCheck") + + // log.Info("start Create Config Check Secret") + + vectorConfigCheckSecret, err := createVectorConfigCheckConfig(cfg, name, ns, hash) + if err != nil { + return err + } + + _, err = k8sutils.CreateOrUpdateSecret(vectorConfigCheckSecret, c) + + return err +} + +func ensureVectorConfigCheckPod(c client.Client, name, ns, image, hash string) error { + // ctx := context.Background() + // log := log.FromContext(ctx).WithValues("vector-config-check-pod", "ConfigCheck") + + // log.Info("start Vector Config Check Pod") + + vectorConfigCheckPod := createVectorConfigCheckPod(name, ns, image, hash) + + err := k8sutils.CreatePod(vectorConfigCheckPod, c) + if err != nil { + return err + } + + err = getCheckResult(vectorConfigCheckPod, c) + if err != nil { + return err + } + + return nil +} + +func labelsForVectorConfigCheck() map[string]string { + return map[string]string{ + label.ManagedByLabelKey: "vector-operator", + label.NameLabelKey: "vector-configcheck", + label.ComponentLabelKey: "ConfigCheck", + label.VectorExcludeLabel: "true", + } +} + +func getNameVectorConfigCheck(name, hash string) string { + n := "configcheck-" + name + "-" + hash + return n +} + +func randStringRunes() string { + var letterRunes = []rune("abcdefghijklmnopqrstuvwxyz") + + b := make([]rune, 5) + for i := range b { + b[i] = letterRunes[rand.Intn(len(letterRunes))] + } + return string(b) +} + +func getCheckResult(pod *corev1.Pod, c client.Client) error { + log := log.FromContext(context.TODO()).WithValues("Vector ConfigCheck", pod.Name) + + for { + existing, err := k8sutils.GetPod(pod, c) + if err != nil { + return err + } + + switch existing.Status.Phase { + case "Pending": + log.Info("wait Validate Vector Config Result") + time.Sleep(5 * time.Second) + case "Failed": + return ErrConfigCheck + case "Succeeded": + log.Info("Config Check completed successfully") + return nil + } + } +} diff --git a/controllers/factory/config/configcheck/configcheck_config.go b/controllers/factory/config/configcheck/configcheck_config.go new file mode 100644 index 00000000..85a0a0e1 --- /dev/null +++ b/controllers/factory/config/configcheck/configcheck_config.go @@ -0,0 +1,25 @@ +package configcheck + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func createVectorConfigCheckConfig(cfg []byte, name, ns, hash string) (*corev1.Secret, error) { + labels := labelsForVectorConfigCheck() + + config := map[string][]byte{ + "agent.json": cfg, + } + + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: getNameVectorConfigCheck(name, hash), + Namespace: ns, + Labels: labels, + }, + Data: config, + } + + return secret, nil +} diff --git a/controllers/factory/config/configcheck/configcheck_pod.go b/controllers/factory/config/configcheck/configcheck_pod.go new file mode 100644 index 00000000..8fe883b5 --- /dev/null +++ b/controllers/factory/config/configcheck/configcheck_pod.go @@ -0,0 +1,170 @@ +package configcheck + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func createVectorConfigCheckPod(name, ns, image, hash string) *corev1.Pod { + labels := labelsForVectorConfigCheck() + + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: getNameVectorConfigCheck(name, hash), + Namespace: ns, + Labels: labels, + }, + Spec: corev1.PodSpec{ + ServiceAccountName: "vector-configcheck", + Volumes: generateVectorConfigCheckVolume(name, hash), + SecurityContext: &corev1.PodSecurityContext{}, + Containers: []corev1.Container{ + { + Name: "config-check", + Image: image, + Args: []string{"validate", "/etc/vector/*.json"}, + Env: generateVectorConfigCheckEnvs(), + Ports: []corev1.ContainerPort{ + { + Name: "prom-exporter", + ContainerPort: 9090, + Protocol: "TCP", + }, + }, + VolumeMounts: generateVectorConfigCheckVolumeMounts(), + }, + }, + RestartPolicy: "Never", + }, + } + + return pod +} + +func generateVectorConfigCheckVolume(name, hash string) []corev1.Volume { + volume := []corev1.Volume{ + { + Name: "config", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: getNameVectorConfigCheck(name, hash), + }, + }, + }, + { + Name: "data", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/var/lib/vector", + }, + }, + }, + { + Name: "var-log", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/var/log/", + }, + }, + }, + { + Name: "var-lib", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/var/lib/", + }, + }, + }, + { + Name: "procfs", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/proc", + }, + }, + }, + { + Name: "sysfs", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/sys", + }, + }, + }, + } + + return volume +} + +func generateVectorConfigCheckVolumeMounts() []corev1.VolumeMount { + volumeMount := []corev1.VolumeMount{ + { + Name: "config", + MountPath: "/etc/vector/", + }, + { + Name: "data", + MountPath: "/vector-data-dir", + }, + { + Name: "var-log", + MountPath: "/var/log/", + }, + { + Name: "var-lib", + MountPath: "/var/lib/", + }, + { + Name: "procfs", + MountPath: "/host/proc", + }, + { + Name: "sysfs", + MountPath: "/host/sys", + }, + } + + return volumeMount +} + +func generateVectorConfigCheckEnvs() []corev1.EnvVar { + envs := []corev1.EnvVar{ + { + Name: "VECTOR_SELF_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + APIVersion: "v1", + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "VECTOR_SELF_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + APIVersion: "v1", + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "VECTOR_SELF_POD_NAMESPACE", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + APIVersion: "v1", + FieldPath: "metadata.namespace", + }, + }, + }, + { + Name: "PROCFS_ROOT", + Value: "/host/proc", + }, + { + Name: "SYSFS_ROOT", + Value: "/host/sys", + }, + } + + return envs +} diff --git a/controllers/factory/config/configcheck/configcheck_rbac.go b/controllers/factory/config/configcheck/configcheck_rbac.go new file mode 100644 index 00000000..84d06b35 --- /dev/null +++ b/controllers/factory/config/configcheck/configcheck_rbac.go @@ -0,0 +1,20 @@ +package configcheck + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func createVectorConfigCheckServiceAccount(ns string) *corev1.ServiceAccount { + labels := labelsForVectorConfigCheck() + + serviceAccount := &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vector-configcheck", + Namespace: ns, + Labels: labels, + }, + } + + return serviceAccount +} diff --git a/controllers/factory/k8sutils/utils.go b/controllers/factory/k8sutils/utils.go index d34c444e..75c70932 100644 --- a/controllers/factory/k8sutils/utils.go +++ b/controllers/factory/k8sutils/utils.go @@ -41,6 +41,27 @@ func CreateOrUpdateClusterRoleBinding(secret *rbacv1.ClusterRoleBinding, c clien return reconcileClusterRoleBinding(secret, c) } +func CreatePod(pod *corev1.Pod, c client.Client) error { + err := c.Create(context.TODO(), pod) + if err != nil { + return err + } + return nil +} + +func GetPod(pod *corev1.Pod, c client.Client) (*corev1.Pod, error) { + result := &corev1.Pod{} + err := c.Get(context.TODO(), client.ObjectKeyFromObject(pod), result) + if err != nil { + return nil, err + } + return result, nil +} + +func UpdateStatus(ctx context.Context, obj client.Object, c client.Client) error { + return c.Status().Update(ctx, obj) +} + func reconcileService(obj runtime.Object, c client.Client) (*reconcile.Result, error) { existing := &corev1.Service{} diff --git a/controllers/factory/vector/vector.go b/controllers/factory/vector/vector.go new file mode 100644 index 00000000..29070e88 --- /dev/null +++ b/controllers/factory/vector/vector.go @@ -0,0 +1,15 @@ +package vector + +func New(dataDir string, apiEnabled bool) *VectorConfig { + sources := make(map[string]interface{}) + sinks := make(map[string]interface{}) + + return &VectorConfig{ + DataDir: dataDir, + Api: &ApiSpec{ + Enabled: &apiEnabled, + }, + Sources: sources, + Sinks: sinks, + } +} diff --git a/controllers/factory/vectoragent/vector_agent_config.go b/controllers/factory/vectoragent/vector_agent_config.go index 6e2868a4..d544725c 100644 --- a/controllers/factory/vectoragent/vector_agent_config.go +++ b/controllers/factory/vectoragent/vector_agent_config.go @@ -7,16 +7,27 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" - "github.com/kaasops/vector-operator/controllers/factory/vector" - "github.com/kaasops/vector-operator/controllers/factory/vectorpipeline" + "github.com/kaasops/vector-operator/controllers/factory/config" + "github.com/kaasops/vector-operator/controllers/factory/config/configcheck" ) func createVectorAgentConfig(ctx context.Context, v *vectorv1alpha1.Vector, c client.Client) (*corev1.Secret, error) { - cfg, err := getConfig(ctx, v, c) + cfg, err := config.Get(ctx, v, c) if err != nil { return nil, err } + err = configcheck.Run(cfg, c, v.Name, v.Namespace, v.Spec.Agent.Image) + if err == configcheck.ErrConfigCheck { + setFailedStatus(ctx, v, c) + return nil, err + } + if err != nil { + return nil, err + } + + setSucceesStatus(ctx, v, c) + labels := labelsForVectorAgent(v.Name) config := map[string][]byte{ "agent.json": cfg, @@ -29,16 +40,3 @@ func createVectorAgentConfig(ctx context.Context, v *vectorv1alpha1.Vector, c cl return secret, nil } - -func getConfig(ctx context.Context, v *vectorv1alpha1.Vector, c client.Client) ([]byte, error) { - vps, err := vectorpipeline.Select(ctx, c) - if err != nil { - return nil, err - } - cfg, err := vector.GenerateConfig(v, vps) - if err != nil { - return nil, err - } - - return cfg, nil -} diff --git a/controllers/factory/vectoragent/vector_agent_controller.go b/controllers/factory/vectoragent/vector_agent_controller.go index 68c1578d..a0fd8290 100644 --- a/controllers/factory/vectoragent/vector_agent_controller.go +++ b/controllers/factory/vectoragent/vector_agent_controller.go @@ -14,96 +14,96 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" ) -func EnsureVectorAgent(vectorCR *vectorv1alpha1.Vector, rclient client.Client) (done bool, result ctrl.Result, err error) { +func EnsureVectorAgent(v *vectorv1alpha1.Vector, rclient client.Client) (done bool, result ctrl.Result, err error) { ctx := context.Background() - log := log.FromContext(ctx).WithValues("vector-agent", vectorCR.Name) + log := log.FromContext(ctx).WithValues("vector-agent", v.Name) log.Info("start Reconcile Vector Agent") - if done, result, err = ensureVectorAgentRBAC(vectorCR, rclient); done { + if done, result, err = ensureVectorAgentRBAC(v, rclient); done { return } - if vectorCR.Spec.Agent.Service { - if done, result, err = ensureVectorAgentService(vectorCR, rclient); done { + if v.Spec.Agent.Service { + if done, result, err = ensureVectorAgentService(v, rclient); done { return } } - if done, result, err = ensureVectorAgentConfig(vectorCR, rclient); done { + if done, result, err = ensureVectorAgentConfig(v, rclient); done { return } - if done, result, err = ensureVectorAgentDaemonSet(vectorCR, rclient); done { + if done, result, err = ensureVectorAgentDaemonSet(v, rclient); done { return } return } -func ensureVectorAgentRBAC(vectorCR *vectorv1alpha1.Vector, rclient client.Client) (bool, ctrl.Result, error) { +func ensureVectorAgentRBAC(v *vectorv1alpha1.Vector, rclient client.Client) (bool, ctrl.Result, error) { ctx := context.Background() - log := log.FromContext(ctx).WithValues("vector-agent-rbac", vectorCR.Name) + log := log.FromContext(ctx).WithValues("vector-agent-rbac", v.Name) log.Info("start Reconcile Vector Agent RBAC") - if done, _, err := ensureVectorAgentServiceAccount(vectorCR, rclient); done { + if done, _, err := ensureVectorAgentServiceAccount(v, rclient); done { return helper.ReconcileResult(err) } - if done, _, err := ensureVectorAgentClusterRole(vectorCR, rclient); done { + if done, _, err := ensureVectorAgentClusterRole(v, rclient); done { return helper.ReconcileResult(err) } - if done, _, err := ensureVectorAgentClusterRoleBinding(vectorCR, rclient); done { + if done, _, err := ensureVectorAgentClusterRoleBinding(v, rclient); done { return helper.ReconcileResult(err) } return helper.ReconcileResult(nil) } -func ensureVectorAgentServiceAccount(vectorCR *vectorv1alpha1.Vector, rclient client.Client) (bool, ctrl.Result, error) { - vectorAgentServiceAccount := createVectorAgentServiceAccount(vectorCR) +func ensureVectorAgentServiceAccount(v *vectorv1alpha1.Vector, rclient client.Client) (bool, ctrl.Result, error) { + vectorAgentServiceAccount := createVectorAgentServiceAccount(v) _, err := k8sutils.CreateOrUpdateServiceAccount(vectorAgentServiceAccount, rclient) return helper.ReconcileResult(err) } -func ensureVectorAgentClusterRole(vectorCR *vectorv1alpha1.Vector, rclient client.Client) (bool, ctrl.Result, error) { - vectorAgentClusterRole := createVectorAgentClusterRole(vectorCR) +func ensureVectorAgentClusterRole(v *vectorv1alpha1.Vector, rclient client.Client) (bool, ctrl.Result, error) { + vectorAgentClusterRole := createVectorAgentClusterRole(v) _, err := k8sutils.CreateOrUpdateClusterRole(vectorAgentClusterRole, rclient) return helper.ReconcileResult(err) } -func ensureVectorAgentClusterRoleBinding(vectorCR *vectorv1alpha1.Vector, rclient client.Client) (bool, ctrl.Result, error) { - vectorAgentClusterRoleBinding := createVectorAgentClusterRoleBinding(vectorCR) +func ensureVectorAgentClusterRoleBinding(v *vectorv1alpha1.Vector, rclient client.Client) (bool, ctrl.Result, error) { + vectorAgentClusterRoleBinding := createVectorAgentClusterRoleBinding(v) _, err := k8sutils.CreateOrUpdateClusterRoleBinding(vectorAgentClusterRoleBinding, rclient) return helper.ReconcileResult(err) } -func ensureVectorAgentService(vectorCR *vectorv1alpha1.Vector, rclient client.Client) (bool, ctrl.Result, error) { +func ensureVectorAgentService(v *vectorv1alpha1.Vector, rclient client.Client) (bool, ctrl.Result, error) { ctx := context.Background() - log := log.FromContext(ctx).WithValues("vector-agent-service", vectorCR.Name) + log := log.FromContext(ctx).WithValues("vector-agent-service", v.Name) log.Info("start Reconcile Vector Agent Service") - vectorAgentService := createVectorAgentService(vectorCR) + vectorAgentService := createVectorAgentService(v) _, err := k8sutils.CreateOrUpdateService(vectorAgentService, rclient) return helper.ReconcileResult(err) } -func ensureVectorAgentConfig(vectorCR *vectorv1alpha1.Vector, rclient client.Client) (bool, ctrl.Result, error) { +func ensureVectorAgentConfig(v *vectorv1alpha1.Vector, rclient client.Client) (bool, ctrl.Result, error) { ctx := context.Background() - log := log.FromContext(ctx).WithValues("vector-agent-secret", vectorCR.Name) + log := log.FromContext(ctx).WithValues("vector-agent-secret", v.Name) log.Info("start Reconcile Vector Agent Secret") - vectorAgentSecret, err := createVectorAgentConfig(ctx, vectorCR, rclient) + vectorAgentSecret, err := createVectorAgentConfig(ctx, v, rclient) if err != nil { return helper.ReconcileResult(err) } @@ -113,13 +113,13 @@ func ensureVectorAgentConfig(vectorCR *vectorv1alpha1.Vector, rclient client.Cli return helper.ReconcileResult(err) } -func ensureVectorAgentDaemonSet(vectorCR *vectorv1alpha1.Vector, rclient client.Client) (bool, ctrl.Result, error) { +func ensureVectorAgentDaemonSet(v *vectorv1alpha1.Vector, rclient client.Client) (bool, ctrl.Result, error) { ctx := context.Background() - log := log.FromContext(ctx).WithValues("vector-agent-daemon-set", vectorCR.Name) + log := log.FromContext(ctx).WithValues("vector-agent-daemon-set", v.Name) log.Info("start Reconcile Vector Agent DaemonSet") - vectorAgentDaemonSet := createVectorAgentDaemonSet(vectorCR) + vectorAgentDaemonSet := createVectorAgentDaemonSet(v) _, err := k8sutils.CreateOrUpdateDaemonSet(vectorAgentDaemonSet, rclient) @@ -162,3 +162,15 @@ func getControllerReference(owner *vectorv1alpha1.Vector) []metav1.OwnerReferenc }, } } + +func setSucceesStatus(ctx context.Context, vp *vectorv1alpha1.Vector, c client.Client) { + var status = true + vp.Status.ConfigCheckResult = &status + k8sutils.UpdateStatus(ctx, vp, c) +} + +func setFailedStatus(ctx context.Context, vp *vectorv1alpha1.Vector, c client.Client) { + var status = false + vp.Status.ConfigCheckResult = &status + k8sutils.UpdateStatus(ctx, vp, c) +} diff --git a/controllers/factory/vectorpipeline/vectorpipeline.go b/controllers/factory/vectorpipeline/vectorpipeline.go index 11dc31ea..65d513ba 100644 --- a/controllers/factory/vectorpipeline/vectorpipeline.go +++ b/controllers/factory/vectorpipeline/vectorpipeline.go @@ -4,10 +4,11 @@ import ( "context" vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" + "github.com/kaasops/vector-operator/controllers/factory/k8sutils" "sigs.k8s.io/controller-runtime/pkg/client" ) -func Select(ctx context.Context, rclient client.Client) (map[string]*vectorv1alpha1.VectorPipeline, error) { +func SelectSucceesed(ctx context.Context, rclient client.Client) (map[string]*vectorv1alpha1.VectorPipeline, error) { res := make(map[string]*vectorv1alpha1.VectorPipeline) @@ -23,7 +24,12 @@ func Select(ctx context.Context, rclient client.Client) (map[string]*vectorv1alp if !item.DeletionTimestamp.IsZero() { continue } - vectorPipelinesCombined = append(vectorPipelinesCombined, item) + if item.Status.ConfigCheckResult != nil { + if *item.Status.ConfigCheckResult { + vectorPipelinesCombined = append(vectorPipelinesCombined, item) + } + } + } for _, vectorPipeline := range vectorPipelinesCombined { @@ -33,6 +39,18 @@ func Select(ctx context.Context, rclient client.Client) (map[string]*vectorv1alp return res, nil } -func generateName(pipelineCR *vectorv1alpha1.VectorPipeline) string { - return pipelineCR.Namespace + "-" + pipelineCR.Name +func generateName(vp *vectorv1alpha1.VectorPipeline) string { + return vp.Namespace + "-" + vp.Name +} + +func SetSucceesStatus(ctx context.Context, vp *vectorv1alpha1.VectorPipeline, c client.Client) { + var status = true + vp.Status.ConfigCheckResult = &status + k8sutils.UpdateStatus(ctx, vp, c) +} + +func SetFailedStatus(ctx context.Context, vp *vectorv1alpha1.VectorPipeline, c client.Client) { + var status = false + vp.Status.ConfigCheckResult = &status + k8sutils.UpdateStatus(ctx, vp, c) } diff --git a/controllers/vector_controller.go b/controllers/vector_controller.go index d3c5c9a9..77c5a947 100644 --- a/controllers/vector_controller.go +++ b/controllers/vector_controller.go @@ -58,22 +58,22 @@ func (r *VectorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr log.Info("start Reconcile Vector") - vectorCR, done, result, err := r.findVectorCustomResourceInstance(ctx, log, req) + v, done, result, err := r.findVectorCustomResourceInstance(ctx, log, req) if done { return result, err } - if vectorCR.Spec.Agent.DataDir == "" { - vectorCR.Spec.Agent.DataDir = "/vector-data-dir" + if v.Spec.Agent.DataDir == "" { + v.Spec.Agent.DataDir = "/vector-data-dir" } - return CreateOrUpdateVector(ctx, vectorCR, r.Client) + return CreateOrUpdateVector(ctx, v, r.Client) } func (r *VectorReconciler) findVectorCustomResourceInstance(ctx context.Context, log logr.Logger, req ctrl.Request) (*vectorv1alpha1.Vector, bool, ctrl.Result, error) { // fetch the master instance - vectorCR := &vectorv1alpha1.Vector{} - err := r.Get(ctx, req.NamespacedName, vectorCR) + v := &vectorv1alpha1.Vector{} + err := r.Get(ctx, req.NamespacedName, v) if err != nil { if errors.IsNotFound(err) { // Request object not found, could have been deleted after reconcile request. @@ -83,11 +83,11 @@ func (r *VectorReconciler) findVectorCustomResourceInstance(ctx context.Context, return nil, true, ctrl.Result{}, nil } // Error reading the object - requeue the request. - log.Error(err, "Failed to get VectorCR") + log.Error(err, "Failed to get Vector") return nil, true, ctrl.Result{}, err } - log.Info("Get Vector " + vectorCR.Name) - return vectorCR, false, ctrl.Result{}, nil + + return v, false, ctrl.Result{}, nil } // SetupWithManager sets up the controller with the Manager. @@ -97,10 +97,10 @@ func (r *VectorReconciler) SetupWithManager(mgr ctrl.Manager) error { Complete(r) } -func CreateOrUpdateVector(ctx context.Context, vector *vectorv1alpha1.Vector, rclient client.Client) (ctrl.Result, error) { - if done, result, err := vectoragent.EnsureVectorAgent(vector, rclient); done { +func CreateOrUpdateVector(ctx context.Context, v *vectorv1alpha1.Vector, rclient client.Client) (ctrl.Result, error) { + if done, result, err := vectoragent.EnsureVectorAgent(v, rclient); done { return result, err } - return ctrl.Result{RequeueAfter: 60 * time.Second}, nil + return ctrl.Result{RequeueAfter: 15 * time.Second}, nil } diff --git a/controllers/vectorpipeline_controller.go b/controllers/vectorpipeline_controller.go index 11c83e69..52102e48 100644 --- a/controllers/vectorpipeline_controller.go +++ b/controllers/vectorpipeline_controller.go @@ -19,12 +19,17 @@ package controllers import ( "context" + "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" + "github.com/go-logr/logr" vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" + "github.com/kaasops/vector-operator/controllers/factory/config" + "github.com/kaasops/vector-operator/controllers/factory/config/configcheck" + "github.com/kaasops/vector-operator/controllers/factory/vectorpipeline" ) // VectorPipelineReconciler reconciles a VectorPipeline object @@ -52,8 +57,13 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque log.Info("start Reconcile VectorPipeline") + vp, done, result, err := r.findVectorCustomResourceInstance(ctx, log, req) + if done { + return result, err + } + vectorInstances := &vectorv1alpha1.VectorList{} - err := r.List(ctx, vectorInstances) + err = r.List(ctx, vectorInstances) if err != nil { return ctrl.Result{}, err } @@ -63,21 +73,73 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque return ctrl.Result{}, nil } - for _, vector := range vectorInstances.Items { - if vector.DeletionTimestamp != nil { + for _, v := range vectorInstances.Items { + if v.DeletionTimestamp != nil { continue } - currentVector := &vector - CreateOrUpdateVector(ctx, currentVector, r.Client) + + err = checkConfig(ctx, &v, vp, r.Client) + if err != nil { + return ctrl.Result{}, err + } } + log.Info("finish Reconcile VectorPipeline") return ctrl.Result{}, nil } +func (r *VectorPipelineReconciler) findVectorCustomResourceInstance(ctx context.Context, log logr.Logger, req ctrl.Request) (*vectorv1alpha1.VectorPipeline, bool, ctrl.Result, error) { + // fetch the master instance + vp := &vectorv1alpha1.VectorPipeline{} + err := r.Get(ctx, req.NamespacedName, vp) + if err != nil { + if errors.IsNotFound(err) { + // Request object not found, could have been deleted after reconcile request. + // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers. + // Return and don't requeue + log.Info("VectorPipeline CR not found. Ignoring since object must be deleted") + return nil, true, ctrl.Result{}, nil + } + // Error reading the object - requeue the request. + log.Error(err, "Failed to get Vector") + return nil, true, ctrl.Result{}, err + } + log.Info("Get Vector Pipeline" + vp.Name) + return vp, false, ctrl.Result{}, nil +} + // SetupWithManager sets up the controller with the Manager. func (r *VectorPipelineReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&vectorv1alpha1.VectorPipeline{}). Complete(r) } + +func checkConfig(ctx context.Context, v *vectorv1alpha1.Vector, vp *vectorv1alpha1.VectorPipeline, c client.Client) error { + log := log.FromContext(context.TODO()).WithValues("Vector Pipeline", "ConfigCheck") + + vpName := vp.Namespace + "-" + vp.Name + vps := map[string]*vectorv1alpha1.VectorPipeline{ + vpName: vp, + } + + cfg, err := config.GenerateConfig(v, vps) + if err != nil { + return err + } + + err = configcheck.Run(cfg, c, vp.Name, vp.Namespace, v.Spec.Agent.Image) + if err == configcheck.ErrConfigCheck { + vectorpipeline.SetFailedStatus(ctx, vp, c) + log.Error(err, "Vector Config has error") + return nil + } + if err != nil { + return err + } + + vectorpipeline.SetSucceesStatus(ctx, vp, c) + + return nil +} From d16261f07838a3eb9beae269265e70b5c00ca642 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Tue, 25 Oct 2022 09:39:57 +0300 Subject: [PATCH 012/316] Parse config as structs --- controllers/factory/config/config_build.go | 87 +++++-------- controllers/factory/vector/types.go | 24 ++++ controllers/factory/vector/vector.go | 36 ++++++ .../factory/vectorpipeline/vectorpipeline.go | 115 +++++++++++++++--- controllers/vectorpipeline_controller.go | 10 +- go.mod | 1 + go.sum | 2 + 7 files changed, 195 insertions(+), 80 deletions(-) diff --git a/controllers/factory/config/config_build.go b/controllers/factory/config/config_build.go index 5dead386..281d7580 100644 --- a/controllers/factory/config/config_build.go +++ b/controllers/factory/config/config_build.go @@ -8,7 +8,6 @@ import ( vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" "github.com/kaasops/vector-operator/controllers/factory/vector" "github.com/kaasops/vector-operator/controllers/factory/vectorpipeline" - "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -46,7 +45,7 @@ func Get(ctx context.Context, v *vectorv1alpha1.Vector, c client.Client) ([]byte func GenerateConfig( v *vectorv1alpha1.Vector, - vps map[string]*vectorv1alpha1.VectorPipeline, + vps []*vectorv1alpha1.VectorPipeline, ) ([]byte, error) { cfg := vector.New(v.Spec.Agent.DataDir, v.Spec.Agent.ApiEnabled) sources, transforms, sinks, err := getComponents(vps) @@ -67,44 +66,47 @@ func GenerateConfig( return vectorConfigToJson(cfg) } -func getComponents(vps map[string]*vectorv1alpha1.VectorPipeline) (map[string]interface{}, map[string]interface{}, map[string]interface{}, error) { - sources := make(map[string]interface{}) - transforms := make(map[string]interface{}) - sinks := make(map[string]interface{}) +func getComponents(vps []*vectorv1alpha1.VectorPipeline) (map[string]interface{}, map[string]interface{}, map[string]interface{}, error) { + sourcesMap := make(map[string]interface{}) + transformsMap := make(map[string]interface{}) + sinksMap := make(map[string]interface{}) - for name, vp := range vps { - if vp.Spec.Sources != nil { - data, err := decode(vp.Spec.Sources) + for _, vp := range vps { + sources, err := vectorpipeline.GetSources(vp, nil) + if err != nil { + return nil, nil, nil, err + } + for _, source := range sources { + spec, err := vector.Decoder(source) if err != nil { return nil, nil, nil, err } - vp_sources := uniqWithPrefix(data, name) - for source_k, source_v := range vp_sources { - sources[source_k] = source_v - } + sourcesMap[source.Name] = spec + } + transforms, err := vectorpipeline.GetTransforms(vp) + if err != nil { + return nil, nil, nil, err } - if vp.Spec.Transforms != nil { - data, err := decode(vp.Spec.Transforms) + for _, transform := range transforms { + spec, err := vector.Decoder(transform) if err != nil { return nil, nil, nil, err } - vp_transforms := uniqWithPrefix(data, name) - for transform_k, transform_v := range vp_transforms { - transforms[transform_k] = transform_v - } + transformsMap[transform.Name] = spec + } + sinks, err := vectorpipeline.GetSinks(vp) + if err != nil { + return nil, nil, nil, err } - if vp.Spec.Sinks != nil { - data, err := decode(vp.Spec.Sinks) + for _, sink := range sinks { + spec, err := vector.Decoder(sink) if err != nil { return nil, nil, nil, err } - vp_sinks := uniqWithPrefix(data, name) - for sink_k, sink_v := range vp_sinks { - sinks[sink_k] = sink_v - } + sinksMap[sink.Name] = spec } } - return sources, transforms, sinks, nil + return sourcesMap, transformsMap, sinksMap, nil } func vectorConfigToJson(conf *vector.VectorConfig) ([]byte, error) { @@ -115,36 +117,3 @@ func vectorConfigToJson(conf *vector.VectorConfig) ([]byte, error) { return data, nil } - -func uniqWithPrefix(in map[string]interface{}, prefix string) map[string]interface{} { - out := make(map[string]interface{}) - for k_in, v_in := range in { - spec := v_in.(map[string]interface{}) - out[addPrefix(prefix, k_in)] = spec - for k_spec, v_spec := range spec { - if k_spec == "inputs" { - inputs := make([]string, 0) - for _, i := range v_spec.([]interface{}) { - newInput := addPrefix(prefix, i.(string)) - inputs = append(inputs, newInput) - } - spec[k_spec] = inputs - continue - } - } - } - return out -} - -func addPrefix(prefix string, name string) string { - return prefix + "-" + name -} - -func decode(data *runtime.RawExtension) (map[string]interface{}, error) { - out := make(map[string]interface{}) - err := json.Unmarshal(data.Raw, &out) - if err != nil { - return nil, err - } - return out, nil -} diff --git a/controllers/factory/vector/types.go b/controllers/factory/vector/types.go index c273d9b6..dd9eefde 100644 --- a/controllers/factory/vector/types.go +++ b/controllers/factory/vector/types.go @@ -13,3 +13,27 @@ type ApiSpec struct { Address *string `json:"address,omitempty"` Playground *bool `json:"playground,omitempty"` } + +type Source struct { + Name string + Type string `mapper:"type"` + Options map[string]interface{} `mapstructure:",remain"` +} + +type Transform struct { + Name string + Type string `mapper:"type"` + Inputs []string `mapper:"inputs"` + Options map[string]interface{} `mapstructure:",remain"` +} + +type Sink struct { + Name string + Type string `mapper:"type"` + Inputs []string `mapper:"inputs"` + Options map[string]interface{} `mapstructure:",remain"` +} + +type ConfigComponent interface { + GetOptions() map[string]interface{} +} diff --git a/controllers/factory/vector/vector.go b/controllers/factory/vector/vector.go index 29070e88..c82c350b 100644 --- a/controllers/factory/vector/vector.go +++ b/controllers/factory/vector/vector.go @@ -1,5 +1,9 @@ package vector +import ( + "github.com/mitchellh/mapstructure" +) + func New(dataDir string, apiEnabled bool) *VectorConfig { sources := make(map[string]interface{}) sinks := make(map[string]interface{}) @@ -13,3 +17,35 @@ func New(dataDir string, apiEnabled bool) *VectorConfig { Sinks: sinks, } } + +func Decoder(c ConfigComponent) (map[string]interface{}, error) { + spec := make(map[string]interface{}) + spec = c.GetOptions() + config := &mapstructure.DecoderConfig{ + Result: &spec, + ZeroFields: false, + TagName: "mapper", + IgnoreUntaggedFields: true, + } + decoder, err := mapstructure.NewDecoder(config) + if err != nil { + return nil, err + } + err = decoder.Decode(c) + if err != nil { + return nil, err + } + return spec, nil +} + +func (t Source) GetOptions() map[string]interface{} { + return t.Options +} + +func (t Transform) GetOptions() map[string]interface{} { + return t.Options +} + +func (t Sink) GetOptions() map[string]interface{} { + return t.Options +} diff --git a/controllers/factory/vectorpipeline/vectorpipeline.go b/controllers/factory/vectorpipeline/vectorpipeline.go index 65d513ba..710ec798 100644 --- a/controllers/factory/vectorpipeline/vectorpipeline.go +++ b/controllers/factory/vectorpipeline/vectorpipeline.go @@ -2,17 +2,18 @@ package vectorpipeline import ( "context" + "encoding/json" vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" "github.com/kaasops/vector-operator/controllers/factory/k8sutils" + "github.com/kaasops/vector-operator/controllers/factory/vector" + "github.com/mitchellh/mapstructure" "sigs.k8s.io/controller-runtime/pkg/client" ) -func SelectSucceesed(ctx context.Context, rclient client.Client) (map[string]*vectorv1alpha1.VectorPipeline, error) { +func SelectSucceesed(ctx context.Context, rclient client.Client) ([]*vectorv1alpha1.VectorPipeline, error) { - res := make(map[string]*vectorv1alpha1.VectorPipeline) - - var vectorPipelinesCombined []vectorv1alpha1.VectorPipeline + var vectorPipelinesCombined []*vectorv1alpha1.VectorPipeline objlist := vectorv1alpha1.VectorPipelineList{} err := rclient.List(ctx, &objlist) @@ -26,21 +27,12 @@ func SelectSucceesed(ctx context.Context, rclient client.Client) (map[string]*ve } if item.Status.ConfigCheckResult != nil { if *item.Status.ConfigCheckResult { - vectorPipelinesCombined = append(vectorPipelinesCombined, item) + vectorPipelinesCombined = append(vectorPipelinesCombined, item.DeepCopy()) } } } - - for _, vectorPipeline := range vectorPipelinesCombined { - m := vectorPipeline.DeepCopy() - res[generateName(&vectorPipeline)] = m - } - return res, nil -} - -func generateName(vp *vectorv1alpha1.VectorPipeline) string { - return vp.Namespace + "-" + vp.Name + return vectorPipelinesCombined, nil } func SetSucceesStatus(ctx context.Context, vp *vectorv1alpha1.VectorPipeline, c client.Client) { @@ -54,3 +46,96 @@ func SetFailedStatus(ctx context.Context, vp *vectorv1alpha1.VectorPipeline, c c vp.Status.ConfigCheckResult = &status k8sutils.UpdateStatus(ctx, vp, c) } + +func GetSources(vp *vectorv1alpha1.VectorPipeline, filter []string) ([]vector.Source, error) { + var sources []vector.Source + sourcesMap, err := decodeRaw(vp.Spec.Sources.Raw) + if err != nil { + return nil, err + } + for k, v := range sourcesMap { + if len(filter) != 0 { + if !contains(filter, k) { + continue + } + } + var source vector.Source + if err := mapstructure.Decode(v, &source); err != nil { + return nil, err + } + source.Name = addPrefix(vp, k) + sources = append(sources, source) + } + return sources, nil +} + +func GetTransforms(vp *vectorv1alpha1.VectorPipeline) ([]vector.Transform, error) { + if vp.Spec.Transforms == nil { + return nil, nil + } + transformsMap, err := decodeRaw(vp.Spec.Transforms.Raw) + if err != nil { + return nil, err + } + var transforms []vector.Transform + if err := json.Unmarshal(vp.Spec.Transforms.Raw, &transformsMap); err != nil { + return nil, err + } + for k, v := range transformsMap { + var transform vector.Transform + if err := mapstructure.Decode(v, &transform); err != nil { + return nil, err + } + transform.Name = addPrefix(vp, k) + for i, inputName := range transform.Inputs { + transform.Inputs[i] = addPrefix(vp, inputName) + } + transforms = append(transforms, transform) + } + return transforms, nil +} + +func GetSinks(vp *vectorv1alpha1.VectorPipeline) ([]vector.Sink, error) { + sinksMap, err := decodeRaw(vp.Spec.Sinks.Raw) + if err != nil { + return nil, err + } + var sinks []vector.Sink + for k, v := range sinksMap { + var sink vector.Sink + if err := mapstructure.Decode(v, &sink); err != nil { + return nil, err + } + sink.Name = addPrefix(vp, k) + for i, inputName := range sink.Inputs { + sink.Inputs[i] = addPrefix(vp, inputName) + } + sinks = append(sinks, sink) + } + return sinks, nil +} + +func decodeRaw(raw []byte) (map[string]interface{}, error) { + result := make(map[string]interface{}) + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return result, nil +} + +func addPrefix(vp *vectorv1alpha1.VectorPipeline, name string) string { + return generateName(vp) + "-" + name +} + +func generateName(vp *vectorv1alpha1.VectorPipeline) string { + return vp.Namespace + "-" + vp.Name +} + +func contains(elems []string, v string) bool { + for _, s := range elems { + if v == s { + return true + } + } + return false +} diff --git a/controllers/vectorpipeline_controller.go b/controllers/vectorpipeline_controller.go index 52102e48..12d83d12 100644 --- a/controllers/vectorpipeline_controller.go +++ b/controllers/vectorpipeline_controller.go @@ -57,7 +57,7 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque log.Info("start Reconcile VectorPipeline") - vp, done, result, err := r.findVectorCustomResourceInstance(ctx, log, req) + vp, done, result, err := r.findVectorPipelineCustomResourceInstance(ctx, log, req) if done { return result, err } @@ -89,7 +89,7 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque return ctrl.Result{}, nil } -func (r *VectorPipelineReconciler) findVectorCustomResourceInstance(ctx context.Context, log logr.Logger, req ctrl.Request) (*vectorv1alpha1.VectorPipeline, bool, ctrl.Result, error) { +func (r *VectorPipelineReconciler) findVectorPipelineCustomResourceInstance(ctx context.Context, log logr.Logger, req ctrl.Request) (*vectorv1alpha1.VectorPipeline, bool, ctrl.Result, error) { // fetch the master instance vp := &vectorv1alpha1.VectorPipeline{} err := r.Get(ctx, req.NamespacedName, vp) @@ -119,10 +119,8 @@ func (r *VectorPipelineReconciler) SetupWithManager(mgr ctrl.Manager) error { func checkConfig(ctx context.Context, v *vectorv1alpha1.Vector, vp *vectorv1alpha1.VectorPipeline, c client.Client) error { log := log.FromContext(context.TODO()).WithValues("Vector Pipeline", "ConfigCheck") - vpName := vp.Namespace + "-" + vp.Name - vps := map[string]*vectorv1alpha1.VectorPipeline{ - vpName: vp, - } + var vps []*vectorv1alpha1.VectorPipeline + vps = append(vps, vp) cfg, err := config.GenerateConfig(v, vps) if err != nil { diff --git a/go.mod b/go.mod index 3eb591f8..d9825a97 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.18 require ( github.com/go-logr/logr v1.2.0 + github.com/mitchellh/mapstructure v1.5.0 github.com/onsi/ginkgo v1.16.5 github.com/onsi/gomega v1.18.1 k8s.io/api v0.24.2 diff --git a/go.sum b/go.sum index c27022bf..250a18ed 100644 --- a/go.sum +++ b/go.sum @@ -341,6 +341,8 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= From 107aa3682ad0fbc1cc372ec52fb362e2c035c2d8 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Tue, 25 Oct 2022 15:01:29 +0300 Subject: [PATCH 013/316] Add field reason to CRs Signed-off-by: Zemtsov Vladimir --- CHANGELOG.md | 3 ++ api/v1alpha1/vector_types.go | 3 +- api/v1alpha1/vectorpipeline_types.go | 3 +- api/v1alpha1/zz_generated.deepcopy.go | 10 +++++++ ...ervability.kaasops.io_vectorpipelines.yaml | 2 ++ .../observability.kaasops.io_vectors.yaml | 2 ++ .../factory/config/configcheck/configcheck.go | 28 +++++++++---------- .../config/configcheck/configcheck_error.go | 14 ++++++++++ controllers/factory/k8sutils/utils.go | 26 +++++++++++++++++ .../vectoragent/vector_agent_config.go | 9 +++--- .../vectoragent/vector_agent_controller.go | 23 ++++++++------- .../factory/vectorpipeline/vectorpipeline.go | 7 +++-- controllers/vector_controller.go | 12 ++++---- controllers/vectorpipeline_controller.go | 16 +++++++---- main.go | 20 +++++++++---- 15 files changed, 130 insertions(+), 48 deletions(-) create mode 100644 controllers/factory/config/configcheck/configcheck_error.go diff --git a/CHANGELOG.md b/CHANGELOG.md index cb970329..1ae96357 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,7 @@ ### Added +- Feature: Add field reason to CR Vector and VectorPipeline +- Feature: Add ConfigCheck for Vector +- Feature: Add ConfigCheck for VectorPipeline - Feature: Add utils for reconciling Kubernetes resources - Cleanup: Update Kustomize version in Makefile to v4.2.0 (for amd64 support) - Agent: Init Vector Agent Controller diff --git a/api/v1alpha1/vector_types.go b/api/v1alpha1/vector_types.go index c955b8bf..7d107148 100644 --- a/api/v1alpha1/vector_types.go +++ b/api/v1alpha1/vector_types.go @@ -36,7 +36,8 @@ type VectorSpec struct { // VectorStatus defines the observed state of Vector type VectorStatus struct { - ConfigCheckResult *bool `json:"configCheckResult,omitempty"` + ConfigCheckResult *bool `json:"configCheckResult,omitempty"` + Reason *string `json:"reason,omitempty"` } // VectorAgent is the Schema for the Vector Agent diff --git a/api/v1alpha1/vectorpipeline_types.go b/api/v1alpha1/vectorpipeline_types.go index 7bd66bdc..64cf4602 100644 --- a/api/v1alpha1/vectorpipeline_types.go +++ b/api/v1alpha1/vectorpipeline_types.go @@ -36,7 +36,8 @@ type VectorPipelineSpec struct { // VectorPipelineStatus defines the observed state of VectorPipeline type VectorPipelineStatus struct { - ConfigCheckResult *bool `json:"configCheckResult,omitempty"` + ConfigCheckResult *bool `json:"configCheckResult,omitempty"` + Reason *string `json:"reason,omitempty"` } //+kubebuilder:object:root=true diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index b72ba00d..bd0d017c 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -219,6 +219,11 @@ func (in *VectorPipelineStatus) DeepCopyInto(out *VectorPipelineStatus) { *out = new(bool) **out = **in } + if in.Reason != nil { + in, out := &in.Reason, &out.Reason + *out = new(string) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VectorPipelineStatus. @@ -259,6 +264,11 @@ func (in *VectorStatus) DeepCopyInto(out *VectorStatus) { *out = new(bool) **out = **in } + if in.Reason != nil { + in, out := &in.Reason, &out.Reason + *out = new(string) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VectorStatus. diff --git a/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml b/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml index 35b81682..f8be050b 100644 --- a/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml +++ b/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml @@ -50,6 +50,8 @@ spec: properties: configCheckResult: type: boolean + reason: + type: string type: object type: object served: true diff --git a/config/crd/bases/observability.kaasops.io_vectors.yaml b/config/crd/bases/observability.kaasops.io_vectors.yaml index bcf4a695..5edb2458 100644 --- a/config/crd/bases/observability.kaasops.io_vectors.yaml +++ b/config/crd/bases/observability.kaasops.io_vectors.yaml @@ -95,6 +95,8 @@ spec: properties: configCheckResult: type: boolean + reason: + type: string type: object type: object served: true diff --git a/controllers/factory/config/configcheck/configcheck.go b/controllers/factory/config/configcheck/configcheck.go index c181f291..4cbc9a90 100644 --- a/controllers/factory/config/configcheck/configcheck.go +++ b/controllers/factory/config/configcheck/configcheck.go @@ -2,7 +2,6 @@ package configcheck import ( "context" - "errors" "math/rand" "time" @@ -10,18 +9,16 @@ import ( "github.com/kaasops/vector-operator/controllers/factory/k8sutils" "github.com/kaasops/vector-operator/controllers/factory/label" corev1 "k8s.io/api/core/v1" + "k8s.io/client-go/kubernetes" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" ) -var ( - ErrConfigCheck = errors.New("Config Check finish with errors") -) - func Run( cfg []byte, c client.Client, + cs *kubernetes.Clientset, name, namespace, image string, @@ -42,7 +39,7 @@ func Run( return err } - err = ensureVectorConfigCheckPod(c, name, namespace, image, hash) + err = checkVectorConfigCheckPod(c, cs, name, namespace, image, hash) if err != nil { return err } @@ -86,12 +83,7 @@ func ensureVectorConfigCheckConfig(c client.Client, cfg []byte, name, ns, hash s return err } -func ensureVectorConfigCheckPod(c client.Client, name, ns, image, hash string) error { - // ctx := context.Background() - // log := log.FromContext(ctx).WithValues("vector-config-check-pod", "ConfigCheck") - - // log.Info("start Vector Config Check Pod") - +func checkVectorConfigCheckPod(c client.Client, cs *kubernetes.Clientset, name, ns, image, hash string) error { vectorConfigCheckPod := createVectorConfigCheckPod(name, ns, image, hash) err := k8sutils.CreatePod(vectorConfigCheckPod, c) @@ -99,7 +91,7 @@ func ensureVectorConfigCheckPod(c client.Client, name, ns, image, hash string) e return err } - err = getCheckResult(vectorConfigCheckPod, c) + err = getCheckResult(vectorConfigCheckPod, c, cs) if err != nil { return err } @@ -131,7 +123,7 @@ func randStringRunes() string { return string(b) } -func getCheckResult(pod *corev1.Pod, c client.Client) error { +func getCheckResult(pod *corev1.Pod, c client.Client, cs *kubernetes.Clientset) error { log := log.FromContext(context.TODO()).WithValues("Vector ConfigCheck", pod.Name) for { @@ -145,7 +137,13 @@ func getCheckResult(pod *corev1.Pod, c client.Client) error { log.Info("wait Validate Vector Config Result") time.Sleep(5 * time.Second) case "Failed": - return ErrConfigCheck + reason, err := k8sutils.GetPodLogs(pod, cs) + if err != nil { + return err + } + return &ErrConfigCheck{ + Reason: reason, + } case "Succeeded": log.Info("Config Check completed successfully") return nil diff --git a/controllers/factory/config/configcheck/configcheck_error.go b/controllers/factory/config/configcheck/configcheck_error.go new file mode 100644 index 00000000..37b3265d --- /dev/null +++ b/controllers/factory/config/configcheck/configcheck_error.go @@ -0,0 +1,14 @@ +package configcheck + +type error interface { + Error() string +} + +type ErrConfigCheck struct { + Reason string + Err error +} + +func (e *ErrConfigCheck) Error() string { + return e.Reason +} diff --git a/controllers/factory/k8sutils/utils.go b/controllers/factory/k8sutils/utils.go index 75c70932..a2e0e321 100644 --- a/controllers/factory/k8sutils/utils.go +++ b/controllers/factory/k8sutils/utils.go @@ -1,7 +1,9 @@ package k8sutils import ( + "bytes" "context" + "io" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -9,6 +11,7 @@ import ( "k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" ) @@ -58,6 +61,29 @@ func GetPod(pod *corev1.Pod, c client.Client) (*corev1.Pod, error) { return result, nil } +func GetPodLogs(pod *corev1.Pod, cs *kubernetes.Clientset) (string, error) { + count := int64(100) + podLogOptions := corev1.PodLogOptions{ + TailLines: &count, + } + + req := cs.CoreV1().Pods(pod.Namespace).GetLogs(pod.Name, &podLogOptions) + podLogs, err := req.Stream(context.TODO()) + if err != nil { + return "", err + } + defer podLogs.Close() + + buf := new(bytes.Buffer) + _, err = io.Copy(buf, podLogs) + if err != nil { + return "", err + } + str := buf.String() + + return str, nil +} + func UpdateStatus(ctx context.Context, obj client.Object, c client.Client) error { return c.Status().Update(ctx, obj) } diff --git a/controllers/factory/vectoragent/vector_agent_config.go b/controllers/factory/vectoragent/vector_agent_config.go index d544725c..7e203aac 100644 --- a/controllers/factory/vectoragent/vector_agent_config.go +++ b/controllers/factory/vectoragent/vector_agent_config.go @@ -4,6 +4,7 @@ import ( "context" corev1 "k8s.io/api/core/v1" + "k8s.io/client-go/kubernetes" "sigs.k8s.io/controller-runtime/pkg/client" vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" @@ -11,15 +12,15 @@ import ( "github.com/kaasops/vector-operator/controllers/factory/config/configcheck" ) -func createVectorAgentConfig(ctx context.Context, v *vectorv1alpha1.Vector, c client.Client) (*corev1.Secret, error) { +func createVectorAgentConfig(ctx context.Context, v *vectorv1alpha1.Vector, c client.Client, cs *kubernetes.Clientset) (*corev1.Secret, error) { cfg, err := config.Get(ctx, v, c) if err != nil { return nil, err } - err = configcheck.Run(cfg, c, v.Name, v.Namespace, v.Spec.Agent.Image) - if err == configcheck.ErrConfigCheck { - setFailedStatus(ctx, v, c) + err = configcheck.Run(cfg, c, cs, v.Name, v.Namespace, v.Spec.Agent.Image) + if _, ok := err.(*configcheck.ErrConfigCheck); ok { + setFailedStatus(ctx, v, c, err) return nil, err } if err != nil { diff --git a/controllers/factory/vectoragent/vector_agent_controller.go b/controllers/factory/vectoragent/vector_agent_controller.go index a0fd8290..43c5e10a 100644 --- a/controllers/factory/vectoragent/vector_agent_controller.go +++ b/controllers/factory/vectoragent/vector_agent_controller.go @@ -8,13 +8,14 @@ import ( "github.com/kaasops/vector-operator/controllers/factory/k8sutils" "github.com/kaasops/vector-operator/controllers/factory/label" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" "k8s.io/utils/pointer" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" ) -func EnsureVectorAgent(v *vectorv1alpha1.Vector, rclient client.Client) (done bool, result ctrl.Result, err error) { +func EnsureVectorAgent(v *vectorv1alpha1.Vector, rclient client.Client, cs *kubernetes.Clientset) (done bool, result ctrl.Result, err error) { ctx := context.Background() log := log.FromContext(ctx).WithValues("vector-agent", v.Name) @@ -30,7 +31,7 @@ func EnsureVectorAgent(v *vectorv1alpha1.Vector, rclient client.Client) (done bo } } - if done, result, err = ensureVectorAgentConfig(v, rclient); done { + if done, result, err = ensureVectorAgentConfig(v, rclient, cs); done { return } @@ -97,13 +98,13 @@ func ensureVectorAgentService(v *vectorv1alpha1.Vector, rclient client.Client) ( return helper.ReconcileResult(err) } -func ensureVectorAgentConfig(v *vectorv1alpha1.Vector, rclient client.Client) (bool, ctrl.Result, error) { +func ensureVectorAgentConfig(v *vectorv1alpha1.Vector, rclient client.Client, cs *kubernetes.Clientset) (bool, ctrl.Result, error) { ctx := context.Background() log := log.FromContext(ctx).WithValues("vector-agent-secret", v.Name) log.Info("start Reconcile Vector Agent Secret") - vectorAgentSecret, err := createVectorAgentConfig(ctx, v, rclient) + vectorAgentSecret, err := createVectorAgentConfig(ctx, v, rclient, cs) if err != nil { return helper.ReconcileResult(err) } @@ -163,14 +164,16 @@ func getControllerReference(owner *vectorv1alpha1.Vector) []metav1.OwnerReferenc } } -func setSucceesStatus(ctx context.Context, vp *vectorv1alpha1.Vector, c client.Client) { +func setSucceesStatus(ctx context.Context, v *vectorv1alpha1.Vector, c client.Client) { var status = true - vp.Status.ConfigCheckResult = &status - k8sutils.UpdateStatus(ctx, vp, c) + v.Status.ConfigCheckResult = &status + k8sutils.UpdateStatus(ctx, v, c) } -func setFailedStatus(ctx context.Context, vp *vectorv1alpha1.Vector, c client.Client) { +func setFailedStatus(ctx context.Context, v *vectorv1alpha1.Vector, c client.Client, err error) error { var status = false - vp.Status.ConfigCheckResult = &status - k8sutils.UpdateStatus(ctx, vp, c) + var reason = err.Error() + v.Status.ConfigCheckResult = &status + v.Status.Reason = &reason + return k8sutils.UpdateStatus(ctx, v, c) } diff --git a/controllers/factory/vectorpipeline/vectorpipeline.go b/controllers/factory/vectorpipeline/vectorpipeline.go index 710ec798..cab7811e 100644 --- a/controllers/factory/vectorpipeline/vectorpipeline.go +++ b/controllers/factory/vectorpipeline/vectorpipeline.go @@ -41,10 +41,13 @@ func SetSucceesStatus(ctx context.Context, vp *vectorv1alpha1.VectorPipeline, c k8sutils.UpdateStatus(ctx, vp, c) } -func SetFailedStatus(ctx context.Context, vp *vectorv1alpha1.VectorPipeline, c client.Client) { +func SetFailedStatus(ctx context.Context, vp *vectorv1alpha1.VectorPipeline, c client.Client, err error) error { var status = false + var reason = err.Error() vp.Status.ConfigCheckResult = &status - k8sutils.UpdateStatus(ctx, vp, c) + vp.Status.Reason = &reason + + return k8sutils.UpdateStatus(ctx, vp, c) } func GetSources(vp *vectorv1alpha1.VectorPipeline, filter []string) ([]vector.Source, error) { diff --git a/controllers/vector_controller.go b/controllers/vector_controller.go index 77c5a947..c179ece6 100644 --- a/controllers/vector_controller.go +++ b/controllers/vector_controller.go @@ -23,6 +23,7 @@ import ( "github.com/kaasops/vector-operator/controllers/factory/vectoragent" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" @@ -35,8 +36,9 @@ import ( type VectorReconciler struct { client.Client Scheme *runtime.Scheme - // Config *VectorConfig - // Status *VectorPipelineReconcileStatus + + // Temp. Wait this issue - https://github.com/kubernetes-sigs/controller-runtime/issues/452 + Clientset *kubernetes.Clientset } //+kubebuilder:rbac:groups=observability.kaasops.io,resources=vectors,verbs=get;list;watch;create;update;patch;delete @@ -67,7 +69,7 @@ func (r *VectorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr v.Spec.Agent.DataDir = "/vector-data-dir" } - return CreateOrUpdateVector(ctx, v, r.Client) + return CreateOrUpdateVector(ctx, v, r.Client, r.Clientset) } func (r *VectorReconciler) findVectorCustomResourceInstance(ctx context.Context, log logr.Logger, req ctrl.Request) (*vectorv1alpha1.Vector, bool, ctrl.Result, error) { @@ -97,8 +99,8 @@ func (r *VectorReconciler) SetupWithManager(mgr ctrl.Manager) error { Complete(r) } -func CreateOrUpdateVector(ctx context.Context, v *vectorv1alpha1.Vector, rclient client.Client) (ctrl.Result, error) { - if done, result, err := vectoragent.EnsureVectorAgent(v, rclient); done { +func CreateOrUpdateVector(ctx context.Context, v *vectorv1alpha1.Vector, rclient client.Client, cs *kubernetes.Clientset) (ctrl.Result, error) { + if done, result, err := vectoragent.EnsureVectorAgent(v, rclient, cs); done { return result, err } diff --git a/controllers/vectorpipeline_controller.go b/controllers/vectorpipeline_controller.go index 12d83d12..af119778 100644 --- a/controllers/vectorpipeline_controller.go +++ b/controllers/vectorpipeline_controller.go @@ -21,6 +21,7 @@ import ( "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" @@ -36,6 +37,9 @@ import ( type VectorPipelineReconciler struct { client.Client Scheme *runtime.Scheme + + // Temp. Wait this issue - https://github.com/kubernetes-sigs/controller-runtime/issues/452 + Clientset *kubernetes.Clientset } //+kubebuilder:rbac:groups=observability.kaasops.io,resources=vectorpipelines,verbs=get;list;watch;create;update;patch;delete @@ -78,7 +82,7 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque continue } - err = checkConfig(ctx, &v, vp, r.Client) + err = checkConfig(ctx, &v, vp, r.Client, r.Clientset) if err != nil { return ctrl.Result{}, err } @@ -116,7 +120,7 @@ func (r *VectorPipelineReconciler) SetupWithManager(mgr ctrl.Manager) error { Complete(r) } -func checkConfig(ctx context.Context, v *vectorv1alpha1.Vector, vp *vectorv1alpha1.VectorPipeline, c client.Client) error { +func checkConfig(ctx context.Context, v *vectorv1alpha1.Vector, vp *vectorv1alpha1.VectorPipeline, c client.Client, cs *kubernetes.Clientset) error { log := log.FromContext(context.TODO()).WithValues("Vector Pipeline", "ConfigCheck") var vps []*vectorv1alpha1.VectorPipeline @@ -127,9 +131,11 @@ func checkConfig(ctx context.Context, v *vectorv1alpha1.Vector, vp *vectorv1alph return err } - err = configcheck.Run(cfg, c, vp.Name, vp.Namespace, v.Spec.Agent.Image) - if err == configcheck.ErrConfigCheck { - vectorpipeline.SetFailedStatus(ctx, vp, c) + err = configcheck.Run(cfg, c, cs, vp.Name, vp.Namespace, v.Spec.Agent.Image) + if _, ok := err.(*configcheck.ErrConfigCheck); ok { + if err := vectorpipeline.SetFailedStatus(ctx, vp, c, err); err != nil { + return err + } log.Error(err, "Vector Config has error") return nil } diff --git a/main.go b/main.go index ac07ebd3..aa5c9188 100644 --- a/main.go +++ b/main.go @@ -24,6 +24,7 @@ import ( // to ensure that exec-entrypoint and run can make use of them. appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + "k8s.io/client-go/kubernetes" _ "k8s.io/client-go/plugin/pkg/client/auth" "sigs.k8s.io/controller-runtime/pkg/client" @@ -68,7 +69,14 @@ func main() { ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) - mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ + config := ctrl.GetConfigOrDie() + + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + panic(err) + } + + mgr, err := ctrl.NewManager(config, ctrl.Options{ Scheme: scheme, MetricsBindAddress: metricsAddr, Port: 9443, @@ -95,15 +103,17 @@ func main() { } if err = (&controllers.VectorReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Clientset: clientset, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Vector") os.Exit(1) } if err = (&controllers.VectorPipelineReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Clientset: clientset, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "VectorPipeline") os.Exit(1) From 50f2fbeffa4463151311d24ad3d72ec3c48684de Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Tue, 25 Oct 2022 11:33:56 +0300 Subject: [PATCH 014/316] return err on set status --- controllers/factory/vectorpipeline/vectorpipeline.go | 7 +++++-- controllers/vectorpipeline_controller.go | 4 +++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/controllers/factory/vectorpipeline/vectorpipeline.go b/controllers/factory/vectorpipeline/vectorpipeline.go index cab7811e..d0bd8f71 100644 --- a/controllers/factory/vectorpipeline/vectorpipeline.go +++ b/controllers/factory/vectorpipeline/vectorpipeline.go @@ -35,10 +35,13 @@ func SelectSucceesed(ctx context.Context, rclient client.Client) ([]*vectorv1alp return vectorPipelinesCombined, nil } -func SetSucceesStatus(ctx context.Context, vp *vectorv1alpha1.VectorPipeline, c client.Client) { +func SetSucceesStatus(ctx context.Context, vp *vectorv1alpha1.VectorPipeline, c client.Client) error { var status = true vp.Status.ConfigCheckResult = &status - k8sutils.UpdateStatus(ctx, vp, c) + if err := k8sutils.UpdateStatus(ctx, vp, c); err != nil { + return err + } + return nil } func SetFailedStatus(ctx context.Context, vp *vectorv1alpha1.VectorPipeline, c client.Client, err error) error { diff --git a/controllers/vectorpipeline_controller.go b/controllers/vectorpipeline_controller.go index af119778..93d18913 100644 --- a/controllers/vectorpipeline_controller.go +++ b/controllers/vectorpipeline_controller.go @@ -143,7 +143,9 @@ func checkConfig(ctx context.Context, v *vectorv1alpha1.Vector, vp *vectorv1alph return err } - vectorpipeline.SetSucceesStatus(ctx, vp, c) + if err := vectorpipeline.SetSucceesStatus(ctx, vp, c); err != nil { + return err + } return nil } From 4c22b65ae8ce745629c020158bf538e2b82fb262 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Tue, 25 Oct 2022 15:21:31 +0300 Subject: [PATCH 015/316] check hash before checkconfig --- api/v1alpha1/vectorpipeline_types.go | 5 +++-- api/v1alpha1/zz_generated.deepcopy.go | 5 +++++ ...ervability.kaasops.io_vectorpipelines.yaml | 3 +++ controllers/factory/config/config_build.go | 6 ++--- controllers/factory/vector/vector.go | 2 +- controllers/factory/vectorpipeline/hash.go | 22 +++++++++++++++++++ .../factory/vectorpipeline/vectorpipeline.go | 12 ++++++++++ controllers/vectorpipeline_controller.go | 16 ++++++++++---- 8 files changed, 61 insertions(+), 10 deletions(-) create mode 100644 controllers/factory/vectorpipeline/hash.go diff --git a/api/v1alpha1/vectorpipeline_types.go b/api/v1alpha1/vectorpipeline_types.go index 64cf4602..368f7477 100644 --- a/api/v1alpha1/vectorpipeline_types.go +++ b/api/v1alpha1/vectorpipeline_types.go @@ -36,8 +36,9 @@ type VectorPipelineSpec struct { // VectorPipelineStatus defines the observed state of VectorPipeline type VectorPipelineStatus struct { - ConfigCheckResult *bool `json:"configCheckResult,omitempty"` - Reason *string `json:"reason,omitempty"` + ConfigCheckResult *bool `json:"configCheckResult,omitempty"` + Reason *string `json:"reason,omitempty"` + LastAppliedConfigHash *uint32 `json:"lastAppliedConfigHash,omitempty"` } //+kubebuilder:object:root=true diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index bd0d017c..7e48bce9 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -224,6 +224,11 @@ func (in *VectorPipelineStatus) DeepCopyInto(out *VectorPipelineStatus) { *out = new(string) **out = **in } + if in.LastAppliedConfigHash != nil { + in, out := &in.LastAppliedConfigHash, &out.LastAppliedConfigHash + *out = new(uint32) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VectorPipelineStatus. diff --git a/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml b/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml index f8be050b..cb238823 100644 --- a/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml +++ b/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml @@ -52,6 +52,9 @@ spec: type: boolean reason: type: string + lastAppliedConfigHash: + format: int32 + type: integer type: object type: object served: true diff --git a/controllers/factory/config/config_build.go b/controllers/factory/config/config_build.go index 281d7580..58ce5fde 100644 --- a/controllers/factory/config/config_build.go +++ b/controllers/factory/config/config_build.go @@ -77,7 +77,7 @@ func getComponents(vps []*vectorv1alpha1.VectorPipeline) (map[string]interface{} return nil, nil, nil, err } for _, source := range sources { - spec, err := vector.Decoder(source) + spec, err := vector.Mapper(source) if err != nil { return nil, nil, nil, err } @@ -88,7 +88,7 @@ func getComponents(vps []*vectorv1alpha1.VectorPipeline) (map[string]interface{} return nil, nil, nil, err } for _, transform := range transforms { - spec, err := vector.Decoder(transform) + spec, err := vector.Mapper(transform) if err != nil { return nil, nil, nil, err } @@ -99,7 +99,7 @@ func getComponents(vps []*vectorv1alpha1.VectorPipeline) (map[string]interface{} return nil, nil, nil, err } for _, sink := range sinks { - spec, err := vector.Decoder(sink) + spec, err := vector.Mapper(sink) if err != nil { return nil, nil, nil, err } diff --git a/controllers/factory/vector/vector.go b/controllers/factory/vector/vector.go index c82c350b..5582e688 100644 --- a/controllers/factory/vector/vector.go +++ b/controllers/factory/vector/vector.go @@ -18,7 +18,7 @@ func New(dataDir string, apiEnabled bool) *VectorConfig { } } -func Decoder(c ConfigComponent) (map[string]interface{}, error) { +func Mapper(c ConfigComponent) (map[string]interface{}, error) { spec := make(map[string]interface{}) spec = c.GetOptions() config := &mapstructure.DecoderConfig{ diff --git a/controllers/factory/vectorpipeline/hash.go b/controllers/factory/vectorpipeline/hash.go new file mode 100644 index 00000000..1f86249f --- /dev/null +++ b/controllers/factory/vectorpipeline/hash.go @@ -0,0 +1,22 @@ +package vectorpipeline + +import ( + "encoding/json" + "hash/crc32" + + vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" +) + +func GetHash(input []byte) uint32 { + crc32q := crc32.MakeTable(crc32.IEEE) + return crc32.Checksum(input, crc32q) +} + +func GetVpSpecHash(vp *vectorv1alpha1.VectorPipeline) (*uint32, error) { + a, err := json.Marshal(vp.Spec) + if err != nil { + return nil, err + } + hash := GetHash(a) + return &hash, nil +} diff --git a/controllers/factory/vectorpipeline/vectorpipeline.go b/controllers/factory/vectorpipeline/vectorpipeline.go index d0bd8f71..d9fd24e0 100644 --- a/controllers/factory/vectorpipeline/vectorpipeline.go +++ b/controllers/factory/vectorpipeline/vectorpipeline.go @@ -53,6 +53,18 @@ func SetFailedStatus(ctx context.Context, vp *vectorv1alpha1.VectorPipeline, c c return k8sutils.UpdateStatus(ctx, vp, c) } +func SetLastAppliedConfigStatus(ctx context.Context, vp *vectorv1alpha1.VectorPipeline, c client.Client) error { + hash, err := GetVpSpecHash(vp) + if err != nil { + return err + } + vp.Status.LastAppliedConfigHash = hash + if err := k8sutils.UpdateStatus(ctx, vp, c); err != nil { + return err + } + return nil +} + func GetSources(vp *vectorv1alpha1.VectorPipeline, filter []string) ([]vector.Source, error) { var sources []vector.Source sourcesMap, err := decodeRaw(vp.Spec.Sources.Raw) diff --git a/controllers/vectorpipeline_controller.go b/controllers/vectorpipeline_controller.go index 93d18913..e3b9ebc1 100644 --- a/controllers/vectorpipeline_controller.go +++ b/controllers/vectorpipeline_controller.go @@ -77,14 +77,22 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque return ctrl.Result{}, nil } + hash, err := vectorpipeline.GetVpSpecHash(vp) + if err != nil { + return ctrl.Result{}, err + } + for _, v := range vectorInstances.Items { if v.DeletionTimestamp != nil { continue } - - err = checkConfig(ctx, &v, vp, r.Client, r.Clientset) - if err != nil { - return ctrl.Result{}, err + if vp.Status.LastAppliedConfigHash == nil || *hash != *vp.Status.LastAppliedConfigHash { + if err = checkConfig(ctx, &v, vp, r.Client, r.Clientset); err != nil { + return ctrl.Result{}, err + } + if err = vectorpipeline.SetLastAppliedConfigStatus(ctx, vp, r.Client); err != nil { + return ctrl.Result{}, err + } } } From 3ff0782d785b6e1617ae2623639a76acef25c608 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Tue, 25 Oct 2022 17:01:21 +0300 Subject: [PATCH 016/316] init README.md --- README.md | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ad182354..f1e9c6ea 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,18 @@ # vector-operator -// TODO(user): Add simple overview of use/purpose +Operator for Kubernetes to deploy and manage [Vector](https://vector.dev/). ## Description -// TODO(user): An in-depth paragraph about your project and overview of use +The operator deploys and configures a vector agent daemonset on every node to collect container and application logs from the node file system. + +## Features + +- [x] Building vector config from namespaced custom resources (kind: VectorPipeline) +- [x] Configuration validation +- [x] Full support of vector config options +- [ ] Namespace isolation +- [ ] Garbage collection +- [ ] Vector config optimization +- [ ] Vector aggregator support ## Getting Started You’ll need a Kubernetes cluster to run against. You can use [KIND](https://sigs.k8s.io/kind) to get a local cluster for testing, or run against a remote cluster. @@ -18,13 +28,13 @@ kubectl apply -f config/samples/ 2. Build and push your image to the location specified by `IMG`: ```sh -make docker-build docker-push IMG=/vector-operator:tag +make docker-build docker-push IMG=docker pull kaasops/vector-operator:latest ``` 3. Deploy the controller to the cluster with the image specified by `IMG`: ```sh -make deploy IMG=/vector-operator:tag +make deploy IMG=docker pull kaasops/vector-operator:latest ``` ### Uninstall CRDs @@ -42,7 +52,6 @@ make undeploy ``` ## Contributing -// TODO(user): Add detailed information on how you would like others to contribute to this project ### How it works This project aims to follow the Kubernetes [Operator pattern](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/) From fe53f0bf4562a59562171cd07eb950277d4760ef Mon Sep 17 00:00:00 2001 From: Vladimir <31961982+zvlb@users.noreply.github.com> Date: Tue, 25 Oct 2022 16:04:56 +0200 Subject: [PATCH 017/316] Pretty fix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f1e9c6ea..c33a4a1a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# vector-operator +# Vector Operator Operator for Kubernetes to deploy and manage [Vector](https://vector.dev/). ## Description From 78c7d8a9605ce0cce202b036789488ad8f7864fe Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Wed, 26 Oct 2022 13:01:13 +0300 Subject: [PATCH 018/316] rename lastapplied confighash to pipelinehash --- api/v1alpha1/vectorpipeline_types.go | 6 +++--- api/v1alpha1/zz_generated.deepcopy.go | 4 ++-- .../crd/bases/observability.kaasops.io_vectorpipelines.yaml | 4 ++-- controllers/factory/vectorpipeline/vectorpipeline.go | 2 +- controllers/vectorpipeline_controller.go | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/api/v1alpha1/vectorpipeline_types.go b/api/v1alpha1/vectorpipeline_types.go index 368f7477..dd971317 100644 --- a/api/v1alpha1/vectorpipeline_types.go +++ b/api/v1alpha1/vectorpipeline_types.go @@ -36,9 +36,9 @@ type VectorPipelineSpec struct { // VectorPipelineStatus defines the observed state of VectorPipeline type VectorPipelineStatus struct { - ConfigCheckResult *bool `json:"configCheckResult,omitempty"` - Reason *string `json:"reason,omitempty"` - LastAppliedConfigHash *uint32 `json:"lastAppliedConfigHash,omitempty"` + ConfigCheckResult *bool `json:"configCheckResult,omitempty"` + Reason *string `json:"reason,omitempty"` + LastAppliedPipelineHash *uint32 `json:"lastAppliedConfigHash,omitempty"` } //+kubebuilder:object:root=true diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 7e48bce9..1e64dc9f 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -224,8 +224,8 @@ func (in *VectorPipelineStatus) DeepCopyInto(out *VectorPipelineStatus) { *out = new(string) **out = **in } - if in.LastAppliedConfigHash != nil { - in, out := &in.LastAppliedConfigHash, &out.LastAppliedConfigHash + if in.LastAppliedPipelineHash != nil { + in, out := &in.LastAppliedPipelineHash, &out.LastAppliedPipelineHash *out = new(uint32) **out = **in } diff --git a/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml b/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml index cb238823..7a500dc7 100644 --- a/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml +++ b/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml @@ -50,11 +50,11 @@ spec: properties: configCheckResult: type: boolean - reason: - type: string lastAppliedConfigHash: format: int32 type: integer + reason: + type: string type: object type: object served: true diff --git a/controllers/factory/vectorpipeline/vectorpipeline.go b/controllers/factory/vectorpipeline/vectorpipeline.go index d9fd24e0..33472cb2 100644 --- a/controllers/factory/vectorpipeline/vectorpipeline.go +++ b/controllers/factory/vectorpipeline/vectorpipeline.go @@ -58,7 +58,7 @@ func SetLastAppliedConfigStatus(ctx context.Context, vp *vectorv1alpha1.VectorPi if err != nil { return err } - vp.Status.LastAppliedConfigHash = hash + vp.Status.LastAppliedPipelineHash = hash if err := k8sutils.UpdateStatus(ctx, vp, c); err != nil { return err } diff --git a/controllers/vectorpipeline_controller.go b/controllers/vectorpipeline_controller.go index e3b9ebc1..4cb162ef 100644 --- a/controllers/vectorpipeline_controller.go +++ b/controllers/vectorpipeline_controller.go @@ -86,7 +86,7 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque if v.DeletionTimestamp != nil { continue } - if vp.Status.LastAppliedConfigHash == nil || *hash != *vp.Status.LastAppliedConfigHash { + if vp.Status.LastAppliedPipelineHash == nil || *hash != *vp.Status.LastAppliedPipelineHash { if err = checkConfig(ctx, &v, vp, r.Client, r.Clientset); err != nil { return ctrl.Result{}, err } From 5d6a2a0d757849015002cbae1f456af5a59b2815 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Wed, 26 Oct 2022 13:04:14 +0300 Subject: [PATCH 019/316] fix lastappliedpipelinehash json tag name --- api/v1alpha1/vectorpipeline_types.go | 2 +- .../crd/bases/observability.kaasops.io_vectorpipelines.yaml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/v1alpha1/vectorpipeline_types.go b/api/v1alpha1/vectorpipeline_types.go index dd971317..f64a0b4e 100644 --- a/api/v1alpha1/vectorpipeline_types.go +++ b/api/v1alpha1/vectorpipeline_types.go @@ -38,7 +38,7 @@ type VectorPipelineSpec struct { type VectorPipelineStatus struct { ConfigCheckResult *bool `json:"configCheckResult,omitempty"` Reason *string `json:"reason,omitempty"` - LastAppliedPipelineHash *uint32 `json:"lastAppliedConfigHash,omitempty"` + LastAppliedPipelineHash *uint32 `json:"LastAppliedPipelineHash,omitempty"` } //+kubebuilder:object:root=true diff --git a/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml b/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml index 7a500dc7..52b7ff41 100644 --- a/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml +++ b/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml @@ -48,11 +48,11 @@ spec: status: description: VectorPipelineStatus defines the observed state of VectorPipeline properties: - configCheckResult: - type: boolean - lastAppliedConfigHash: + LastAppliedPipelineHash: format: int32 type: integer + configCheckResult: + type: boolean reason: type: string type: object From 1db8675c609dd4c8ed38e21eed991fbd12e95ecc Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Wed, 26 Oct 2022 13:45:20 +0300 Subject: [PATCH 020/316] check hash before vector config check --- api/v1alpha1/vector_types.go | 5 +-- api/v1alpha1/zz_generated.deepcopy.go | 5 +++ .../observability.kaasops.io_vectors.yaml | 3 ++ controllers/factory/utils/utils.go | 8 +++++ .../vectoragent/vector_agent_config.go | 32 +++++++++++++------ .../vectoragent/vector_agent_controller.go | 13 ++++++-- controllers/factory/vectorpipeline/hash.go | 9 ++---- .../factory/vectorpipeline/vectorpipeline.go | 2 +- controllers/vectorpipeline_controller.go | 2 +- 9 files changed, 57 insertions(+), 22 deletions(-) create mode 100644 controllers/factory/utils/utils.go diff --git a/api/v1alpha1/vector_types.go b/api/v1alpha1/vector_types.go index 7d107148..91740b7c 100644 --- a/api/v1alpha1/vector_types.go +++ b/api/v1alpha1/vector_types.go @@ -36,8 +36,9 @@ type VectorSpec struct { // VectorStatus defines the observed state of Vector type VectorStatus struct { - ConfigCheckResult *bool `json:"configCheckResult,omitempty"` - Reason *string `json:"reason,omitempty"` + ConfigCheckResult *bool `json:"configCheckResult,omitempty"` + Reason *string `json:"reason,omitempty"` + LastAppliedConfigHash *uint32 `json:"LastAppliedConfigHash,omitempty"` } // VectorAgent is the Schema for the Vector Agent diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 1e64dc9f..f36d6c1f 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -274,6 +274,11 @@ func (in *VectorStatus) DeepCopyInto(out *VectorStatus) { *out = new(string) **out = **in } + if in.LastAppliedConfigHash != nil { + in, out := &in.LastAppliedConfigHash, &out.LastAppliedConfigHash + *out = new(uint32) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VectorStatus. diff --git a/config/crd/bases/observability.kaasops.io_vectors.yaml b/config/crd/bases/observability.kaasops.io_vectors.yaml index 5edb2458..7b1a9a6b 100644 --- a/config/crd/bases/observability.kaasops.io_vectors.yaml +++ b/config/crd/bases/observability.kaasops.io_vectors.yaml @@ -93,6 +93,9 @@ spec: status: description: VectorStatus defines the observed state of Vector properties: + LastAppliedConfigHash: + format: int32 + type: integer configCheckResult: type: boolean reason: diff --git a/controllers/factory/utils/utils.go b/controllers/factory/utils/utils.go new file mode 100644 index 00000000..a3c1486c --- /dev/null +++ b/controllers/factory/utils/utils.go @@ -0,0 +1,8 @@ +package utils + +import "hash/crc32" + +func GetHash(input []byte) uint32 { + crc32q := crc32.MakeTable(crc32.IEEE) + return crc32.Checksum(input, crc32q) +} diff --git a/controllers/factory/vectoragent/vector_agent_config.go b/controllers/factory/vectoragent/vector_agent_config.go index 7e203aac..3270bc6e 100644 --- a/controllers/factory/vectoragent/vector_agent_config.go +++ b/controllers/factory/vectoragent/vector_agent_config.go @@ -10,6 +10,7 @@ import ( vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" "github.com/kaasops/vector-operator/controllers/factory/config" "github.com/kaasops/vector-operator/controllers/factory/config/configcheck" + "github.com/kaasops/vector-operator/controllers/factory/utils" ) func createVectorAgentConfig(ctx context.Context, v *vectorv1alpha1.Vector, c client.Client, cs *kubernetes.Clientset) (*corev1.Secret, error) { @@ -18,16 +19,29 @@ func createVectorAgentConfig(ctx context.Context, v *vectorv1alpha1.Vector, c cl return nil, err } - err = configcheck.Run(cfg, c, cs, v.Name, v.Namespace, v.Spec.Agent.Image) - if _, ok := err.(*configcheck.ErrConfigCheck); ok { - setFailedStatus(ctx, v, c, err) - return nil, err - } - if err != nil { - return nil, err - } + cfgHash := utils.GetHash(cfg) - setSucceesStatus(ctx, v, c) + if v.Status.LastAppliedConfigHash == nil || *v.Status.LastAppliedConfigHash != cfgHash { + err = configcheck.Run(cfg, c, cs, v.Name, v.Namespace, v.Spec.Agent.Image) + if _, ok := err.(*configcheck.ErrConfigCheck); ok { + if err := setFailedStatus(ctx, v, c, err); err != nil { + return nil, err + } + return nil, err + } + if err != nil { + return nil, err + } + + if err := SetLastAppliedPipelineStatus(ctx, v, c, &cfgHash); err != nil { + return nil, err + } + + if err := setSucceesStatus(ctx, v, c); err != nil { + return nil, err + } + + } labels := labelsForVectorAgent(v.Name) config := map[string][]byte{ diff --git a/controllers/factory/vectoragent/vector_agent_controller.go b/controllers/factory/vectoragent/vector_agent_controller.go index 43c5e10a..cf644302 100644 --- a/controllers/factory/vectoragent/vector_agent_controller.go +++ b/controllers/factory/vectoragent/vector_agent_controller.go @@ -164,10 +164,10 @@ func getControllerReference(owner *vectorv1alpha1.Vector) []metav1.OwnerReferenc } } -func setSucceesStatus(ctx context.Context, v *vectorv1alpha1.Vector, c client.Client) { +func setSucceesStatus(ctx context.Context, v *vectorv1alpha1.Vector, c client.Client) error { var status = true v.Status.ConfigCheckResult = &status - k8sutils.UpdateStatus(ctx, v, c) + return k8sutils.UpdateStatus(ctx, v, c) } func setFailedStatus(ctx context.Context, v *vectorv1alpha1.Vector, c client.Client, err error) error { @@ -177,3 +177,12 @@ func setFailedStatus(ctx context.Context, v *vectorv1alpha1.Vector, c client.Cli v.Status.Reason = &reason return k8sutils.UpdateStatus(ctx, v, c) } + +func SetLastAppliedPipelineStatus(ctx context.Context, v *vectorv1alpha1.Vector, c client.Client, hash *uint32) error { + + v.Status.LastAppliedConfigHash = hash + if err := k8sutils.UpdateStatus(ctx, v, c); err != nil { + return err + } + return nil +} diff --git a/controllers/factory/vectorpipeline/hash.go b/controllers/factory/vectorpipeline/hash.go index 1f86249f..8e6a83d9 100644 --- a/controllers/factory/vectorpipeline/hash.go +++ b/controllers/factory/vectorpipeline/hash.go @@ -2,21 +2,16 @@ package vectorpipeline import ( "encoding/json" - "hash/crc32" vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" + "github.com/kaasops/vector-operator/controllers/factory/utils" ) -func GetHash(input []byte) uint32 { - crc32q := crc32.MakeTable(crc32.IEEE) - return crc32.Checksum(input, crc32q) -} - func GetVpSpecHash(vp *vectorv1alpha1.VectorPipeline) (*uint32, error) { a, err := json.Marshal(vp.Spec) if err != nil { return nil, err } - hash := GetHash(a) + hash := utils.GetHash(a) return &hash, nil } diff --git a/controllers/factory/vectorpipeline/vectorpipeline.go b/controllers/factory/vectorpipeline/vectorpipeline.go index 33472cb2..5338fd1f 100644 --- a/controllers/factory/vectorpipeline/vectorpipeline.go +++ b/controllers/factory/vectorpipeline/vectorpipeline.go @@ -53,7 +53,7 @@ func SetFailedStatus(ctx context.Context, vp *vectorv1alpha1.VectorPipeline, c c return k8sutils.UpdateStatus(ctx, vp, c) } -func SetLastAppliedConfigStatus(ctx context.Context, vp *vectorv1alpha1.VectorPipeline, c client.Client) error { +func SetLastAppliedPipelineStatus(ctx context.Context, vp *vectorv1alpha1.VectorPipeline, c client.Client) error { hash, err := GetVpSpecHash(vp) if err != nil { return err diff --git a/controllers/vectorpipeline_controller.go b/controllers/vectorpipeline_controller.go index 4cb162ef..954b386d 100644 --- a/controllers/vectorpipeline_controller.go +++ b/controllers/vectorpipeline_controller.go @@ -90,7 +90,7 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque if err = checkConfig(ctx, &v, vp, r.Client, r.Clientset); err != nil { return ctrl.Result{}, err } - if err = vectorpipeline.SetLastAppliedConfigStatus(ctx, vp, r.Client); err != nil { + if err = vectorpipeline.SetLastAppliedPipelineStatus(ctx, vp, r.Client); err != nil { return ctrl.Result{}, err } } From 5a1ef4aba8d241b2f3db8033b51622055594378c Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Wed, 26 Oct 2022 13:48:35 +0300 Subject: [PATCH 021/316] add short name vectorpipeline --- api/v1alpha1/vectorpipeline_types.go | 1 + config/crd/bases/observability.kaasops.io_vectorpipelines.yaml | 2 ++ 2 files changed, 3 insertions(+) diff --git a/api/v1alpha1/vectorpipeline_types.go b/api/v1alpha1/vectorpipeline_types.go index f64a0b4e..9941d176 100644 --- a/api/v1alpha1/vectorpipeline_types.go +++ b/api/v1alpha1/vectorpipeline_types.go @@ -43,6 +43,7 @@ type VectorPipelineStatus struct { //+kubebuilder:object:root=true //+kubebuilder:subresource:status +//+kubebuilder:resource:shortName=vp // VectorPipeline is the Schema for the vectorpipelines API type VectorPipeline struct { diff --git a/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml b/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml index 52b7ff41..7c53b8f9 100644 --- a/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml +++ b/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml @@ -12,6 +12,8 @@ spec: kind: VectorPipeline listKind: VectorPipelineList plural: vectorpipelines + shortNames: + - vp singular: vectorpipeline scope: Namespaced versions: From eab0cebc445099569f678e307bf6d2ca611ae576 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Wed, 26 Oct 2022 14:46:48 +0300 Subject: [PATCH 022/316] Add additionalPrinterColumns for configCheckResult and age Signed-off-by: Zemtsov Vladimir --- api/v1alpha1/vector_types.go | 2 ++ api/v1alpha1/vectorpipeline_types.go | 2 ++ .../bases/observability.kaasops.io_vectorpipelines.yaml | 9 ++++++++- config/crd/bases/observability.kaasops.io_vectors.yaml | 9 ++++++++- 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/api/v1alpha1/vector_types.go b/api/v1alpha1/vector_types.go index 91740b7c..a2cc0249 100644 --- a/api/v1alpha1/vector_types.go +++ b/api/v1alpha1/vector_types.go @@ -61,6 +61,8 @@ type VectorAggregator struct { //+kubebuilder:object:root=true //+kubebuilder:subresource:status +//+kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" +//+kubebuilder:printcolumn:name="Status",type="boolean",JSONPath=".status.configCheckResult" // Vector is the Schema for the vectors API type Vector struct { diff --git a/api/v1alpha1/vectorpipeline_types.go b/api/v1alpha1/vectorpipeline_types.go index 9941d176..b65340f3 100644 --- a/api/v1alpha1/vectorpipeline_types.go +++ b/api/v1alpha1/vectorpipeline_types.go @@ -44,6 +44,8 @@ type VectorPipelineStatus struct { //+kubebuilder:object:root=true //+kubebuilder:subresource:status //+kubebuilder:resource:shortName=vp +//+kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" +//+kubebuilder:printcolumn:name="Status",type="boolean",JSONPath=".status.configCheckResult" // VectorPipeline is the Schema for the vectorpipelines API type VectorPipeline struct { diff --git a/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml b/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml index 7c53b8f9..0577b656 100644 --- a/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml +++ b/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml @@ -17,7 +17,14 @@ spec: singular: vectorpipeline scope: Namespaced versions: - - name: v1alpha1 + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.configCheckResult + name: Status + type: boolean + name: v1alpha1 schema: openAPIV3Schema: description: VectorPipeline is the Schema for the vectorpipelines API diff --git a/config/crd/bases/observability.kaasops.io_vectors.yaml b/config/crd/bases/observability.kaasops.io_vectors.yaml index 7b1a9a6b..e4f237ff 100644 --- a/config/crd/bases/observability.kaasops.io_vectors.yaml +++ b/config/crd/bases/observability.kaasops.io_vectors.yaml @@ -15,7 +15,14 @@ spec: singular: vector scope: Namespaced versions: - - name: v1alpha1 + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.configCheckResult + name: Status + type: boolean + name: v1alpha1 schema: openAPIV3Schema: description: Vector is the Schema for the vectors API From be54d60a0bfc801498b35ccbddbc87bf28f39096 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Wed, 26 Oct 2022 14:53:36 +0300 Subject: [PATCH 023/316] Clean field reason Signed-off-by: Zemtsov Vladimir --- controllers/factory/vectoragent/vector_agent_controller.go | 3 +++ controllers/factory/vectorpipeline/vectorpipeline.go | 7 +++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/controllers/factory/vectoragent/vector_agent_controller.go b/controllers/factory/vectoragent/vector_agent_controller.go index cf644302..ed6436af 100644 --- a/controllers/factory/vectoragent/vector_agent_controller.go +++ b/controllers/factory/vectoragent/vector_agent_controller.go @@ -167,6 +167,8 @@ func getControllerReference(owner *vectorv1alpha1.Vector) []metav1.OwnerReferenc func setSucceesStatus(ctx context.Context, v *vectorv1alpha1.Vector, c client.Client) error { var status = true v.Status.ConfigCheckResult = &status + v.Status.Reason = nil + return k8sutils.UpdateStatus(ctx, v, c) } @@ -175,6 +177,7 @@ func setFailedStatus(ctx context.Context, v *vectorv1alpha1.Vector, c client.Cli var reason = err.Error() v.Status.ConfigCheckResult = &status v.Status.Reason = &reason + return k8sutils.UpdateStatus(ctx, v, c) } diff --git a/controllers/factory/vectorpipeline/vectorpipeline.go b/controllers/factory/vectorpipeline/vectorpipeline.go index 5338fd1f..224342c2 100644 --- a/controllers/factory/vectorpipeline/vectorpipeline.go +++ b/controllers/factory/vectorpipeline/vectorpipeline.go @@ -38,10 +38,9 @@ func SelectSucceesed(ctx context.Context, rclient client.Client) ([]*vectorv1alp func SetSucceesStatus(ctx context.Context, vp *vectorv1alpha1.VectorPipeline, c client.Client) error { var status = true vp.Status.ConfigCheckResult = &status - if err := k8sutils.UpdateStatus(ctx, vp, c); err != nil { - return err - } - return nil + vp.Status.Reason = nil + + return k8sutils.UpdateStatus(ctx, vp, c) } func SetFailedStatus(ctx context.Context, vp *vectorv1alpha1.VectorPipeline, c client.Client, err error) error { From 57e804dfa5e86687f4c5c64671f44238c116dce0 Mon Sep 17 00:00:00 2001 From: Vladimir <31961982+zvlb@users.noreply.github.com> Date: Wed, 26 Oct 2022 16:11:52 +0200 Subject: [PATCH 024/316] Create LICENSE.md --- LICENSE.md | 201 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. From 5954717dabe6261905848500c83da90bde3a72af Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Wed, 26 Oct 2022 17:23:57 +0300 Subject: [PATCH 025/316] Update ReadMe Signed-off-by: Zemtsov Vladimir --- README.md | 82 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 64 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index c33a4a1a..e2bc1ff9 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ -# Vector Operator -Operator for Kubernetes to deploy and manage [Vector](https://vector.dev/). +

+ + Go Report Card + +

+ ## Description The operator deploys and configures a vector agent daemonset on every node to collect container and application logs from the node file system. @@ -14,6 +18,7 @@ The operator deploys and configures a vector agent daemonset on every node to co - [ ] Vector config optimization - [ ] Vector aggregator support + ## Getting Started You’ll need a Kubernetes cluster to run against. You can use [KIND](https://sigs.k8s.io/kind) to get a local cluster for testing, or run against a remote cluster. **Note:** Your controller will automatically use the current context in your kubeconfig file (i.e. whatever cluster `kubectl cluster-info` shows). @@ -51,6 +56,63 @@ UnDeploy the controller to the cluster: make undeploy ``` +## Configuration Examples +Configuration for CR Vector: +```yaml +apiVersion: observability.kaasops.io/v1alpha1 +kind: Vector +metadata: + name: vector-sample + namespace: vector +spec: + agent: + service: true + image: "timberio/vector:0.24.0-distroless-libc" +``` + +Configuration for CR VectorPipeline: +```yaml +apiVersion: observability.kaasops.io/v1alpha1 +kind: VectorPipeline +metadata: + name: vectorpipeline-sample +spec: + sources: + source1: + type: "kubernetes_logs" + extra_label_selector: "app!=testdeployment" + source2: + type: "kubernetes_logs" + extra_label_selector: "app!=testdeployment1" + transforms: + remap: + type: "remap" + inputs: + - source1 + source: | + .@timestamp = del(.timestamp) + + .testField = "testValuevalue" + filter: + type: "filter" + inputs: + - source2 + condition: + type: "vrl" + source: ".status != 200" + sinks: + test222: + type: "console" + encoding: + codec: "json" + inputs: + - filter + - remap +``` + + + + ## Contributing ### How it works @@ -85,19 +147,3 @@ make manifests More information can be found via the [Kubebuilder Documentation](https://book.kubebuilder.io/introduction.html) -## License - -Copyright 2022. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - From 469a4d815a53eac0beb8522cb25a9368d9b3530d Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Wed, 26 Oct 2022 19:55:18 +0300 Subject: [PATCH 026/316] added github workflow to build image (#14) * added github workflow to build image --- .github/workflows/main.yaml | 43 +++++++++++++++++++++++++++++++++++++ Makefile | 2 +- 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/main.yaml diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml new file mode 100644 index 00000000..c6cec786 --- /dev/null +++ b/.github/workflows/main.yaml @@ -0,0 +1,43 @@ +name: Build and Test + +# This workflow will run on master branch and on any pull requests targeting master +on: + push: + tags: + - '*' + branches: + - main + pull_request: + branches: + - main +jobs: + build-and-push-docker-image: + name: Build and push Docker image + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Get tag + id: tag + run: echo "TAG=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + + - name: Set up QEMU + uses: docker/setup-qemu-action@v1 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Login to DockerHub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build image and push to Docker Hub + uses: docker/build-push-action@v2 + with: + context: . + platforms: linux/amd64,linux/arm64 + push: true + tags: ${{ github.repository }}:${{ steps.tag.outputs.TAG }} \ No newline at end of file diff --git a/Makefile b/Makefile index a5157b83..c8419f6d 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ BUNDLE_METADATA_OPTS ?= $(BUNDLE_CHANNELS) $(BUNDLE_DEFAULT_CHANNEL) # # For example, running 'make bundle-build bundle-push catalog-build catalog-push' will build and push both # kaasops.io/vector-operator-bundle:$VERSION and kaasops.io/vector-operator-catalog:$VERSION. -IMAGE_TAG_BASE ?= kaasops.io/vector-operator +IMAGE_TAG_BASE ?= kaasops/vector-operator # BUNDLE_IMG defines the image:tag used for the bundle. # You can use it as an arg. (E.g make bundle-build BUNDLE_IMG=/:) From 31711f17c4580ea950cbc9a7dbc23320bd65600b Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Thu, 27 Oct 2022 09:39:38 +0300 Subject: [PATCH 027/316] Refactor VectorAgent Reconciler Signed-off-by: Zemtsov Vladimir --- .../vectoragent/vector_agent_config.go | 21 ++-- .../vectoragent/vector_agent_controller.go | 108 ++++++++++-------- .../vectoragent/vector_agent_daemonset.go | 32 +++--- .../factory/vectoragent/vector_agent_rbac.go | 26 ++--- .../vectoragent/vector_agent_service.go | 7 +- controllers/vector_controller.go | 8 +- 6 files changed, 106 insertions(+), 96 deletions(-) diff --git a/controllers/factory/vectoragent/vector_agent_config.go b/controllers/factory/vectoragent/vector_agent_config.go index 3270bc6e..2cca9e42 100644 --- a/controllers/factory/vectoragent/vector_agent_config.go +++ b/controllers/factory/vectoragent/vector_agent_config.go @@ -4,27 +4,24 @@ import ( "context" corev1 "k8s.io/api/core/v1" - "k8s.io/client-go/kubernetes" - "sigs.k8s.io/controller-runtime/pkg/client" - vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" "github.com/kaasops/vector-operator/controllers/factory/config" "github.com/kaasops/vector-operator/controllers/factory/config/configcheck" "github.com/kaasops/vector-operator/controllers/factory/utils" ) -func createVectorAgentConfig(ctx context.Context, v *vectorv1alpha1.Vector, c client.Client, cs *kubernetes.Clientset) (*corev1.Secret, error) { - cfg, err := config.Get(ctx, v, c) +func (vr *VectorAgentReconciler) createVectorAgentConfig(ctx context.Context) (*corev1.Secret, error) { + cfg, err := config.Get(ctx, vr.Vector, vr.Client) if err != nil { return nil, err } cfgHash := utils.GetHash(cfg) - if v.Status.LastAppliedConfigHash == nil || *v.Status.LastAppliedConfigHash != cfgHash { - err = configcheck.Run(cfg, c, cs, v.Name, v.Namespace, v.Spec.Agent.Image) + if vr.Vector.Status.LastAppliedConfigHash == nil || *vr.Vector.Status.LastAppliedConfigHash != cfgHash { + err = configcheck.Run(cfg, vr.Client, vr.Clientset, vr.Vector.Name, vr.Vector.Namespace, vr.Vector.Spec.Agent.Image) if _, ok := err.(*configcheck.ErrConfigCheck); ok { - if err := setFailedStatus(ctx, v, c, err); err != nil { + if err := setFailedStatus(ctx, vr.Vector, vr.Client, err); err != nil { return nil, err } return nil, err @@ -33,23 +30,23 @@ func createVectorAgentConfig(ctx context.Context, v *vectorv1alpha1.Vector, c cl return nil, err } - if err := SetLastAppliedPipelineStatus(ctx, v, c, &cfgHash); err != nil { + if err := SetLastAppliedPipelineStatus(ctx, vr.Vector, vr.Client, &cfgHash); err != nil { return nil, err } - if err := setSucceesStatus(ctx, v, c); err != nil { + if err := setSucceesStatus(ctx, vr.Vector, vr.Client); err != nil { return nil, err } } - labels := labelsForVectorAgent(v.Name) + labels := vr.labelsForVectorAgent() config := map[string][]byte{ "agent.json": cfg, } secret := &corev1.Secret{ - ObjectMeta: objectMetaVectorAgent(v, labels), + ObjectMeta: vr.objectMetaVectorAgent(labels), Data: config, } diff --git a/controllers/factory/vectoragent/vector_agent_controller.go b/controllers/factory/vectoragent/vector_agent_controller.go index ed6436af..7ad079f5 100644 --- a/controllers/factory/vectoragent/vector_agent_controller.go +++ b/controllers/factory/vectoragent/vector_agent_controller.go @@ -15,149 +15,165 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" ) -func EnsureVectorAgent(v *vectorv1alpha1.Vector, rclient client.Client, cs *kubernetes.Clientset) (done bool, result ctrl.Result, err error) { +type VectorAgentReconciler struct { + client.Client + Vector *vectorv1alpha1.Vector + + // Temp. Wait this issue - https://github.com/kubernetes-sigs/controller-runtime/issues/452 + Clientset *kubernetes.Clientset +} + +func NewReconciler(v *vectorv1alpha1.Vector, c client.Client, cs *kubernetes.Clientset) *VectorAgentReconciler { + return &VectorAgentReconciler{ + Client: c, + Vector: v, + Clientset: cs, + } +} + +func (vr *VectorAgentReconciler) EnsureVectorAgent() (done bool, result ctrl.Result, err error) { ctx := context.Background() - log := log.FromContext(ctx).WithValues("vector-agent", v.Name) + log := log.FromContext(ctx).WithValues("vector-agent", vr.Vector.Name) log.Info("start Reconcile Vector Agent") - if done, result, err = ensureVectorAgentRBAC(v, rclient); done { + if done, result, err = vr.ensureVectorAgentRBAC(); done { return } - if v.Spec.Agent.Service { - if done, result, err = ensureVectorAgentService(v, rclient); done { + if vr.Vector.Spec.Agent.Service { + if done, result, err = vr.ensureVectorAgentService(); done { return } } - if done, result, err = ensureVectorAgentConfig(v, rclient, cs); done { + if done, result, err = vr.ensureVectorAgentConfig(); done { return } - if done, result, err = ensureVectorAgentDaemonSet(v, rclient); done { + if done, result, err = vr.ensureVectorAgentDaemonSet(); done { return } return } -func ensureVectorAgentRBAC(v *vectorv1alpha1.Vector, rclient client.Client) (bool, ctrl.Result, error) { +func (vr *VectorAgentReconciler) ensureVectorAgentRBAC() (bool, ctrl.Result, error) { ctx := context.Background() - log := log.FromContext(ctx).WithValues("vector-agent-rbac", v.Name) + log := log.FromContext(ctx).WithValues("vector-agent-rbac", vr.Vector.Name) log.Info("start Reconcile Vector Agent RBAC") - if done, _, err := ensureVectorAgentServiceAccount(v, rclient); done { + if done, _, err := vr.ensureVectorAgentServiceAccount(); done { return helper.ReconcileResult(err) } - if done, _, err := ensureVectorAgentClusterRole(v, rclient); done { + if done, _, err := vr.ensureVectorAgentClusterRole(); done { return helper.ReconcileResult(err) } - if done, _, err := ensureVectorAgentClusterRoleBinding(v, rclient); done { + if done, _, err := vr.ensureVectorAgentClusterRoleBinding(); done { return helper.ReconcileResult(err) } return helper.ReconcileResult(nil) } -func ensureVectorAgentServiceAccount(v *vectorv1alpha1.Vector, rclient client.Client) (bool, ctrl.Result, error) { - vectorAgentServiceAccount := createVectorAgentServiceAccount(v) +func (vr *VectorAgentReconciler) ensureVectorAgentServiceAccount() (bool, ctrl.Result, error) { + vectorAgentServiceAccount := vr.createVectorAgentServiceAccount() - _, err := k8sutils.CreateOrUpdateServiceAccount(vectorAgentServiceAccount, rclient) + _, err := k8sutils.CreateOrUpdateServiceAccount(vectorAgentServiceAccount, vr.Client) return helper.ReconcileResult(err) } -func ensureVectorAgentClusterRole(v *vectorv1alpha1.Vector, rclient client.Client) (bool, ctrl.Result, error) { - vectorAgentClusterRole := createVectorAgentClusterRole(v) +func (vr *VectorAgentReconciler) ensureVectorAgentClusterRole() (bool, ctrl.Result, error) { + vectorAgentClusterRole := vr.createVectorAgentClusterRole() - _, err := k8sutils.CreateOrUpdateClusterRole(vectorAgentClusterRole, rclient) + _, err := k8sutils.CreateOrUpdateClusterRole(vectorAgentClusterRole, vr.Client) return helper.ReconcileResult(err) } -func ensureVectorAgentClusterRoleBinding(v *vectorv1alpha1.Vector, rclient client.Client) (bool, ctrl.Result, error) { - vectorAgentClusterRoleBinding := createVectorAgentClusterRoleBinding(v) +func (vr *VectorAgentReconciler) ensureVectorAgentClusterRoleBinding() (bool, ctrl.Result, error) { + vectorAgentClusterRoleBinding := vr.createVectorAgentClusterRoleBinding() - _, err := k8sutils.CreateOrUpdateClusterRoleBinding(vectorAgentClusterRoleBinding, rclient) + _, err := k8sutils.CreateOrUpdateClusterRoleBinding(vectorAgentClusterRoleBinding, vr.Client) return helper.ReconcileResult(err) } -func ensureVectorAgentService(v *vectorv1alpha1.Vector, rclient client.Client) (bool, ctrl.Result, error) { +func (vr *VectorAgentReconciler) ensureVectorAgentService() (bool, ctrl.Result, error) { ctx := context.Background() - log := log.FromContext(ctx).WithValues("vector-agent-service", v.Name) + log := log.FromContext(ctx).WithValues("vector-agent-service", vr.Vector.Name) log.Info("start Reconcile Vector Agent Service") - vectorAgentService := createVectorAgentService(v) + vectorAgentService := vr.createVectorAgentService() - _, err := k8sutils.CreateOrUpdateService(vectorAgentService, rclient) + _, err := k8sutils.CreateOrUpdateService(vectorAgentService, vr.Client) return helper.ReconcileResult(err) } -func ensureVectorAgentConfig(v *vectorv1alpha1.Vector, rclient client.Client, cs *kubernetes.Clientset) (bool, ctrl.Result, error) { +func (vr *VectorAgentReconciler) ensureVectorAgentConfig() (bool, ctrl.Result, error) { ctx := context.Background() - log := log.FromContext(ctx).WithValues("vector-agent-secret", v.Name) + log := log.FromContext(ctx).WithValues("vector-agent-secret", vr.Vector.Name) log.Info("start Reconcile Vector Agent Secret") - vectorAgentSecret, err := createVectorAgentConfig(ctx, v, rclient, cs) + vectorAgentSecret, err := vr.createVectorAgentConfig(ctx) if err != nil { return helper.ReconcileResult(err) } - _, err = k8sutils.CreateOrUpdateSecret(vectorAgentSecret, rclient) + _, err = k8sutils.CreateOrUpdateSecret(vectorAgentSecret, vr.Client) return helper.ReconcileResult(err) } -func ensureVectorAgentDaemonSet(v *vectorv1alpha1.Vector, rclient client.Client) (bool, ctrl.Result, error) { +func (vr *VectorAgentReconciler) ensureVectorAgentDaemonSet() (bool, ctrl.Result, error) { ctx := context.Background() - log := log.FromContext(ctx).WithValues("vector-agent-daemon-set", v.Name) + log := log.FromContext(ctx).WithValues("vector-agent-daemon-set", vr.Vector.Name) log.Info("start Reconcile Vector Agent DaemonSet") - vectorAgentDaemonSet := createVectorAgentDaemonSet(v) + vectorAgentDaemonSet := vr.createVectorAgentDaemonSet() - _, err := k8sutils.CreateOrUpdateDaemonSet(vectorAgentDaemonSet, rclient) + _, err := k8sutils.CreateOrUpdateDaemonSet(vectorAgentDaemonSet, vr.Client) return helper.ReconcileResult(err) } -func labelsForVectorAgent(name string) map[string]string { +func (vr *VectorAgentReconciler) labelsForVectorAgent() map[string]string { return map[string]string{ label.ManagedByLabelKey: "vector-operator", label.NameLabelKey: "vector", label.ComponentLabelKey: "Agent", - label.InstanceLabelKey: name, + label.InstanceLabelKey: vr.Vector.Name, label.VectorExcludeLabel: "true", } } -func objectMetaVectorAgent(v *vectorv1alpha1.Vector, labels map[string]string) metav1.ObjectMeta { +func (vr *VectorAgentReconciler) objectMetaVectorAgent(labels map[string]string) metav1.ObjectMeta { return metav1.ObjectMeta{ - Name: v.Name + "-agent", - Namespace: v.Namespace, + Name: vr.Vector.Name + "-agent", + Namespace: vr.Vector.Namespace, Labels: labels, - OwnerReferences: getControllerReference(v), + OwnerReferences: vr.getControllerReference(), } } -func getNameVectorAgent(v *vectorv1alpha1.Vector) string { - name := v.Name + "-agent" +func (vr *VectorAgentReconciler) getNameVectorAgent() string { + name := vr.Vector.Name + "-agent" return name } -func getControllerReference(owner *vectorv1alpha1.Vector) []metav1.OwnerReference { +func (vr *VectorAgentReconciler) getControllerReference() []metav1.OwnerReference { return []metav1.OwnerReference{ { - APIVersion: owner.APIVersion, - Kind: owner.Kind, - Name: owner.GetName(), - UID: owner.GetUID(), + APIVersion: vr.Vector.APIVersion, + Kind: vr.Vector.Kind, + Name: vr.Vector.GetName(), + UID: vr.Vector.GetUID(), BlockOwnerDeletion: pointer.BoolPtr(true), Controller: pointer.BoolPtr(true), }, diff --git a/controllers/factory/vectoragent/vector_agent_daemonset.go b/controllers/factory/vectoragent/vector_agent_daemonset.go index f06e4804..3029c919 100644 --- a/controllers/factory/vectoragent/vector_agent_daemonset.go +++ b/controllers/factory/vectoragent/vector_agent_daemonset.go @@ -4,30 +4,28 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" ) -func createVectorAgentDaemonSet(v *vectorv1alpha1.Vector) *appsv1.DaemonSet { - labels := labelsForVectorAgent(v.Name) +func (vr *VectorAgentReconciler) createVectorAgentDaemonSet() *appsv1.DaemonSet { + labels := vr.labelsForVectorAgent() daemonset := &appsv1.DaemonSet{ - ObjectMeta: objectMetaVectorAgent(v, labels), + ObjectMeta: vr.objectMetaVectorAgent(labels), Spec: appsv1.DaemonSetSpec{ Selector: &metav1.LabelSelector{MatchLabels: labels}, Template: corev1.PodTemplateSpec{ - ObjectMeta: objectMetaVectorAgent(v, labels), + ObjectMeta: vr.objectMetaVectorAgent(labels), Spec: corev1.PodSpec{ - ServiceAccountName: getNameVectorAgent(v), - Volumes: generateVectorAgentVolume(v), + ServiceAccountName: vr.getNameVectorAgent(), + Volumes: vr.generateVectorAgentVolume(), SecurityContext: &corev1.PodSecurityContext{}, - Tolerations: v.Spec.Agent.Tolerations, + Tolerations: vr.Vector.Spec.Agent.Tolerations, Containers: []corev1.Container{ { - Name: getNameVectorAgent(v), - Image: v.Spec.Agent.Image, + Name: vr.getNameVectorAgent(), + Image: vr.Vector.Spec.Agent.Image, Args: []string{"--config-dir", "/etc/vector/", "--watch-config"}, - Env: generateVectorAgentEnvs(v), + Env: vr.generateVectorAgentEnvs(), Ports: []corev1.ContainerPort{ { Name: "prom-exporter", @@ -35,7 +33,7 @@ func createVectorAgentDaemonSet(v *vectorv1alpha1.Vector) *appsv1.DaemonSet { Protocol: "TCP", }, }, - VolumeMounts: generateVectorAgentVolumeMounts(v), + VolumeMounts: vr.generateVectorAgentVolumeMounts(), SecurityContext: &corev1.SecurityContext{}, }, }, @@ -47,13 +45,13 @@ func createVectorAgentDaemonSet(v *vectorv1alpha1.Vector) *appsv1.DaemonSet { return daemonset } -func generateVectorAgentVolume(v *vectorv1alpha1.Vector) []corev1.Volume { +func (vr *VectorAgentReconciler) generateVectorAgentVolume() []corev1.Volume { volume := []corev1.Volume{ { Name: "config", VolumeSource: corev1.VolumeSource{ Secret: &corev1.SecretVolumeSource{ - SecretName: getNameVectorAgent(v), + SecretName: vr.getNameVectorAgent(), }, }, }, @@ -102,7 +100,7 @@ func generateVectorAgentVolume(v *vectorv1alpha1.Vector) []corev1.Volume { return volume } -func generateVectorAgentVolumeMounts(spec *vectorv1alpha1.Vector) []corev1.VolumeMount { +func (vr *VectorAgentReconciler) generateVectorAgentVolumeMounts() []corev1.VolumeMount { volumeMount := []corev1.VolumeMount{ { Name: "config", @@ -133,7 +131,7 @@ func generateVectorAgentVolumeMounts(spec *vectorv1alpha1.Vector) []corev1.Volum return volumeMount } -func generateVectorAgentEnvs(spec *vectorv1alpha1.Vector) []corev1.EnvVar { +func (vr *VectorAgentReconciler) generateVectorAgentEnvs() []corev1.EnvVar { envs := []corev1.EnvVar{ { Name: "VECTOR_SELF_NODE_NAME", diff --git a/controllers/factory/vectoragent/vector_agent_rbac.go b/controllers/factory/vectoragent/vector_agent_rbac.go index 6ea637b4..6baf21ad 100644 --- a/controllers/factory/vectoragent/vector_agent_rbac.go +++ b/controllers/factory/vectoragent/vector_agent_rbac.go @@ -3,25 +3,23 @@ package vectoragent import ( corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" - - vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" ) -func createVectorAgentServiceAccount(v *vectorv1alpha1.Vector) *corev1.ServiceAccount { - labels := labelsForVectorAgent(v.Name) +func (vr *VectorAgentReconciler) createVectorAgentServiceAccount() *corev1.ServiceAccount { + labels := vr.labelsForVectorAgent() serviceAccount := &corev1.ServiceAccount{ - ObjectMeta: objectMetaVectorAgent(v, labels), + ObjectMeta: vr.objectMetaVectorAgent(labels), } return serviceAccount } -func createVectorAgentClusterRole(v *vectorv1alpha1.Vector) *rbacv1.ClusterRole { - labels := labelsForVectorAgent(v.Name) +func (vr *VectorAgentReconciler) createVectorAgentClusterRole() *rbacv1.ClusterRole { + labels := vr.labelsForVectorAgent() clusterRole := &rbacv1.ClusterRole{ - ObjectMeta: objectMetaVectorAgent(v, labels), + ObjectMeta: vr.objectMetaVectorAgent(labels), Rules: []rbacv1.PolicyRule{ { APIGroups: []string{""}, @@ -34,21 +32,21 @@ func createVectorAgentClusterRole(v *vectorv1alpha1.Vector) *rbacv1.ClusterRole return clusterRole } -func createVectorAgentClusterRoleBinding(v *vectorv1alpha1.Vector) *rbacv1.ClusterRoleBinding { - labels := labelsForVectorAgent(v.Name) +func (vr *VectorAgentReconciler) createVectorAgentClusterRoleBinding() *rbacv1.ClusterRoleBinding { + labels := vr.labelsForVectorAgent() clusterRoleBinding := &rbacv1.ClusterRoleBinding{ - ObjectMeta: objectMetaVectorAgent(v, labels), + ObjectMeta: vr.objectMetaVectorAgent(labels), RoleRef: rbacv1.RoleRef{ Kind: "ClusterRole", APIGroup: "rbac.authorization.k8s.io", - Name: getNameVectorAgent(v), + Name: vr.getNameVectorAgent(), }, Subjects: []rbacv1.Subject{ { Kind: "ServiceAccount", - Name: getNameVectorAgent(v), - Namespace: v.Namespace, + Name: vr.getNameVectorAgent(), + Namespace: vr.Vector.Namespace, }, }, } diff --git a/controllers/factory/vectoragent/vector_agent_service.go b/controllers/factory/vectoragent/vector_agent_service.go index c30909bd..6d84d3d3 100644 --- a/controllers/factory/vectoragent/vector_agent_service.go +++ b/controllers/factory/vectoragent/vector_agent_service.go @@ -3,15 +3,14 @@ package vectoragent import ( corev1 "k8s.io/api/core/v1" - vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" "k8s.io/apimachinery/pkg/util/intstr" ) -func createVectorAgentService(v *vectorv1alpha1.Vector) *corev1.Service { - labels := labelsForVectorAgent(v.Name) +func (vr *VectorAgentReconciler) createVectorAgentService() *corev1.Service { + labels := vr.labelsForVectorAgent() service := &corev1.Service{ - ObjectMeta: objectMetaVectorAgent(v, labels), + ObjectMeta: vr.objectMetaVectorAgent(labels), Spec: corev1.ServiceSpec{ Ports: []corev1.ServicePort{ { diff --git a/controllers/vector_controller.go b/controllers/vector_controller.go index c179ece6..57a5a91e 100644 --- a/controllers/vector_controller.go +++ b/controllers/vector_controller.go @@ -69,7 +69,7 @@ func (r *VectorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr v.Spec.Agent.DataDir = "/vector-data-dir" } - return CreateOrUpdateVector(ctx, v, r.Client, r.Clientset) + return r.CreateOrUpdateVector(v) } func (r *VectorReconciler) findVectorCustomResourceInstance(ctx context.Context, log logr.Logger, req ctrl.Request) (*vectorv1alpha1.Vector, bool, ctrl.Result, error) { @@ -99,8 +99,10 @@ func (r *VectorReconciler) SetupWithManager(mgr ctrl.Manager) error { Complete(r) } -func CreateOrUpdateVector(ctx context.Context, v *vectorv1alpha1.Vector, rclient client.Client, cs *kubernetes.Clientset) (ctrl.Result, error) { - if done, result, err := vectoragent.EnsureVectorAgent(v, rclient, cs); done { +func (r *VectorReconciler) CreateOrUpdateVector(v *vectorv1alpha1.Vector) (ctrl.Result, error) { + vectorAgentReconciler := vectoragent.NewReconciler(v, r.Client, r.Clientset) + + if done, result, err := vectorAgentReconciler.EnsureVectorAgent(); done { return result, err } From f4a6f159bb9d1193b6649c53270435d370f4cf45 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Thu, 27 Oct 2022 09:45:13 +0300 Subject: [PATCH 028/316] Add licensed block to all go-files Signed-off-by: Zemtsov Vladimir --- controllers/factory/config/config_build.go | 16 ++++++++++++++++ .../factory/config/configcheck/configcheck.go | 16 ++++++++++++++++ .../config/configcheck/configcheck_config.go | 16 ++++++++++++++++ .../config/configcheck/configcheck_error.go | 16 ++++++++++++++++ .../config/configcheck/configcheck_pod.go | 16 ++++++++++++++++ .../config/configcheck/configcheck_rbac.go | 16 ++++++++++++++++ controllers/factory/helper/helper.go | 16 ++++++++++++++++ controllers/factory/k8sutils/utils.go | 16 ++++++++++++++++ controllers/factory/label/label.go | 16 ++++++++++++++++ controllers/factory/utils/utils.go | 16 ++++++++++++++++ controllers/factory/vector/types.go | 16 ++++++++++++++++ controllers/factory/vector/vector.go | 16 ++++++++++++++++ .../factory/vectoragent/vector_agent_config.go | 16 ++++++++++++++++ .../vectoragent/vector_agent_controller.go | 16 ++++++++++++++++ .../vectoragent/vector_agent_daemonset.go | 16 ++++++++++++++++ .../factory/vectoragent/vector_agent_rbac.go | 16 ++++++++++++++++ .../factory/vectoragent/vector_agent_service.go | 16 ++++++++++++++++ controllers/factory/vectorpipeline/hash.go | 16 ++++++++++++++++ .../factory/vectorpipeline/vectorpipeline.go | 16 ++++++++++++++++ 19 files changed, 304 insertions(+) diff --git a/controllers/factory/config/config_build.go b/controllers/factory/config/config_build.go index 58ce5fde..5a67f397 100644 --- a/controllers/factory/config/config_build.go +++ b/controllers/factory/config/config_build.go @@ -1,3 +1,19 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package config import ( diff --git a/controllers/factory/config/configcheck/configcheck.go b/controllers/factory/config/configcheck/configcheck.go index 4cbc9a90..16616087 100644 --- a/controllers/factory/config/configcheck/configcheck.go +++ b/controllers/factory/config/configcheck/configcheck.go @@ -1,3 +1,19 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package configcheck import ( diff --git a/controllers/factory/config/configcheck/configcheck_config.go b/controllers/factory/config/configcheck/configcheck_config.go index 85a0a0e1..4b5e6fc9 100644 --- a/controllers/factory/config/configcheck/configcheck_config.go +++ b/controllers/factory/config/configcheck/configcheck_config.go @@ -1,3 +1,19 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package configcheck import ( diff --git a/controllers/factory/config/configcheck/configcheck_error.go b/controllers/factory/config/configcheck/configcheck_error.go index 37b3265d..2fd40672 100644 --- a/controllers/factory/config/configcheck/configcheck_error.go +++ b/controllers/factory/config/configcheck/configcheck_error.go @@ -1,3 +1,19 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package configcheck type error interface { diff --git a/controllers/factory/config/configcheck/configcheck_pod.go b/controllers/factory/config/configcheck/configcheck_pod.go index 8fe883b5..0bbd6c16 100644 --- a/controllers/factory/config/configcheck/configcheck_pod.go +++ b/controllers/factory/config/configcheck/configcheck_pod.go @@ -1,3 +1,19 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package configcheck import ( diff --git a/controllers/factory/config/configcheck/configcheck_rbac.go b/controllers/factory/config/configcheck/configcheck_rbac.go index 84d06b35..08317c7f 100644 --- a/controllers/factory/config/configcheck/configcheck_rbac.go +++ b/controllers/factory/config/configcheck/configcheck_rbac.go @@ -1,3 +1,19 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package configcheck import ( diff --git a/controllers/factory/helper/helper.go b/controllers/factory/helper/helper.go index 5247a1ef..5e3e0c64 100644 --- a/controllers/factory/helper/helper.go +++ b/controllers/factory/helper/helper.go @@ -1,3 +1,19 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package helper import ctrl "sigs.k8s.io/controller-runtime" diff --git a/controllers/factory/k8sutils/utils.go b/controllers/factory/k8sutils/utils.go index a2e0e321..01d77300 100644 --- a/controllers/factory/k8sutils/utils.go +++ b/controllers/factory/k8sutils/utils.go @@ -1,3 +1,19 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package k8sutils import ( diff --git a/controllers/factory/label/label.go b/controllers/factory/label/label.go index 3d21f139..7dd3cf71 100644 --- a/controllers/factory/label/label.go +++ b/controllers/factory/label/label.go @@ -1,3 +1,19 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package label const ( diff --git a/controllers/factory/utils/utils.go b/controllers/factory/utils/utils.go index a3c1486c..4f9ceb7d 100644 --- a/controllers/factory/utils/utils.go +++ b/controllers/factory/utils/utils.go @@ -1,3 +1,19 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package utils import "hash/crc32" diff --git a/controllers/factory/vector/types.go b/controllers/factory/vector/types.go index dd9eefde..80806517 100644 --- a/controllers/factory/vector/types.go +++ b/controllers/factory/vector/types.go @@ -1,3 +1,19 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package vector type VectorConfig struct { diff --git a/controllers/factory/vector/vector.go b/controllers/factory/vector/vector.go index 5582e688..963cc0b3 100644 --- a/controllers/factory/vector/vector.go +++ b/controllers/factory/vector/vector.go @@ -1,3 +1,19 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package vector import ( diff --git a/controllers/factory/vectoragent/vector_agent_config.go b/controllers/factory/vectoragent/vector_agent_config.go index 2cca9e42..b7a5044d 100644 --- a/controllers/factory/vectoragent/vector_agent_config.go +++ b/controllers/factory/vectoragent/vector_agent_config.go @@ -1,3 +1,19 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package vectoragent import ( diff --git a/controllers/factory/vectoragent/vector_agent_controller.go b/controllers/factory/vectoragent/vector_agent_controller.go index 7ad079f5..1939859a 100644 --- a/controllers/factory/vectoragent/vector_agent_controller.go +++ b/controllers/factory/vectoragent/vector_agent_controller.go @@ -1,3 +1,19 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package vectoragent import ( diff --git a/controllers/factory/vectoragent/vector_agent_daemonset.go b/controllers/factory/vectoragent/vector_agent_daemonset.go index 3029c919..88a3593a 100644 --- a/controllers/factory/vectoragent/vector_agent_daemonset.go +++ b/controllers/factory/vectoragent/vector_agent_daemonset.go @@ -1,3 +1,19 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package vectoragent import ( diff --git a/controllers/factory/vectoragent/vector_agent_rbac.go b/controllers/factory/vectoragent/vector_agent_rbac.go index 6baf21ad..7cad24b4 100644 --- a/controllers/factory/vectoragent/vector_agent_rbac.go +++ b/controllers/factory/vectoragent/vector_agent_rbac.go @@ -1,3 +1,19 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package vectoragent import ( diff --git a/controllers/factory/vectoragent/vector_agent_service.go b/controllers/factory/vectoragent/vector_agent_service.go index 6d84d3d3..93d14c24 100644 --- a/controllers/factory/vectoragent/vector_agent_service.go +++ b/controllers/factory/vectoragent/vector_agent_service.go @@ -1,3 +1,19 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package vectoragent import ( diff --git a/controllers/factory/vectorpipeline/hash.go b/controllers/factory/vectorpipeline/hash.go index 8e6a83d9..946f7353 100644 --- a/controllers/factory/vectorpipeline/hash.go +++ b/controllers/factory/vectorpipeline/hash.go @@ -1,3 +1,19 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package vectorpipeline import ( diff --git a/controllers/factory/vectorpipeline/vectorpipeline.go b/controllers/factory/vectorpipeline/vectorpipeline.go index 224342c2..f468f247 100644 --- a/controllers/factory/vectorpipeline/vectorpipeline.go +++ b/controllers/factory/vectorpipeline/vectorpipeline.go @@ -1,3 +1,19 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package vectorpipeline import ( From bae4f8dfc198a7f35438e96b128dcf3243d7cd53 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Thu, 27 Oct 2022 09:46:41 +0300 Subject: [PATCH 029/316] Add __debug_bin to gitignore Signed-off-by: Zemtsov Vladimir --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index c2b8826e..bf6f53ac 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,5 @@ testbin/* *.swp *.swo *~ -.vscode \ No newline at end of file +.vscode +__debug_bin From 6785c7a9d41519c93f887ae6630fc4569ea516ae Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Thu, 27 Oct 2022 09:52:43 +0300 Subject: [PATCH 030/316] init clustervectorpipeline --- PROJECT | 9 ++ api/v1alpha1/clustervectorpipeline_types.go | 64 +++++++++++++ api/v1alpha1/zz_generated.deepcopy.go | 89 +++++++++++++++++++ ...ity.kaasops.io_clustervectorpipelines.yaml | 52 +++++++++++ config/crd/kustomization.yaml | 3 + ...cainjection_in_clustervectorpipelines.yaml | 7 ++ .../webhook_in_clustervectorpipelines.yaml | 16 ++++ .../clustervectorpipeline_editor_role.yaml | 24 +++++ .../clustervectorpipeline_viewer_role.yaml | 20 +++++ config/rbac/role.yaml | 26 ++++++ config/samples/kustomization.yaml | 1 + ...bility_v1alpha1_clustervectorpipeline.yaml | 6 ++ .../clustervectorpipeline_controller.go | 62 +++++++++++++ main.go | 7 ++ 14 files changed, 386 insertions(+) create mode 100644 api/v1alpha1/clustervectorpipeline_types.go create mode 100644 config/crd/bases/observability.kaasops.io_clustervectorpipelines.yaml create mode 100644 config/crd/patches/cainjection_in_clustervectorpipelines.yaml create mode 100644 config/crd/patches/webhook_in_clustervectorpipelines.yaml create mode 100644 config/rbac/clustervectorpipeline_editor_role.yaml create mode 100644 config/rbac/clustervectorpipeline_viewer_role.yaml create mode 100644 config/samples/observability_v1alpha1_clustervectorpipeline.yaml create mode 100644 controllers/clustervectorpipeline_controller.go diff --git a/PROJECT b/PROJECT index 05c036cc..fca8aa8e 100644 --- a/PROJECT +++ b/PROJECT @@ -25,4 +25,13 @@ resources: kind: VectorPipeline path: github.com/kaasops/vector-operator/api/v1alpha1 version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: kaasops.io + group: observability + kind: ClusterVectorPipeline + path: github.com/kaasops/vector-operator/api/v1alpha1 + version: v1alpha1 version: "3" diff --git a/api/v1alpha1/clustervectorpipeline_types.go b/api/v1alpha1/clustervectorpipeline_types.go new file mode 100644 index 00000000..d56e3db7 --- /dev/null +++ b/api/v1alpha1/clustervectorpipeline_types.go @@ -0,0 +1,64 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// ClusterVectorPipelineSpec defines the desired state of ClusterVectorPipeline +type ClusterVectorPipelineSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // Foo is an example field of ClusterVectorPipeline. Edit clustervectorpipeline_types.go to remove/update + Foo string `json:"foo,omitempty"` +} + +// ClusterVectorPipelineStatus defines the observed state of ClusterVectorPipeline +type ClusterVectorPipelineStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// ClusterVectorPipeline is the Schema for the clustervectorpipelines API +type ClusterVectorPipeline struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ClusterVectorPipelineSpec `json:"spec,omitempty"` + Status ClusterVectorPipelineStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// ClusterVectorPipelineList contains a list of ClusterVectorPipeline +type ClusterVectorPipelineList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ClusterVectorPipeline `json:"items"` +} + +func init() { + SchemeBuilder.Register(&ClusterVectorPipeline{}, &ClusterVectorPipelineList{}) +} diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index f36d6c1f..ce334621 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -26,6 +26,95 @@ import ( "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterVectorPipeline) DeepCopyInto(out *ClusterVectorPipeline) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterVectorPipeline. +func (in *ClusterVectorPipeline) DeepCopy() *ClusterVectorPipeline { + if in == nil { + return nil + } + out := new(ClusterVectorPipeline) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterVectorPipeline) 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 *ClusterVectorPipelineList) DeepCopyInto(out *ClusterVectorPipelineList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ClusterVectorPipeline, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterVectorPipelineList. +func (in *ClusterVectorPipelineList) DeepCopy() *ClusterVectorPipelineList { + if in == nil { + return nil + } + out := new(ClusterVectorPipelineList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterVectorPipelineList) 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 *ClusterVectorPipelineSpec) DeepCopyInto(out *ClusterVectorPipelineSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterVectorPipelineSpec. +func (in *ClusterVectorPipelineSpec) DeepCopy() *ClusterVectorPipelineSpec { + if in == nil { + return nil + } + out := new(ClusterVectorPipelineSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterVectorPipelineStatus) DeepCopyInto(out *ClusterVectorPipelineStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterVectorPipelineStatus. +func (in *ClusterVectorPipelineStatus) DeepCopy() *ClusterVectorPipelineStatus { + if in == nil { + return nil + } + out := new(ClusterVectorPipelineStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Vector) DeepCopyInto(out *Vector) { *out = *in diff --git a/config/crd/bases/observability.kaasops.io_clustervectorpipelines.yaml b/config/crd/bases/observability.kaasops.io_clustervectorpipelines.yaml new file mode 100644 index 00000000..0836390a --- /dev/null +++ b/config/crd/bases/observability.kaasops.io_clustervectorpipelines.yaml @@ -0,0 +1,52 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.2 + creationTimestamp: null + name: clustervectorpipelines.observability.kaasops.io +spec: + group: observability.kaasops.io + names: + kind: ClusterVectorPipeline + listKind: ClusterVectorPipelineList + plural: clustervectorpipelines + singular: clustervectorpipeline + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: ClusterVectorPipeline is the Schema for the clustervectorpipelines + API + 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: + type: object + spec: + description: ClusterVectorPipelineSpec defines the desired state of ClusterVectorPipeline + properties: + foo: + description: Foo is an example field of ClusterVectorPipeline. Edit + clustervectorpipeline_types.go to remove/update + type: string + type: object + status: + description: ClusterVectorPipelineStatus defines the observed state of + ClusterVectorPipeline + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 0121c8b1..d86cff3a 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -4,6 +4,7 @@ resources: - bases/observability.kaasops.io_vectors.yaml - bases/observability.kaasops.io_vectorpipelines.yaml +- bases/observability.kaasops.io_clustervectorpipelines.yaml #+kubebuilder:scaffold:crdkustomizeresource patchesStrategicMerge: @@ -11,12 +12,14 @@ patchesStrategicMerge: # patches here are for enabling the conversion webhook for each CRD #- patches/webhook_in_vectors.yaml #- patches/webhook_in_vectorpipelines.yaml +#- patches/webhook_in_clustervectorpipelines.yaml #+kubebuilder:scaffold:crdkustomizewebhookpatch # [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. # patches here are for enabling the CA injection for each CRD #- patches/cainjection_in_vectors.yaml #- patches/cainjection_in_vectorpipelines.yaml +#- patches/cainjection_in_clustervectorpipelines.yaml #+kubebuilder:scaffold:crdkustomizecainjectionpatch # the following config is for teaching kustomize how to do kustomization for CRDs. diff --git a/config/crd/patches/cainjection_in_clustervectorpipelines.yaml b/config/crd/patches/cainjection_in_clustervectorpipelines.yaml new file mode 100644 index 00000000..7b4b6265 --- /dev/null +++ b/config/crd/patches/cainjection_in_clustervectorpipelines.yaml @@ -0,0 +1,7 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: clustervectorpipelines.observability.kaasops.io diff --git a/config/crd/patches/webhook_in_clustervectorpipelines.yaml b/config/crd/patches/webhook_in_clustervectorpipelines.yaml new file mode 100644 index 00000000..634883c6 --- /dev/null +++ b/config/crd/patches/webhook_in_clustervectorpipelines.yaml @@ -0,0 +1,16 @@ +# The following patch enables a conversion webhook for the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: clustervectorpipelines.observability.kaasops.io +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + namespace: system + name: webhook-service + path: /convert + conversionReviewVersions: + - v1 diff --git a/config/rbac/clustervectorpipeline_editor_role.yaml b/config/rbac/clustervectorpipeline_editor_role.yaml new file mode 100644 index 00000000..6f7b4e4b --- /dev/null +++ b/config/rbac/clustervectorpipeline_editor_role.yaml @@ -0,0 +1,24 @@ +# permissions for end users to edit clustervectorpipelines. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: clustervectorpipeline-editor-role +rules: +- apiGroups: + - observability.kaasops.io + resources: + - clustervectorpipelines + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - observability.kaasops.io + resources: + - clustervectorpipelines/status + verbs: + - get diff --git a/config/rbac/clustervectorpipeline_viewer_role.yaml b/config/rbac/clustervectorpipeline_viewer_role.yaml new file mode 100644 index 00000000..95ab58ce --- /dev/null +++ b/config/rbac/clustervectorpipeline_viewer_role.yaml @@ -0,0 +1,20 @@ +# permissions for end users to view clustervectorpipelines. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: clustervectorpipeline-viewer-role +rules: +- apiGroups: + - observability.kaasops.io + resources: + - clustervectorpipelines + verbs: + - get + - list + - watch +- apiGroups: + - observability.kaasops.io + resources: + - clustervectorpipelines/status + verbs: + - get diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 9b633876..276b6e63 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -5,6 +5,32 @@ metadata: creationTimestamp: null name: manager-role rules: +- apiGroups: + - observability.kaasops.io + resources: + - clustervectorpipelines + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - observability.kaasops.io + resources: + - clustervectorpipelines/finalizers + verbs: + - update +- apiGroups: + - observability.kaasops.io + resources: + - clustervectorpipelines/status + verbs: + - get + - patch + - update - apiGroups: - observability.kaasops.io resources: diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index ab24709f..2dc737f5 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -2,4 +2,5 @@ resources: - observability_v1alpha1_vector.yaml - observability_v1alpha1_vectorpipeline.yaml +- observability_v1alpha1_clustervectorpipeline.yaml #+kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/samples/observability_v1alpha1_clustervectorpipeline.yaml b/config/samples/observability_v1alpha1_clustervectorpipeline.yaml new file mode 100644 index 00000000..4a453702 --- /dev/null +++ b/config/samples/observability_v1alpha1_clustervectorpipeline.yaml @@ -0,0 +1,6 @@ +apiVersion: observability.kaasops.io/v1alpha1 +kind: ClusterVectorPipeline +metadata: + name: clustervectorpipeline-sample +spec: + # TODO(user): Add fields here diff --git a/controllers/clustervectorpipeline_controller.go b/controllers/clustervectorpipeline_controller.go new file mode 100644 index 00000000..ca3691b0 --- /dev/null +++ b/controllers/clustervectorpipeline_controller.go @@ -0,0 +1,62 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controllers + +import ( + "context" + + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" + + observabilityv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" +) + +// ClusterVectorPipelineReconciler reconciles a ClusterVectorPipeline object +type ClusterVectorPipelineReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +//+kubebuilder:rbac:groups=observability.kaasops.io,resources=clustervectorpipelines,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=observability.kaasops.io,resources=clustervectorpipelines/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=observability.kaasops.io,resources=clustervectorpipelines/finalizers,verbs=update + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the ClusterVectorPipeline object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.12.2/pkg/reconcile +func (r *ClusterVectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + _ = log.FromContext(ctx) + + // TODO(user): your logic here + + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *ClusterVectorPipelineReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&observabilityv1alpha1.ClusterVectorPipeline{}). + Complete(r) +} diff --git a/main.go b/main.go index aa5c9188..9cf88ce9 100644 --- a/main.go +++ b/main.go @@ -118,6 +118,13 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "VectorPipeline") os.Exit(1) } + if err = (&controllers.ClusterVectorPipelineReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "ClusterVectorPipeline") + os.Exit(1) + } //+kubebuilder:scaffold:builder if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { From eb83ff9ad90a04d07ac0271c925de2a2ccc45b18 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Thu, 27 Oct 2022 16:41:36 +0300 Subject: [PATCH 031/316] Refactor Pipeline Signed-off-by: Zemtsov Vladimir --- CHANGELOG.md | 1 + LICENSE.md => LICENSE | 0 api/v1alpha1/clustervectorpipeline_types.go | 22 +- api/v1alpha1/zz_generated.deepcopy.go | 34 +-- ...ity.kaasops.io_clustervectorpipelines.yaml | 37 ++- .../clustervectorpipeline_controller.go | 72 ++++- controllers/factory/config/config_build.go | 34 +-- controllers/factory/config/config_check.go | 64 +++++ .../factory/config/configcheck/configcheck.go | 5 - .../clustervectorpipeline/vectorpipeline.go | 75 ++++++ .../{vectorpipeline => pipeline}/hash.go | 21 +- controllers/factory/pipeline/pipeline.go | 255 ++++++++++++++++++ .../pipeline/vectorpipeline/vectorpipeline.go | 75 ++++++ controllers/factory/vector/types.go | 7 +- .../vectoragent/vector_agent_config.go | 18 +- .../vectoragent/vector_agent_controller.go | 98 +++---- .../vectoragent/vector_agent_daemonset.go | 30 +-- .../factory/vectoragent/vector_agent_rbac.go | 24 +- .../vectoragent/vector_agent_service.go | 6 +- .../factory/vectorpipeline/vectorpipeline.go | 174 ------------ controllers/vector_controller.go | 2 +- controllers/vectorpipeline_controller.go | 70 ++--- main.go | 5 +- 23 files changed, 728 insertions(+), 401 deletions(-) rename LICENSE.md => LICENSE (100%) create mode 100644 controllers/factory/config/config_check.go create mode 100644 controllers/factory/pipeline/clustervectorpipeline/vectorpipeline.go rename controllers/factory/{vectorpipeline => pipeline}/hash.go (59%) create mode 100644 controllers/factory/pipeline/pipeline.go create mode 100644 controllers/factory/pipeline/vectorpipeline/vectorpipeline.go delete mode 100644 controllers/factory/vectorpipeline/vectorpipeline.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ae96357..0b943256 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ### Added +- Refactor: Refactor Pipeline for add ClusterVectorPipeline and checks - Feature: Add field reason to CR Vector and VectorPipeline - Feature: Add ConfigCheck for Vector - Feature: Add ConfigCheck for VectorPipeline diff --git a/LICENSE.md b/LICENSE similarity index 100% rename from LICENSE.md rename to LICENSE diff --git a/api/v1alpha1/clustervectorpipeline_types.go b/api/v1alpha1/clustervectorpipeline_types.go index d56e3db7..2a3c088e 100644 --- a/api/v1alpha1/clustervectorpipeline_types.go +++ b/api/v1alpha1/clustervectorpipeline_types.go @@ -23,31 +23,19 @@ import ( // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! // NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. -// ClusterVectorPipelineSpec defines the desired state of ClusterVectorPipeline -type ClusterVectorPipelineSpec struct { - // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - // Important: Run "make" to regenerate code after modifying this file - - // Foo is an example field of ClusterVectorPipeline. Edit clustervectorpipeline_types.go to remove/update - Foo string `json:"foo,omitempty"` -} - -// ClusterVectorPipelineStatus defines the observed state of ClusterVectorPipeline -type ClusterVectorPipelineStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file -} - //+kubebuilder:object:root=true //+kubebuilder:subresource:status +//+kubebuilder:resource:shortName=cvp +//+kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" +//+kubebuilder:printcolumn:name="Status",type="boolean",JSONPath=".status.configCheckResult" // ClusterVectorPipeline is the Schema for the clustervectorpipelines API type ClusterVectorPipeline struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - Spec ClusterVectorPipelineSpec `json:"spec,omitempty"` - Status ClusterVectorPipelineStatus `json:"status,omitempty"` + Spec VectorPipelineSpec `json:"spec,omitempty"` + Status VectorPipelineStatus `json:"status,omitempty"` } //+kubebuilder:object:root=true diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index ce334621..746cbf3f 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -31,8 +31,8 @@ func (in *ClusterVectorPipeline) DeepCopyInto(out *ClusterVectorPipeline) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec - out.Status = in.Status + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterVectorPipeline. @@ -85,36 +85,6 @@ func (in *ClusterVectorPipelineList) DeepCopyObject() runtime.Object { return nil } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ClusterVectorPipelineSpec) DeepCopyInto(out *ClusterVectorPipelineSpec) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterVectorPipelineSpec. -func (in *ClusterVectorPipelineSpec) DeepCopy() *ClusterVectorPipelineSpec { - if in == nil { - return nil - } - out := new(ClusterVectorPipelineSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ClusterVectorPipelineStatus) DeepCopyInto(out *ClusterVectorPipelineStatus) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterVectorPipelineStatus. -func (in *ClusterVectorPipelineStatus) DeepCopy() *ClusterVectorPipelineStatus { - if in == nil { - return nil - } - out := new(ClusterVectorPipelineStatus) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Vector) DeepCopyInto(out *Vector) { *out = *in diff --git a/config/crd/bases/observability.kaasops.io_clustervectorpipelines.yaml b/config/crd/bases/observability.kaasops.io_clustervectorpipelines.yaml index 0836390a..f22f7cbe 100644 --- a/config/crd/bases/observability.kaasops.io_clustervectorpipelines.yaml +++ b/config/crd/bases/observability.kaasops.io_clustervectorpipelines.yaml @@ -12,10 +12,19 @@ spec: kind: ClusterVectorPipeline listKind: ClusterVectorPipelineList plural: clustervectorpipelines + shortNames: + - cvp singular: clustervectorpipeline scope: Namespaced versions: - - name: v1alpha1 + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.configCheckResult + name: Status + type: boolean + name: v1alpha1 schema: openAPIV3Schema: description: ClusterVectorPipeline is the Schema for the clustervectorpipelines @@ -34,16 +43,28 @@ spec: metadata: type: object spec: - description: ClusterVectorPipelineSpec defines the desired state of ClusterVectorPipeline + description: VectorPipelineSpec defines the desired state of VectorPipeline properties: - foo: - description: Foo is an example field of ClusterVectorPipeline. Edit - clustervectorpipeline_types.go to remove/update - type: string + sinks: + type: object + x-kubernetes-preserve-unknown-fields: true + sources: + type: object + x-kubernetes-preserve-unknown-fields: true + transforms: + type: object + x-kubernetes-preserve-unknown-fields: true type: object status: - description: ClusterVectorPipelineStatus defines the observed state of - ClusterVectorPipeline + description: VectorPipelineStatus defines the observed state of VectorPipeline + properties: + LastAppliedPipelineHash: + format: int32 + type: integer + configCheckResult: + type: boolean + reason: + type: string type: object type: object served: true diff --git a/controllers/clustervectorpipeline_controller.go b/controllers/clustervectorpipeline_controller.go index ca3691b0..16339b4c 100644 --- a/controllers/clustervectorpipeline_controller.go +++ b/controllers/clustervectorpipeline_controller.go @@ -19,18 +19,24 @@ package controllers import ( "context" + "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" - observabilityv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" + "github.com/go-logr/logr" + vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" ) // ClusterVectorPipelineReconciler reconciles a ClusterVectorPipeline object type ClusterVectorPipelineReconciler struct { client.Client Scheme *runtime.Scheme + + // Temp. Wait this issue - https://github.com/kubernetes-sigs/controller-runtime/issues/452 + Clientset *kubernetes.Clientset } //+kubebuilder:rbac:groups=observability.kaasops.io,resources=clustervectorpipelines,verbs=get;list;watch;create;update;patch;delete @@ -47,16 +53,74 @@ type ClusterVectorPipelineReconciler struct { // For more details, check Reconcile and its Result here: // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.12.2/pkg/reconcile func (r *ClusterVectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - _ = log.FromContext(ctx) + log := log.FromContext(ctx).WithValues("VectorPipeline", req.Name) + + log.Info("start Reconcile ClusterVectorPipeline") + + // cvp, done, result, err := r.findClusterVectorPipelineCustomResourceInstance(ctx, log, req) + // if done { + // return result, err + // } + // hash, err := vectorpipeline.GetSpecHash(cvp.Spec) + // if err != nil { + // return ctrl.Result{}, err + // } + // if cvp.Status.LastAppliedPipelineHash != nil && *hash == *cvp.Status.LastAppliedPipelineHash { + // log.Info("ClusterVectorPipeline has no changes. Finish Reconcile ClusterVectorPipeline") + // return ctrl.Result{}, nil + // } + + // vectorInstances := &vectorv1alpha1.VectorList{} + // err = r.List(ctx, vectorInstances) + // if err != nil { + // return ctrl.Result{}, err + // } - // TODO(user): your logic here + // if len(vectorInstances.Items) == 0 { + // log.Info("Vertors not found") + // return ctrl.Result{}, nil + // } + // for _, v := range vectorInstances.Items { + // if v.DeletionTimestamp != nil { + // continue + // } + // if err = checkConfig(ctx, &v, vp, r.Client, r.Clientset); err != nil { + // return ctrl.Result{}, err + // } + // if err = vectorpipeline.SetLastAppliedPipelineStatus(ctx, vp, r.Client); err != nil { + // return ctrl.Result{}, err + // } + + // } + + log.Info("finish Reconcile ClusterVectorPipeline") return ctrl.Result{}, nil } +func (r *ClusterVectorPipelineReconciler) findClusterVectorPipelineCustomResourceInstance(ctx context.Context, log logr.Logger, req ctrl.Request) (*vectorv1alpha1.ClusterVectorPipeline, bool, ctrl.Result, error) { + // fetch the master instance + cvp := &vectorv1alpha1.ClusterVectorPipeline{} + err := r.Get(ctx, req.NamespacedName, cvp) + if err != nil { + if errors.IsNotFound(err) { + // Request object not found, could have been deleted after reconcile request. + // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers. + // Return and don't requeue + log.Info("ClusterVectorPipeline CR not found. Ignoring since object must be deleted") + return nil, true, ctrl.Result{}, nil + } + // Error reading the object - requeue the request. + log.Error(err, "Failed to get Vector") + return nil, true, ctrl.Result{}, err + } + log.Info("Get Vector Pipeline" + cvp.Name) + return cvp, false, ctrl.Result{}, nil +} + // SetupWithManager sets up the controller with the Manager. func (r *ClusterVectorPipelineReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). - For(&observabilityv1alpha1.ClusterVectorPipeline{}). + For(&vectorv1alpha1.ClusterVectorPipeline{}). Complete(r) } diff --git a/controllers/factory/config/config_build.go b/controllers/factory/config/config_build.go index 5a67f397..f326a5e0 100644 --- a/controllers/factory/config/config_build.go +++ b/controllers/factory/config/config_build.go @@ -19,11 +19,10 @@ package config import ( "context" "encoding/json" - "log" vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" + "github.com/kaasops/vector-operator/controllers/factory/pipeline" "github.com/kaasops/vector-operator/controllers/factory/vector" - "github.com/kaasops/vector-operator/controllers/factory/vectorpipeline" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -46,27 +45,28 @@ var ( ) func Get(ctx context.Context, v *vectorv1alpha1.Vector, c client.Client) ([]byte, error) { - vps, err := vectorpipeline.SelectSucceesed(ctx, c) + vCtrl := pipeline.NewController(ctx, c, nil) + pipelines, err := vCtrl.SelectSucceesed() if err != nil { return nil, err } - cfg, err := GenerateConfig(v, vps) + cfg, err := GenerateVectorConfig(v, pipelines) if err != nil { return nil, err } - return cfg, nil + return VectorConfigToJson(cfg) } -func GenerateConfig( +func GenerateVectorConfig( v *vectorv1alpha1.Vector, - vps []*vectorv1alpha1.VectorPipeline, -) ([]byte, error) { + vCtrls []pipeline.Controller, +) (*vector.VectorConfig, error) { cfg := vector.New(v.Spec.Agent.DataDir, v.Spec.Agent.ApiEnabled) - sources, transforms, sinks, err := getComponents(vps) + sources, transforms, sinks, err := getComponents(vCtrls) if err != nil { - log.Fatal(err) + return nil, err } if len(sources) == 0 { sources = sourceDefault @@ -79,16 +79,16 @@ func GenerateConfig( cfg.Sources = sources cfg.Transforms = transforms - return vectorConfigToJson(cfg) + return cfg, nil } -func getComponents(vps []*vectorv1alpha1.VectorPipeline) (map[string]interface{}, map[string]interface{}, map[string]interface{}, error) { +func getComponents(vCtrls []pipeline.Controller) (map[string]interface{}, map[string]interface{}, map[string]interface{}, error) { sourcesMap := make(map[string]interface{}) transformsMap := make(map[string]interface{}) sinksMap := make(map[string]interface{}) - for _, vp := range vps { - sources, err := vectorpipeline.GetSources(vp, nil) + for _, vCtrl := range vCtrls { + sources, err := vCtrl.GetSources(nil) if err != nil { return nil, nil, nil, err } @@ -99,7 +99,7 @@ func getComponents(vps []*vectorv1alpha1.VectorPipeline) (map[string]interface{} } sourcesMap[source.Name] = spec } - transforms, err := vectorpipeline.GetTransforms(vp) + transforms, err := vCtrl.GetTransforms() if err != nil { return nil, nil, nil, err } @@ -110,7 +110,7 @@ func getComponents(vps []*vectorv1alpha1.VectorPipeline) (map[string]interface{} } transformsMap[transform.Name] = spec } - sinks, err := vectorpipeline.GetSinks(vp) + sinks, err := vCtrl.GetSinks() if err != nil { return nil, nil, nil, err } @@ -125,7 +125,7 @@ func getComponents(vps []*vectorv1alpha1.VectorPipeline) (map[string]interface{} return sourcesMap, transformsMap, sinksMap, nil } -func vectorConfigToJson(conf *vector.VectorConfig) ([]byte, error) { +func VectorConfigToJson(conf *vector.VectorConfig) ([]byte, error) { data, err := json.Marshal(conf) if err != nil { return nil, err diff --git a/controllers/factory/config/config_check.go b/controllers/factory/config/config_check.go new file mode 100644 index 00000000..68921970 --- /dev/null +++ b/controllers/factory/config/config_check.go @@ -0,0 +1,64 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package config + +import ( + "context" + + vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" + "github.com/kaasops/vector-operator/controllers/factory/config/configcheck" + "github.com/kaasops/vector-operator/controllers/factory/pipeline" + "k8s.io/client-go/kubernetes" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" +) + +func Check(ctx context.Context, v *vectorv1alpha1.Vector, vCtrl *pipeline.Controller, c client.Client, cs *kubernetes.Clientset) error { + + log := log.FromContext(context.TODO()).WithValues("ConfigCheck Vector Pipeline", vCtrl.Pipeline.Name()) + + var vCtrls []pipeline.Controller + vCtrls = append(vCtrls, *vCtrl) + + cfg, err := GenerateVectorConfig(v, vCtrls) + if err != nil { + return err + } + + // if p.Type() == vectorpipeline.Type { + // log.Info("It's VectorPipeline") + // } + + cfgJson, err := VectorConfigToJson(cfg) + if err != nil { + return err + } + + err = configcheck.Run(cfgJson, c, cs, v.Name, v.Namespace, v.Spec.Agent.Image) + if _, ok := err.(*configcheck.ErrConfigCheck); ok { + if err := vCtrl.SetFailedStatus(err); err != nil { + return err + } + log.Error(err, "Vector Config has error") + return nil + } + if err != nil { + return err + } + + return vCtrl.SetSucceesStatus() +} diff --git a/controllers/factory/config/configcheck/configcheck.go b/controllers/factory/config/configcheck/configcheck.go index 16616087..5204c01b 100644 --- a/controllers/factory/config/configcheck/configcheck.go +++ b/controllers/factory/config/configcheck/configcheck.go @@ -84,11 +84,6 @@ func ensureVectorConfigCheckServiceAccount(c client.Client, ns string) (bool, ct return helper.ReconcileResult(err) } func ensureVectorConfigCheckConfig(c client.Client, cfg []byte, name, ns, hash string) error { - // ctx := context.Background() - // log := log.FromContext(ctx).WithValues("vector-config-check-secret", "ConfigCheck") - - // log.Info("start Create Config Check Secret") - vectorConfigCheckSecret, err := createVectorConfigCheckConfig(cfg, name, ns, hash) if err != nil { return err diff --git a/controllers/factory/pipeline/clustervectorpipeline/vectorpipeline.go b/controllers/factory/pipeline/clustervectorpipeline/vectorpipeline.go new file mode 100644 index 00000000..993d15a4 --- /dev/null +++ b/controllers/factory/pipeline/clustervectorpipeline/vectorpipeline.go @@ -0,0 +1,75 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package clustervectorpipeline + +import ( + "context" + + vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" + "github.com/kaasops/vector-operator/controllers/factory/k8sutils" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +var ( + Type = "cluster" +) + +type Controller struct { + ClusterVectorPipeline *vectorv1alpha1.ClusterVectorPipeline +} + +func (ctrl *Controller) Spec() vectorv1alpha1.VectorPipelineSpec { + return ctrl.ClusterVectorPipeline.Spec +} + +func (ctrl *Controller) Name() string { + return ctrl.ClusterVectorPipeline.Name +} + +func (ctrl *Controller) Namespace() string { + return ctrl.ClusterVectorPipeline.Namespace +} + +func (ctrl *Controller) Type() string { + return Type +} + +func (ctrl *Controller) SetConfigCheck(value bool) { + ctrl.ClusterVectorPipeline.Status.ConfigCheckResult = &value +} + +func (ctrl *Controller) SetReason(reason *string) { + ctrl.ClusterVectorPipeline.Status.Reason = reason +} + +func (ctrl *Controller) GetLastAppliedPipeline() *uint32 { + return ctrl.ClusterVectorPipeline.Status.LastAppliedPipelineHash +} + +func (ctrl *Controller) SetLastAppliedPipeline(hash *uint32) { + ctrl.ClusterVectorPipeline.Status.LastAppliedPipelineHash = hash +} + +func (ctrl *Controller) UpdateStatus(ctx context.Context, c client.Client) error { + return k8sutils.UpdateStatus(ctx, ctrl.ClusterVectorPipeline, c) +} + +func NewController(cvp *vectorv1alpha1.ClusterVectorPipeline) *Controller { + return &Controller{ + ClusterVectorPipeline: cvp, + } +} diff --git a/controllers/factory/vectorpipeline/hash.go b/controllers/factory/pipeline/hash.go similarity index 59% rename from controllers/factory/vectorpipeline/hash.go rename to controllers/factory/pipeline/hash.go index 946f7353..bfb73065 100644 --- a/controllers/factory/vectorpipeline/hash.go +++ b/controllers/factory/pipeline/hash.go @@ -14,20 +14,33 @@ See the License for the specific language governing permissions and limitations under the License. */ -package vectorpipeline +package pipeline import ( "encoding/json" - vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" "github.com/kaasops/vector-operator/controllers/factory/utils" ) -func GetVpSpecHash(vp *vectorv1alpha1.VectorPipeline) (*uint32, error) { - a, err := json.Marshal(vp.Spec) +func (ctrl *Controller) GetSpecHash() (*uint32, error) { + a, err := json.Marshal(ctrl.Pipeline.Spec()) if err != nil { return nil, err } hash := utils.GetHash(a) return &hash, nil } + +// CheckHash returns true, if hash in .status.lastAppliedPipelineHash matches with spec Hash +func (ctrl *Controller) CheckHash() (bool, error) { + hash, err := ctrl.GetSpecHash() + if err != nil { + return false, err + } + + if ctrl.Pipeline.GetLastAppliedPipeline() != nil && *hash == *ctrl.Pipeline.GetLastAppliedPipeline() { + return true, nil + } + + return false, nil +} diff --git a/controllers/factory/pipeline/pipeline.go b/controllers/factory/pipeline/pipeline.go new file mode 100644 index 00000000..f6108ddf --- /dev/null +++ b/controllers/factory/pipeline/pipeline.go @@ -0,0 +1,255 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package pipeline + +import ( + "context" + "encoding/json" + + vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" + "github.com/kaasops/vector-operator/controllers/factory/pipeline/clustervectorpipeline" + "github.com/kaasops/vector-operator/controllers/factory/pipeline/vectorpipeline" + "github.com/kaasops/vector-operator/controllers/factory/vector" + "github.com/mitchellh/mapstructure" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +type Pipeline interface { + Spec() vectorv1alpha1.VectorPipelineSpec + Name() string + Namespace() string + Type() string + SetConfigCheck(bool) + SetReason(*string) + GetLastAppliedPipeline() *uint32 + SetLastAppliedPipeline(*uint32) + UpdateStatus(context.Context, client.Client) error +} + +type Controller struct { + Pipeline Pipeline + Ctx context.Context + Client client.Client +} + +func NewController(ctx context.Context, c client.Client, p Pipeline) *Controller { + return &Controller{ + Pipeline: p, + Ctx: ctx, + Client: c, + } +} + +func (ctrl *Controller) SelectSucceesed() ([]Controller, error) { + var combined []Controller + + vpCombined, err := ctrl.SelectVectorPipelineSucceesed() + if err != nil { + return nil, err + } + + cvpCombined, err := ctrl.SelectClusterVectorPipelineSucceesed() + if err != nil { + return nil, err + } + + for _, vp := range vpCombined { + combined = append(combined, Controller{ + Pipeline: vp, + }) + } + for _, cvp := range cvpCombined { + combined = append(combined, Controller{ + Pipeline: cvp, + }) + } + + return combined, nil + +} + +func (ctrl *Controller) SelectVectorPipelineSucceesed() ([]*vectorpipeline.Controller, error) { + var vpCombined []*vectorpipeline.Controller + + vps := vectorv1alpha1.VectorPipelineList{} + err := ctrl.Client.List(ctrl.Ctx, &vps) + if err != nil { + return nil, err + } + + for _, vp := range vps.Items { + if !vp.DeletionTimestamp.IsZero() { + continue + } + if vp.Status.ConfigCheckResult != nil { + if *vp.Status.ConfigCheckResult { + vpCombined = append(vpCombined, &vectorpipeline.Controller{ + VectorPipeline: vp.DeepCopy(), + }) + } + } + + } + return vpCombined, nil +} + +func (ctrl *Controller) SelectClusterVectorPipelineSucceesed() ([]*clustervectorpipeline.Controller, error) { + var cvpCombined []*clustervectorpipeline.Controller + + cvps := vectorv1alpha1.ClusterVectorPipelineList{} + err := ctrl.Client.List(ctrl.Ctx, &cvps) + if err != nil { + return nil, err + } + + for _, cvp := range cvps.Items { + if !cvp.DeletionTimestamp.IsZero() { + continue + } + if cvp.Status.ConfigCheckResult != nil { + if *cvp.Status.ConfigCheckResult { + cvpCombined = append(cvpCombined, &clustervectorpipeline.Controller{ + ClusterVectorPipeline: cvp.DeepCopy(), + }) + } + } + + } + return cvpCombined, nil +} + +func (ctrl *Controller) GetSources(filter []string) ([]vector.Source, error) { + var sources []vector.Source + sourcesMap, err := decodeRaw(ctrl.Pipeline.Spec().Sources.Raw) + if err != nil { + return nil, err + } + for k, v := range sourcesMap { + if len(filter) != 0 { + if !contains(filter, k) { + continue + } + } + var source vector.Source + if err := mapstructure.Decode(v, &source); err != nil { + return nil, err + } + source.Name = addPrefix(ctrl.Pipeline.Namespace(), ctrl.Pipeline.Name(), k) + sources = append(sources, source) + } + return sources, nil +} + +func (ctrl *Controller) GetTransforms() ([]vector.Transform, error) { + if ctrl.Pipeline.Spec().Transforms == nil { + return nil, nil + } + transformsMap, err := decodeRaw(ctrl.Pipeline.Spec().Transforms.Raw) + if err != nil { + return nil, err + } + var transforms []vector.Transform + if err := json.Unmarshal(ctrl.Pipeline.Spec().Transforms.Raw, &transformsMap); err != nil { + return nil, err + } + for k, v := range transformsMap { + var transform vector.Transform + if err := mapstructure.Decode(v, &transform); err != nil { + return nil, err + } + transform.Name = addPrefix(ctrl.Pipeline.Namespace(), ctrl.Pipeline.Name(), k) + for i, inputName := range transform.Inputs { + transform.Inputs[i] = addPrefix(ctrl.Pipeline.Namespace(), ctrl.Pipeline.Name(), inputName) + } + transforms = append(transforms, transform) + } + return transforms, nil +} + +func (ctrl *Controller) GetSinks() ([]vector.Sink, error) { + sinksMap, err := decodeRaw(ctrl.Pipeline.Spec().Sinks.Raw) + if err != nil { + return nil, err + } + var sinks []vector.Sink + for k, v := range sinksMap { + var sink vector.Sink + if err := mapstructure.Decode(v, &sink); err != nil { + return nil, err + } + sink.Name = addPrefix(ctrl.Pipeline.Namespace(), ctrl.Pipeline.Name(), k) + for i, inputName := range sink.Inputs { + sink.Inputs[i] = addPrefix(ctrl.Pipeline.Namespace(), ctrl.Pipeline.Name(), inputName) + } + sinks = append(sinks, sink) + } + return sinks, nil +} + +func (ctrl *Controller) SetSucceesStatus() error { + var status = true + + ctrl.Pipeline.SetConfigCheck(status) + ctrl.Pipeline.SetReason(nil) + + return ctrl.Pipeline.UpdateStatus(ctrl.Ctx, ctrl.Client) +} + +func (ctrl *Controller) SetFailedStatus(err error) error { + var status = false + var reason = err.Error() + + ctrl.Pipeline.SetConfigCheck(status) + ctrl.Pipeline.SetReason(&reason) + + return ctrl.Pipeline.UpdateStatus(ctrl.Ctx, ctrl.Client) +} + +func (ctrl *Controller) SetLastAppliedPipelineStatus() error { + hash, err := ctrl.GetSpecHash() + if err != nil { + return err + } + ctrl.Pipeline.SetLastAppliedPipeline(hash) + + return ctrl.Pipeline.UpdateStatus(ctrl.Ctx, ctrl.Client) +} + +func decodeRaw(raw []byte) (map[string]interface{}, error) { + result := make(map[string]interface{}) + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return result, nil +} + +func contains(elems []string, v string) bool { + for _, s := range elems { + if v == s { + return true + } + } + return false +} + +func addPrefix(Namespace, Name, componentName string) string { + return generateName(Namespace, Name) + "-" + componentName +} + +func generateName(Namespace, Name string) string { + return Namespace + "-" + Name +} diff --git a/controllers/factory/pipeline/vectorpipeline/vectorpipeline.go b/controllers/factory/pipeline/vectorpipeline/vectorpipeline.go new file mode 100644 index 00000000..2503d90e --- /dev/null +++ b/controllers/factory/pipeline/vectorpipeline/vectorpipeline.go @@ -0,0 +1,75 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package vectorpipeline + +import ( + "context" + + vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" + "github.com/kaasops/vector-operator/controllers/factory/k8sutils" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +var ( + Type = "noCluster" +) + +type Controller struct { + VectorPipeline *vectorv1alpha1.VectorPipeline +} + +func (ctrl *Controller) Spec() vectorv1alpha1.VectorPipelineSpec { + return ctrl.VectorPipeline.Spec +} + +func (ctrl *Controller) Name() string { + return ctrl.VectorPipeline.Name +} + +func (ctrl *Controller) Namespace() string { + return ctrl.VectorPipeline.Namespace +} + +func (ctrl *Controller) Type() string { + return Type +} + +func (ctrl *Controller) SetConfigCheck(value bool) { + ctrl.VectorPipeline.Status.ConfigCheckResult = &value +} + +func (ctrl *Controller) SetReason(reason *string) { + ctrl.VectorPipeline.Status.Reason = reason +} + +func (ctrl *Controller) GetLastAppliedPipeline() *uint32 { + return ctrl.VectorPipeline.Status.LastAppliedPipelineHash +} + +func (ctrl *Controller) SetLastAppliedPipeline(hash *uint32) { + ctrl.VectorPipeline.Status.LastAppliedPipelineHash = hash +} + +func (ctrl *Controller) UpdateStatus(ctx context.Context, c client.Client) error { + return k8sutils.UpdateStatus(ctx, ctrl.VectorPipeline, c) +} + +func NewController(vp *vectorv1alpha1.VectorPipeline) *Controller { + return &Controller{ + VectorPipeline: vp, + } +} diff --git a/controllers/factory/vector/types.go b/controllers/factory/vector/types.go index 80806517..f766446b 100644 --- a/controllers/factory/vector/types.go +++ b/controllers/factory/vector/types.go @@ -31,9 +31,10 @@ type ApiSpec struct { } type Source struct { - Name string - Type string `mapper:"type"` - Options map[string]interface{} `mapstructure:",remain"` + Name string + Type string `mapper:"type"` + ExtraNamespaceLabelSelector string `mapper:"extra_namespace_label_selector"` + Options map[string]interface{} `mapstructure:",remain"` } type Transform struct { diff --git a/controllers/factory/vectoragent/vector_agent_config.go b/controllers/factory/vectoragent/vector_agent_config.go index b7a5044d..b1979239 100644 --- a/controllers/factory/vectoragent/vector_agent_config.go +++ b/controllers/factory/vectoragent/vector_agent_config.go @@ -26,18 +26,18 @@ import ( "github.com/kaasops/vector-operator/controllers/factory/utils" ) -func (vr *VectorAgentReconciler) createVectorAgentConfig(ctx context.Context) (*corev1.Secret, error) { - cfg, err := config.Get(ctx, vr.Vector, vr.Client) +func (ctrl *Controller) createVectorAgentConfig(ctx context.Context) (*corev1.Secret, error) { + cfg, err := config.Get(ctx, ctrl.Vector, ctrl.Client) if err != nil { return nil, err } cfgHash := utils.GetHash(cfg) - if vr.Vector.Status.LastAppliedConfigHash == nil || *vr.Vector.Status.LastAppliedConfigHash != cfgHash { - err = configcheck.Run(cfg, vr.Client, vr.Clientset, vr.Vector.Name, vr.Vector.Namespace, vr.Vector.Spec.Agent.Image) + if ctrl.Vector.Status.LastAppliedConfigHash == nil || *ctrl.Vector.Status.LastAppliedConfigHash != cfgHash { + err = configcheck.Run(cfg, ctrl.Client, ctrl.Clientset, ctrl.Vector.Name, ctrl.Vector.Namespace, ctrl.Vector.Spec.Agent.Image) if _, ok := err.(*configcheck.ErrConfigCheck); ok { - if err := setFailedStatus(ctx, vr.Vector, vr.Client, err); err != nil { + if err := setFailedStatus(ctx, ctrl.Vector, ctrl.Client, err); err != nil { return nil, err } return nil, err @@ -46,23 +46,23 @@ func (vr *VectorAgentReconciler) createVectorAgentConfig(ctx context.Context) (* return nil, err } - if err := SetLastAppliedPipelineStatus(ctx, vr.Vector, vr.Client, &cfgHash); err != nil { + if err := SetLastAppliedPipelineStatus(ctx, ctrl.Vector, ctrl.Client, &cfgHash); err != nil { return nil, err } - if err := setSucceesStatus(ctx, vr.Vector, vr.Client); err != nil { + if err := setSucceesStatus(ctx, ctrl.Vector, ctrl.Client); err != nil { return nil, err } } - labels := vr.labelsForVectorAgent() + labels := ctrl.labelsForVectorAgent() config := map[string][]byte{ "agent.json": cfg, } secret := &corev1.Secret{ - ObjectMeta: vr.objectMetaVectorAgent(labels), + ObjectMeta: ctrl.objectMetaVectorAgent(labels), Data: config, } diff --git a/controllers/factory/vectoragent/vector_agent_controller.go b/controllers/factory/vectoragent/vector_agent_controller.go index 1939859a..e747dddc 100644 --- a/controllers/factory/vectoragent/vector_agent_controller.go +++ b/controllers/factory/vectoragent/vector_agent_controller.go @@ -31,7 +31,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" ) -type VectorAgentReconciler struct { +type Controller struct { client.Client Vector *vectorv1alpha1.Vector @@ -39,157 +39,157 @@ type VectorAgentReconciler struct { Clientset *kubernetes.Clientset } -func NewReconciler(v *vectorv1alpha1.Vector, c client.Client, cs *kubernetes.Clientset) *VectorAgentReconciler { - return &VectorAgentReconciler{ +func NewController(v *vectorv1alpha1.Vector, c client.Client, cs *kubernetes.Clientset) *Controller { + return &Controller{ Client: c, Vector: v, Clientset: cs, } } -func (vr *VectorAgentReconciler) EnsureVectorAgent() (done bool, result ctrl.Result, err error) { +func (ctrl *Controller) EnsureVectorAgent() (done bool, result ctrl.Result, err error) { ctx := context.Background() - log := log.FromContext(ctx).WithValues("vector-agent", vr.Vector.Name) + log := log.FromContext(ctx).WithValues("vector-agent", ctrl.Vector.Name) log.Info("start Reconcile Vector Agent") - if done, result, err = vr.ensureVectorAgentRBAC(); done { + if done, result, err = ctrl.ensureVectorAgentRBAC(); done { return } - if vr.Vector.Spec.Agent.Service { - if done, result, err = vr.ensureVectorAgentService(); done { + if ctrl.Vector.Spec.Agent.Service { + if done, result, err = ctrl.ensureVectorAgentService(); done { return } } - if done, result, err = vr.ensureVectorAgentConfig(); done { + if done, result, err = ctrl.ensureVectorAgentConfig(); done { return } - if done, result, err = vr.ensureVectorAgentDaemonSet(); done { + if done, result, err = ctrl.ensureVectorAgentDaemonSet(); done { return } return } -func (vr *VectorAgentReconciler) ensureVectorAgentRBAC() (bool, ctrl.Result, error) { +func (ctrl *Controller) ensureVectorAgentRBAC() (bool, ctrl.Result, error) { ctx := context.Background() - log := log.FromContext(ctx).WithValues("vector-agent-rbac", vr.Vector.Name) + log := log.FromContext(ctx).WithValues("vector-agent-rbac", ctrl.Vector.Name) log.Info("start Reconcile Vector Agent RBAC") - if done, _, err := vr.ensureVectorAgentServiceAccount(); done { + if done, _, err := ctrl.ensureVectorAgentServiceAccount(); done { return helper.ReconcileResult(err) } - if done, _, err := vr.ensureVectorAgentClusterRole(); done { + if done, _, err := ctrl.ensureVectorAgentClusterRole(); done { return helper.ReconcileResult(err) } - if done, _, err := vr.ensureVectorAgentClusterRoleBinding(); done { + if done, _, err := ctrl.ensureVectorAgentClusterRoleBinding(); done { return helper.ReconcileResult(err) } return helper.ReconcileResult(nil) } -func (vr *VectorAgentReconciler) ensureVectorAgentServiceAccount() (bool, ctrl.Result, error) { - vectorAgentServiceAccount := vr.createVectorAgentServiceAccount() +func (ctrl *Controller) ensureVectorAgentServiceAccount() (bool, ctrl.Result, error) { + vectorAgentServiceAccount := ctrl.createVectorAgentServiceAccount() - _, err := k8sutils.CreateOrUpdateServiceAccount(vectorAgentServiceAccount, vr.Client) + _, err := k8sutils.CreateOrUpdateServiceAccount(vectorAgentServiceAccount, ctrl.Client) return helper.ReconcileResult(err) } -func (vr *VectorAgentReconciler) ensureVectorAgentClusterRole() (bool, ctrl.Result, error) { - vectorAgentClusterRole := vr.createVectorAgentClusterRole() +func (ctrl *Controller) ensureVectorAgentClusterRole() (bool, ctrl.Result, error) { + vectorAgentClusterRole := ctrl.createVectorAgentClusterRole() - _, err := k8sutils.CreateOrUpdateClusterRole(vectorAgentClusterRole, vr.Client) + _, err := k8sutils.CreateOrUpdateClusterRole(vectorAgentClusterRole, ctrl.Client) return helper.ReconcileResult(err) } -func (vr *VectorAgentReconciler) ensureVectorAgentClusterRoleBinding() (bool, ctrl.Result, error) { - vectorAgentClusterRoleBinding := vr.createVectorAgentClusterRoleBinding() +func (ctrl *Controller) ensureVectorAgentClusterRoleBinding() (bool, ctrl.Result, error) { + vectorAgentClusterRoleBinding := ctrl.createVectorAgentClusterRoleBinding() - _, err := k8sutils.CreateOrUpdateClusterRoleBinding(vectorAgentClusterRoleBinding, vr.Client) + _, err := k8sutils.CreateOrUpdateClusterRoleBinding(vectorAgentClusterRoleBinding, ctrl.Client) return helper.ReconcileResult(err) } -func (vr *VectorAgentReconciler) ensureVectorAgentService() (bool, ctrl.Result, error) { +func (ctrl *Controller) ensureVectorAgentService() (bool, ctrl.Result, error) { ctx := context.Background() - log := log.FromContext(ctx).WithValues("vector-agent-service", vr.Vector.Name) + log := log.FromContext(ctx).WithValues("vector-agent-service", ctrl.Vector.Name) log.Info("start Reconcile Vector Agent Service") - vectorAgentService := vr.createVectorAgentService() + vectorAgentService := ctrl.createVectorAgentService() - _, err := k8sutils.CreateOrUpdateService(vectorAgentService, vr.Client) + _, err := k8sutils.CreateOrUpdateService(vectorAgentService, ctrl.Client) return helper.ReconcileResult(err) } -func (vr *VectorAgentReconciler) ensureVectorAgentConfig() (bool, ctrl.Result, error) { +func (ctrl *Controller) ensureVectorAgentConfig() (bool, ctrl.Result, error) { ctx := context.Background() - log := log.FromContext(ctx).WithValues("vector-agent-secret", vr.Vector.Name) + log := log.FromContext(ctx).WithValues("vector-agent-secret", ctrl.Vector.Name) log.Info("start Reconcile Vector Agent Secret") - vectorAgentSecret, err := vr.createVectorAgentConfig(ctx) + vectorAgentSecret, err := ctrl.createVectorAgentConfig(ctx) if err != nil { return helper.ReconcileResult(err) } - _, err = k8sutils.CreateOrUpdateSecret(vectorAgentSecret, vr.Client) + _, err = k8sutils.CreateOrUpdateSecret(vectorAgentSecret, ctrl.Client) return helper.ReconcileResult(err) } -func (vr *VectorAgentReconciler) ensureVectorAgentDaemonSet() (bool, ctrl.Result, error) { +func (ctrl *Controller) ensureVectorAgentDaemonSet() (bool, ctrl.Result, error) { ctx := context.Background() - log := log.FromContext(ctx).WithValues("vector-agent-daemon-set", vr.Vector.Name) + log := log.FromContext(ctx).WithValues("vector-agent-daemon-set", ctrl.Vector.Name) log.Info("start Reconcile Vector Agent DaemonSet") - vectorAgentDaemonSet := vr.createVectorAgentDaemonSet() + vectorAgentDaemonSet := ctrl.createVectorAgentDaemonSet() - _, err := k8sutils.CreateOrUpdateDaemonSet(vectorAgentDaemonSet, vr.Client) + _, err := k8sutils.CreateOrUpdateDaemonSet(vectorAgentDaemonSet, ctrl.Client) return helper.ReconcileResult(err) } -func (vr *VectorAgentReconciler) labelsForVectorAgent() map[string]string { +func (ctrl *Controller) labelsForVectorAgent() map[string]string { return map[string]string{ label.ManagedByLabelKey: "vector-operator", label.NameLabelKey: "vector", label.ComponentLabelKey: "Agent", - label.InstanceLabelKey: vr.Vector.Name, + label.InstanceLabelKey: ctrl.Vector.Name, label.VectorExcludeLabel: "true", } } -func (vr *VectorAgentReconciler) objectMetaVectorAgent(labels map[string]string) metav1.ObjectMeta { +func (ctrl *Controller) objectMetaVectorAgent(labels map[string]string) metav1.ObjectMeta { return metav1.ObjectMeta{ - Name: vr.Vector.Name + "-agent", - Namespace: vr.Vector.Namespace, + Name: ctrl.Vector.Name + "-agent", + Namespace: ctrl.Vector.Namespace, Labels: labels, - OwnerReferences: vr.getControllerReference(), + OwnerReferences: ctrl.getControllerReference(), } } -func (vr *VectorAgentReconciler) getNameVectorAgent() string { - name := vr.Vector.Name + "-agent" +func (ctrl *Controller) getNameVectorAgent() string { + name := ctrl.Vector.Name + "-agent" return name } -func (vr *VectorAgentReconciler) getControllerReference() []metav1.OwnerReference { +func (ctrl *Controller) getControllerReference() []metav1.OwnerReference { return []metav1.OwnerReference{ { - APIVersion: vr.Vector.APIVersion, - Kind: vr.Vector.Kind, - Name: vr.Vector.GetName(), - UID: vr.Vector.GetUID(), + APIVersion: ctrl.Vector.APIVersion, + Kind: ctrl.Vector.Kind, + Name: ctrl.Vector.GetName(), + UID: ctrl.Vector.GetUID(), BlockOwnerDeletion: pointer.BoolPtr(true), Controller: pointer.BoolPtr(true), }, diff --git a/controllers/factory/vectoragent/vector_agent_daemonset.go b/controllers/factory/vectoragent/vector_agent_daemonset.go index 88a3593a..e551b8df 100644 --- a/controllers/factory/vectoragent/vector_agent_daemonset.go +++ b/controllers/factory/vectoragent/vector_agent_daemonset.go @@ -22,26 +22,26 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func (vr *VectorAgentReconciler) createVectorAgentDaemonSet() *appsv1.DaemonSet { - labels := vr.labelsForVectorAgent() +func (ctrl *Controller) createVectorAgentDaemonSet() *appsv1.DaemonSet { + labels := ctrl.labelsForVectorAgent() daemonset := &appsv1.DaemonSet{ - ObjectMeta: vr.objectMetaVectorAgent(labels), + ObjectMeta: ctrl.objectMetaVectorAgent(labels), Spec: appsv1.DaemonSetSpec{ Selector: &metav1.LabelSelector{MatchLabels: labels}, Template: corev1.PodTemplateSpec{ - ObjectMeta: vr.objectMetaVectorAgent(labels), + ObjectMeta: ctrl.objectMetaVectorAgent(labels), Spec: corev1.PodSpec{ - ServiceAccountName: vr.getNameVectorAgent(), - Volumes: vr.generateVectorAgentVolume(), + ServiceAccountName: ctrl.getNameVectorAgent(), + Volumes: ctrl.generateVectorAgentVolume(), SecurityContext: &corev1.PodSecurityContext{}, - Tolerations: vr.Vector.Spec.Agent.Tolerations, + Tolerations: ctrl.Vector.Spec.Agent.Tolerations, Containers: []corev1.Container{ { - Name: vr.getNameVectorAgent(), - Image: vr.Vector.Spec.Agent.Image, + Name: ctrl.getNameVectorAgent(), + Image: ctrl.Vector.Spec.Agent.Image, Args: []string{"--config-dir", "/etc/vector/", "--watch-config"}, - Env: vr.generateVectorAgentEnvs(), + Env: ctrl.generateVectorAgentEnvs(), Ports: []corev1.ContainerPort{ { Name: "prom-exporter", @@ -49,7 +49,7 @@ func (vr *VectorAgentReconciler) createVectorAgentDaemonSet() *appsv1.DaemonSet Protocol: "TCP", }, }, - VolumeMounts: vr.generateVectorAgentVolumeMounts(), + VolumeMounts: ctrl.generateVectorAgentVolumeMounts(), SecurityContext: &corev1.SecurityContext{}, }, }, @@ -61,13 +61,13 @@ func (vr *VectorAgentReconciler) createVectorAgentDaemonSet() *appsv1.DaemonSet return daemonset } -func (vr *VectorAgentReconciler) generateVectorAgentVolume() []corev1.Volume { +func (ctrl *Controller) generateVectorAgentVolume() []corev1.Volume { volume := []corev1.Volume{ { Name: "config", VolumeSource: corev1.VolumeSource{ Secret: &corev1.SecretVolumeSource{ - SecretName: vr.getNameVectorAgent(), + SecretName: ctrl.getNameVectorAgent(), }, }, }, @@ -116,7 +116,7 @@ func (vr *VectorAgentReconciler) generateVectorAgentVolume() []corev1.Volume { return volume } -func (vr *VectorAgentReconciler) generateVectorAgentVolumeMounts() []corev1.VolumeMount { +func (ctrl *Controller) generateVectorAgentVolumeMounts() []corev1.VolumeMount { volumeMount := []corev1.VolumeMount{ { Name: "config", @@ -147,7 +147,7 @@ func (vr *VectorAgentReconciler) generateVectorAgentVolumeMounts() []corev1.Volu return volumeMount } -func (vr *VectorAgentReconciler) generateVectorAgentEnvs() []corev1.EnvVar { +func (ctrl *Controller) generateVectorAgentEnvs() []corev1.EnvVar { envs := []corev1.EnvVar{ { Name: "VECTOR_SELF_NODE_NAME", diff --git a/controllers/factory/vectoragent/vector_agent_rbac.go b/controllers/factory/vectoragent/vector_agent_rbac.go index 7cad24b4..190c2d8a 100644 --- a/controllers/factory/vectoragent/vector_agent_rbac.go +++ b/controllers/factory/vectoragent/vector_agent_rbac.go @@ -21,21 +21,21 @@ import ( rbacv1 "k8s.io/api/rbac/v1" ) -func (vr *VectorAgentReconciler) createVectorAgentServiceAccount() *corev1.ServiceAccount { - labels := vr.labelsForVectorAgent() +func (ctrl *Controller) createVectorAgentServiceAccount() *corev1.ServiceAccount { + labels := ctrl.labelsForVectorAgent() serviceAccount := &corev1.ServiceAccount{ - ObjectMeta: vr.objectMetaVectorAgent(labels), + ObjectMeta: ctrl.objectMetaVectorAgent(labels), } return serviceAccount } -func (vr *VectorAgentReconciler) createVectorAgentClusterRole() *rbacv1.ClusterRole { - labels := vr.labelsForVectorAgent() +func (ctrl *Controller) createVectorAgentClusterRole() *rbacv1.ClusterRole { + labels := ctrl.labelsForVectorAgent() clusterRole := &rbacv1.ClusterRole{ - ObjectMeta: vr.objectMetaVectorAgent(labels), + ObjectMeta: ctrl.objectMetaVectorAgent(labels), Rules: []rbacv1.PolicyRule{ { APIGroups: []string{""}, @@ -48,21 +48,21 @@ func (vr *VectorAgentReconciler) createVectorAgentClusterRole() *rbacv1.ClusterR return clusterRole } -func (vr *VectorAgentReconciler) createVectorAgentClusterRoleBinding() *rbacv1.ClusterRoleBinding { - labels := vr.labelsForVectorAgent() +func (ctrl *Controller) createVectorAgentClusterRoleBinding() *rbacv1.ClusterRoleBinding { + labels := ctrl.labelsForVectorAgent() clusterRoleBinding := &rbacv1.ClusterRoleBinding{ - ObjectMeta: vr.objectMetaVectorAgent(labels), + ObjectMeta: ctrl.objectMetaVectorAgent(labels), RoleRef: rbacv1.RoleRef{ Kind: "ClusterRole", APIGroup: "rbac.authorization.k8s.io", - Name: vr.getNameVectorAgent(), + Name: ctrl.getNameVectorAgent(), }, Subjects: []rbacv1.Subject{ { Kind: "ServiceAccount", - Name: vr.getNameVectorAgent(), - Namespace: vr.Vector.Namespace, + Name: ctrl.getNameVectorAgent(), + Namespace: ctrl.Vector.Namespace, }, }, } diff --git a/controllers/factory/vectoragent/vector_agent_service.go b/controllers/factory/vectoragent/vector_agent_service.go index 93d14c24..24f33ed7 100644 --- a/controllers/factory/vectoragent/vector_agent_service.go +++ b/controllers/factory/vectoragent/vector_agent_service.go @@ -22,11 +22,11 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" ) -func (vr *VectorAgentReconciler) createVectorAgentService() *corev1.Service { - labels := vr.labelsForVectorAgent() +func (ctrl *Controller) createVectorAgentService() *corev1.Service { + labels := ctrl.labelsForVectorAgent() service := &corev1.Service{ - ObjectMeta: vr.objectMetaVectorAgent(labels), + ObjectMeta: ctrl.objectMetaVectorAgent(labels), Spec: corev1.ServiceSpec{ Ports: []corev1.ServicePort{ { diff --git a/controllers/factory/vectorpipeline/vectorpipeline.go b/controllers/factory/vectorpipeline/vectorpipeline.go deleted file mode 100644 index f468f247..00000000 --- a/controllers/factory/vectorpipeline/vectorpipeline.go +++ /dev/null @@ -1,174 +0,0 @@ -/* -Copyright 2022. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package vectorpipeline - -import ( - "context" - "encoding/json" - - vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" - "github.com/kaasops/vector-operator/controllers/factory/k8sutils" - "github.com/kaasops/vector-operator/controllers/factory/vector" - "github.com/mitchellh/mapstructure" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -func SelectSucceesed(ctx context.Context, rclient client.Client) ([]*vectorv1alpha1.VectorPipeline, error) { - - var vectorPipelinesCombined []*vectorv1alpha1.VectorPipeline - - objlist := vectorv1alpha1.VectorPipelineList{} - err := rclient.List(ctx, &objlist) - if err != nil { - return nil, err - } - - for _, item := range objlist.Items { - if !item.DeletionTimestamp.IsZero() { - continue - } - if item.Status.ConfigCheckResult != nil { - if *item.Status.ConfigCheckResult { - vectorPipelinesCombined = append(vectorPipelinesCombined, item.DeepCopy()) - } - } - - } - return vectorPipelinesCombined, nil -} - -func SetSucceesStatus(ctx context.Context, vp *vectorv1alpha1.VectorPipeline, c client.Client) error { - var status = true - vp.Status.ConfigCheckResult = &status - vp.Status.Reason = nil - - return k8sutils.UpdateStatus(ctx, vp, c) -} - -func SetFailedStatus(ctx context.Context, vp *vectorv1alpha1.VectorPipeline, c client.Client, err error) error { - var status = false - var reason = err.Error() - vp.Status.ConfigCheckResult = &status - vp.Status.Reason = &reason - - return k8sutils.UpdateStatus(ctx, vp, c) -} - -func SetLastAppliedPipelineStatus(ctx context.Context, vp *vectorv1alpha1.VectorPipeline, c client.Client) error { - hash, err := GetVpSpecHash(vp) - if err != nil { - return err - } - vp.Status.LastAppliedPipelineHash = hash - if err := k8sutils.UpdateStatus(ctx, vp, c); err != nil { - return err - } - return nil -} - -func GetSources(vp *vectorv1alpha1.VectorPipeline, filter []string) ([]vector.Source, error) { - var sources []vector.Source - sourcesMap, err := decodeRaw(vp.Spec.Sources.Raw) - if err != nil { - return nil, err - } - for k, v := range sourcesMap { - if len(filter) != 0 { - if !contains(filter, k) { - continue - } - } - var source vector.Source - if err := mapstructure.Decode(v, &source); err != nil { - return nil, err - } - source.Name = addPrefix(vp, k) - sources = append(sources, source) - } - return sources, nil -} - -func GetTransforms(vp *vectorv1alpha1.VectorPipeline) ([]vector.Transform, error) { - if vp.Spec.Transforms == nil { - return nil, nil - } - transformsMap, err := decodeRaw(vp.Spec.Transforms.Raw) - if err != nil { - return nil, err - } - var transforms []vector.Transform - if err := json.Unmarshal(vp.Spec.Transforms.Raw, &transformsMap); err != nil { - return nil, err - } - for k, v := range transformsMap { - var transform vector.Transform - if err := mapstructure.Decode(v, &transform); err != nil { - return nil, err - } - transform.Name = addPrefix(vp, k) - for i, inputName := range transform.Inputs { - transform.Inputs[i] = addPrefix(vp, inputName) - } - transforms = append(transforms, transform) - } - return transforms, nil -} - -func GetSinks(vp *vectorv1alpha1.VectorPipeline) ([]vector.Sink, error) { - sinksMap, err := decodeRaw(vp.Spec.Sinks.Raw) - if err != nil { - return nil, err - } - var sinks []vector.Sink - for k, v := range sinksMap { - var sink vector.Sink - if err := mapstructure.Decode(v, &sink); err != nil { - return nil, err - } - sink.Name = addPrefix(vp, k) - for i, inputName := range sink.Inputs { - sink.Inputs[i] = addPrefix(vp, inputName) - } - sinks = append(sinks, sink) - } - return sinks, nil -} - -func decodeRaw(raw []byte) (map[string]interface{}, error) { - result := make(map[string]interface{}) - if err := json.Unmarshal(raw, &result); err != nil { - return nil, err - } - return result, nil -} - -func addPrefix(vp *vectorv1alpha1.VectorPipeline, name string) string { - return generateName(vp) + "-" + name -} - -func generateName(vp *vectorv1alpha1.VectorPipeline) string { - return vp.Namespace + "-" + vp.Name -} - -func contains(elems []string, v string) bool { - for _, s := range elems { - if v == s { - return true - } - } - return false -} diff --git a/controllers/vector_controller.go b/controllers/vector_controller.go index 57a5a91e..df3446a4 100644 --- a/controllers/vector_controller.go +++ b/controllers/vector_controller.go @@ -100,7 +100,7 @@ func (r *VectorReconciler) SetupWithManager(mgr ctrl.Manager) error { } func (r *VectorReconciler) CreateOrUpdateVector(v *vectorv1alpha1.Vector) (ctrl.Result, error) { - vectorAgentReconciler := vectoragent.NewReconciler(v, r.Client, r.Clientset) + vectorAgentReconciler := vectoragent.NewController(v, r.Client, r.Clientset) if done, result, err := vectorAgentReconciler.EnsureVectorAgent(); done { return result, err diff --git a/controllers/vectorpipeline_controller.go b/controllers/vectorpipeline_controller.go index 954b386d..43262678 100644 --- a/controllers/vectorpipeline_controller.go +++ b/controllers/vectorpipeline_controller.go @@ -29,8 +29,8 @@ import ( "github.com/go-logr/logr" vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" "github.com/kaasops/vector-operator/controllers/factory/config" - "github.com/kaasops/vector-operator/controllers/factory/config/configcheck" - "github.com/kaasops/vector-operator/controllers/factory/vectorpipeline" + "github.com/kaasops/vector-operator/controllers/factory/pipeline" + "github.com/kaasops/vector-operator/controllers/factory/pipeline/vectorpipeline" ) // VectorPipelineReconciler reconciles a VectorPipeline object @@ -56,8 +56,7 @@ type VectorPipelineReconciler struct { // For more details, check Reconcile and its Result here: // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.12.2/pkg/reconcile func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - _ = log.FromContext(ctx) - log := log.FromContext(ctx).WithValues("VectorPipeline", req.NamespacedName) + log := log.FromContext(ctx).WithValues("VectorPipeline", req.Name) log.Info("start Reconcile VectorPipeline") @@ -66,6 +65,21 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque return result, err } + // Generate VectorPipeline Controller + vpCtrl := vectorpipeline.NewController(vp) + // Generate Pipeline Controller + vCtrl := pipeline.NewController(ctx, r.Client, vpCtrl) + + // Check Pipeline hash + checkResult, err := vCtrl.CheckHash() + if err != nil { + return ctrl.Result{}, err + } + if checkResult { + log.Info("VectorPipeline has no changes. Finish Reconcile VectorPipeline") + return ctrl.Result{}, nil + } + vectorInstances := &vectorv1alpha1.VectorList{} err = r.List(ctx, vectorInstances) if err != nil { @@ -77,22 +91,16 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque return ctrl.Result{}, nil } - hash, err := vectorpipeline.GetVpSpecHash(vp) - if err != nil { - return ctrl.Result{}, err - } - for _, v := range vectorInstances.Items { if v.DeletionTimestamp != nil { continue } - if vp.Status.LastAppliedPipelineHash == nil || *hash != *vp.Status.LastAppliedPipelineHash { - if err = checkConfig(ctx, &v, vp, r.Client, r.Clientset); err != nil { - return ctrl.Result{}, err - } - if err = vectorpipeline.SetLastAppliedPipelineStatus(ctx, vp, r.Client); err != nil { - return ctrl.Result{}, err - } + if err = config.Check(ctx, &v, vCtrl, r.Client, r.Clientset); err != nil { + return ctrl.Result{}, err + } + + if err = vCtrl.SetLastAppliedPipelineStatus(); err != nil { + return ctrl.Result{}, err } } @@ -127,33 +135,3 @@ func (r *VectorPipelineReconciler) SetupWithManager(mgr ctrl.Manager) error { For(&vectorv1alpha1.VectorPipeline{}). Complete(r) } - -func checkConfig(ctx context.Context, v *vectorv1alpha1.Vector, vp *vectorv1alpha1.VectorPipeline, c client.Client, cs *kubernetes.Clientset) error { - log := log.FromContext(context.TODO()).WithValues("Vector Pipeline", "ConfigCheck") - - var vps []*vectorv1alpha1.VectorPipeline - vps = append(vps, vp) - - cfg, err := config.GenerateConfig(v, vps) - if err != nil { - return err - } - - err = configcheck.Run(cfg, c, cs, vp.Name, vp.Namespace, v.Spec.Agent.Image) - if _, ok := err.(*configcheck.ErrConfigCheck); ok { - if err := vectorpipeline.SetFailedStatus(ctx, vp, c, err); err != nil { - return err - } - log.Error(err, "Vector Config has error") - return nil - } - if err != nil { - return err - } - - if err := vectorpipeline.SetSucceesStatus(ctx, vp, c); err != nil { - return err - } - - return nil -} diff --git a/main.go b/main.go index 9cf88ce9..c1cf1585 100644 --- a/main.go +++ b/main.go @@ -119,8 +119,9 @@ func main() { os.Exit(1) } if err = (&controllers.ClusterVectorPipelineReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Clientset: clientset, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "ClusterVectorPipeline") os.Exit(1) From caf62e1163d5d91bc26d3047e1c2a1fb79425782 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Thu, 27 Oct 2022 16:42:47 +0300 Subject: [PATCH 032/316] Update ReadMe Signed-off-by: Zemtsov Vladimir --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index e2bc1ff9..a80a0a6f 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@

+# Vector Operator + ## Description The operator deploys and configures a vector agent daemonset on every node to collect container and application logs from the node file system. From 28350b8175b45d09b38d1ee32c80ee7884035a5b Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Fri, 28 Oct 2022 15:24:51 +0300 Subject: [PATCH 033/316] RefactorConfigCheck Signed-off-by: Zemtsov Vladimir --- controllers/factory/config/config.go | 86 ++++++++++++ controllers/factory/config/config_build.go | 53 ++------ .../factory/config/configcheck/configcheck.go | 123 +++++++++++------- .../config/configcheck/configcheck_config.go | 8 +- .../config/configcheck/configcheck_pod.go | 18 +-- .../config/configcheck/configcheck_rbac.go | 4 +- ...orpipeline.go => clustervectorpipeline.go} | 5 +- controllers/factory/pipeline/hash.go | 4 +- .../pipeline/vectorpipeline/vectorpipeline.go | 5 +- .../factory/utils/{utils.go => hash/hash.go} | 4 +- .../factory/{ => utils}/helper/helper.go | 0 .../{k8sutils/utils.go => utils/k8s/k8s.go} | 2 +- .../factory/{label => utils/k8s}/label.go | 2 +- controllers/factory/vector/vector.go | 3 +- .../factory/vector/vectoragent/vectoragent.go | 67 ++++++++++ .../vectoragent/vectoragent_config.go} | 20 ++- .../vectoragent/vectoragent_controller.go} | 72 ++-------- .../vectoragent/vectoragent_daemonset.go} | 0 .../vectoragent/vectoragent_rbac.go} | 0 .../vectoragent/vectoragent_service.go} | 0 controllers/vector_controller.go | 27 +++- controllers/vectorpipeline_controller.go | 51 +++++++- 22 files changed, 360 insertions(+), 194 deletions(-) create mode 100644 controllers/factory/config/config.go rename controllers/factory/pipeline/clustervectorpipeline/{vectorpipeline.go => clustervectorpipeline.go} (92%) rename controllers/factory/utils/{utils.go => hash/hash.go} (92%) rename controllers/factory/{ => utils}/helper/helper.go (100%) rename controllers/factory/{k8sutils/utils.go => utils/k8s/k8s.go} (99%) rename controllers/factory/{label => utils/k8s}/label.go (99%) create mode 100644 controllers/factory/vector/vectoragent/vectoragent.go rename controllers/factory/{vectoragent/vector_agent_config.go => vector/vectoragent/vectoragent_config.go} (66%) rename controllers/factory/{vectoragent/vector_agent_controller.go => vector/vectoragent/vectoragent_controller.go} (67%) rename controllers/factory/{vectoragent/vector_agent_daemonset.go => vector/vectoragent/vectoragent_daemonset.go} (100%) rename controllers/factory/{vectoragent/vector_agent_rbac.go => vector/vectoragent/vectoragent_rbac.go} (100%) rename controllers/factory/{vectoragent/vector_agent_service.go => vector/vectoragent/vectoragent_service.go} (100%) diff --git a/controllers/factory/config/config.go b/controllers/factory/config/config.go new file mode 100644 index 00000000..8abb097d --- /dev/null +++ b/controllers/factory/config/config.go @@ -0,0 +1,86 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package config + +import ( + "context" + "encoding/json" + + "github.com/kaasops/vector-operator/controllers/factory/pipeline" + "github.com/kaasops/vector-operator/controllers/factory/vector" + "github.com/kaasops/vector-operator/controllers/factory/vector/vectoragent" +) + +type Config struct { + Name string + + Ctx context.Context + vaCtrl *vectoragent.Controller + pCtrls []pipeline.Controller + + VectorConfig *vector.VectorConfig +} + +func New(ctx context.Context, vaCtrl *vectoragent.Controller) (*Config, error) { + cfg := &Config{ + Ctx: ctx, + vaCtrl: vaCtrl, + } + + return cfg, nil +} + +func (cfg *Config) FillForVectorAgent() error { + cfg.Name = cfg.vaCtrl.Vector.Name + + pCtrl := pipeline.NewController(cfg.Ctx, cfg.vaCtrl.Client, nil) + pCtrls, err := pCtrl.SelectSucceesed() + if err != nil { + return err + } + cfg.pCtrls = pCtrls + + if err := cfg.GenerateVectorConfig(); err != nil { + return err + } + + return nil +} + +func (cfg *Config) FillForVectorPipeline(vCtrl *pipeline.Controller) error { + cfg.Name = vCtrl.Pipeline.Name() + + pCtrls := make([]pipeline.Controller, 1) + pCtrls[0] = *vCtrl + + cfg.pCtrls = pCtrls + + if err := cfg.GenerateVectorConfig(); err != nil { + return err + } + + return nil +} + +func (cfg *Config) GetByteConfig() ([]byte, error) { + data, err := json.Marshal(cfg.VectorConfig) + if err != nil { + return nil, err + } + + return data, nil +} diff --git a/controllers/factory/config/config_build.go b/controllers/factory/config/config_build.go index f326a5e0..7672fe7c 100644 --- a/controllers/factory/config/config_build.go +++ b/controllers/factory/config/config_build.go @@ -17,13 +17,7 @@ limitations under the License. package config import ( - "context" - "encoding/json" - - vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" - "github.com/kaasops/vector-operator/controllers/factory/pipeline" "github.com/kaasops/vector-operator/controllers/factory/vector" - "sigs.k8s.io/controller-runtime/pkg/client" ) var ( @@ -44,30 +38,14 @@ var ( } ) -func Get(ctx context.Context, v *vectorv1alpha1.Vector, c client.Client) ([]byte, error) { - vCtrl := pipeline.NewController(ctx, c, nil) - pipelines, err := vCtrl.SelectSucceesed() - if err != nil { - return nil, err - } +func (cfg *Config) GenerateVectorConfig() error { + vectorConfig := vector.New(cfg.vaCtrl.Vector.Spec.Agent.DataDir, cfg.vaCtrl.Vector.Spec.Agent.ApiEnabled) - cfg, err := GenerateVectorConfig(v, pipelines) + sources, transforms, sinks, err := cfg.getComponents() if err != nil { - return nil, err + return err } - return VectorConfigToJson(cfg) -} - -func GenerateVectorConfig( - v *vectorv1alpha1.Vector, - vCtrls []pipeline.Controller, -) (*vector.VectorConfig, error) { - cfg := vector.New(v.Spec.Agent.DataDir, v.Spec.Agent.ApiEnabled) - sources, transforms, sinks, err := getComponents(vCtrls) - if err != nil { - return nil, err - } if len(sources) == 0 { sources = sourceDefault } @@ -75,19 +53,21 @@ func GenerateVectorConfig( sinks = sinkDefault } - cfg.Sinks = sinks - cfg.Sources = sources - cfg.Transforms = transforms + vectorConfig.Sinks = sinks + vectorConfig.Sources = sources + vectorConfig.Transforms = transforms - return cfg, nil + cfg.VectorConfig = vectorConfig + + return nil } -func getComponents(vCtrls []pipeline.Controller) (map[string]interface{}, map[string]interface{}, map[string]interface{}, error) { +func (cfg *Config) getComponents() (map[string]interface{}, map[string]interface{}, map[string]interface{}, error) { sourcesMap := make(map[string]interface{}) transformsMap := make(map[string]interface{}) sinksMap := make(map[string]interface{}) - for _, vCtrl := range vCtrls { + for _, vCtrl := range cfg.pCtrls { sources, err := vCtrl.GetSources(nil) if err != nil { return nil, nil, nil, err @@ -124,12 +104,3 @@ func getComponents(vCtrls []pipeline.Controller) (map[string]interface{}, map[st } return sourcesMap, transformsMap, sinksMap, nil } - -func VectorConfigToJson(conf *vector.VectorConfig) ([]byte, error) { - data, err := json.Marshal(conf) - if err != nil { - return nil, err - } - - return data, nil -} diff --git a/controllers/factory/config/configcheck/configcheck.go b/controllers/factory/config/configcheck/configcheck.go index 5204c01b..9ecfc337 100644 --- a/controllers/factory/config/configcheck/configcheck.go +++ b/controllers/factory/config/configcheck/configcheck.go @@ -21,9 +21,8 @@ import ( "math/rand" "time" - "github.com/kaasops/vector-operator/controllers/factory/helper" - "github.com/kaasops/vector-operator/controllers/factory/k8sutils" - "github.com/kaasops/vector-operator/controllers/factory/label" + "github.com/kaasops/vector-operator/controllers/factory/utils/helper" + "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" corev1 "k8s.io/api/core/v1" "k8s.io/client-go/kubernetes" ctrl "sigs.k8s.io/controller-runtime" @@ -31,78 +30,110 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" ) -func Run( - cfg []byte, - c client.Client, - cs *kubernetes.Clientset, - name, - namespace, - image string, -) error { - log := log.FromContext(context.TODO()).WithValues("Vector ConfigCheck", name) +type ConfigCheck struct { + Ctx context.Context - log.Info("start ConfigCheck Vector") + Config []byte - err := ensureVectorConfigCheckRBAC(c, namespace) - if err != nil { + Client client.Client + ClientSet *kubernetes.Clientset + + Name string + Namespace string + Image string + Hash string +} + +func New(ctx context.Context, config []byte, c client.Client, cs *kubernetes.Clientset, name, namespace, image string) *ConfigCheck { + return &ConfigCheck{ + Ctx: ctx, + Config: config, + Client: c, + ClientSet: cs, + Name: name, + Namespace: namespace, + Image: image, + } +} + +// func (cfg *Config) StartCheck() error { + +// log := log.FromContext(context.TODO()).WithValues("ConfigCheck Vector Pipeline", cfg.vaCtrl.Vector.Name) + +// configCheck := configcheck.New(cfg.Ctx, cfg.ByteConfig, cfg.vaCtrl.Client, cfg.vaCtrl.ClientSet, cfg.Name, cfg.vaCtrl.Vector.Namespace, cfg.vaCtrl.Vector.Spec.Agent.Image) + +// err := configCheck.Run() +// if _, ok := err.(*configcheck.ErrConfigCheck); ok { +// if err := cfg.vaCtrl.SetFailedStatus(cfg.Ctx, err); err != nil { +// return err +// } +// log.Error(err, "Vector Config has error") +// return nil +// } +// if err != nil { +// return err +// } + +// return cfg.vaCtrl.SetSucceesStatus(cfg.Ctx) +// } + +func (cc *ConfigCheck) Run() error { + log := log.FromContext(context.TODO()).WithValues("Vector ConfigCheck", cc.Name) + + log.Info("start ConfigCheck") + + if err := cc.ensureVectorConfigCheckRBAC(); err != nil { return err } - hash := randStringRunes() + cc.Hash = randStringRunes() - err = ensureVectorConfigCheckConfig(c, cfg, name, namespace, hash) - if err != nil { + if err := cc.ensureVectorConfigCheckConfig(); err != nil { return err } - err = checkVectorConfigCheckPod(c, cs, name, namespace, image, hash) - if err != nil { + if err := cc.checkVectorConfigCheckPod(); err != nil { return err } return nil } -func ensureVectorConfigCheckRBAC(c client.Client, ns string) error { - // ctx := context.Background() - // log := log.FromContext(ctx).WithValues("vector-config-check-rbac", "ConfigCheck") - - // log.Info("start Reconcile Vector Config Check RBAC") - - if done, _, err := ensureVectorConfigCheckServiceAccount(c, ns); done { +func (cc *ConfigCheck) ensureVectorConfigCheckRBAC() error { + if done, _, err := cc.ensureVectorConfigCheckServiceAccount(); done { return err } return nil } -func ensureVectorConfigCheckServiceAccount(c client.Client, ns string) (bool, ctrl.Result, error) { - vectorAgentServiceAccount := createVectorConfigCheckServiceAccount(ns) +func (cc *ConfigCheck) ensureVectorConfigCheckServiceAccount() (bool, ctrl.Result, error) { + vectorAgentServiceAccount := cc.createVectorConfigCheckServiceAccount() - _, err := k8sutils.CreateOrUpdateServiceAccount(vectorAgentServiceAccount, c) + _, err := k8s.CreateOrUpdateServiceAccount(vectorAgentServiceAccount, cc.Client) return helper.ReconcileResult(err) } -func ensureVectorConfigCheckConfig(c client.Client, cfg []byte, name, ns, hash string) error { - vectorConfigCheckSecret, err := createVectorConfigCheckConfig(cfg, name, ns, hash) +func (cc *ConfigCheck) ensureVectorConfigCheckConfig() error { + vectorConfigCheckSecret, err := cc.createVectorConfigCheckConfig() if err != nil { return err } - _, err = k8sutils.CreateOrUpdateSecret(vectorConfigCheckSecret, c) + _, err = k8s.CreateOrUpdateSecret(vectorConfigCheckSecret, cc.Client) return err } -func checkVectorConfigCheckPod(c client.Client, cs *kubernetes.Clientset, name, ns, image, hash string) error { - vectorConfigCheckPod := createVectorConfigCheckPod(name, ns, image, hash) +func (cc *ConfigCheck) checkVectorConfigCheckPod() error { + vectorConfigCheckPod := cc.createVectorConfigCheckPod() - err := k8sutils.CreatePod(vectorConfigCheckPod, c) + err := k8s.CreatePod(vectorConfigCheckPod, cc.Client) if err != nil { return err } - err = getCheckResult(vectorConfigCheckPod, c, cs) + err = cc.getCheckResult(vectorConfigCheckPod) if err != nil { return err } @@ -112,15 +143,15 @@ func checkVectorConfigCheckPod(c client.Client, cs *kubernetes.Clientset, name, func labelsForVectorConfigCheck() map[string]string { return map[string]string{ - label.ManagedByLabelKey: "vector-operator", - label.NameLabelKey: "vector-configcheck", - label.ComponentLabelKey: "ConfigCheck", - label.VectorExcludeLabel: "true", + k8s.ManagedByLabelKey: "vector-operator", + k8s.NameLabelKey: "vector-configcheck", + k8s.ComponentLabelKey: "ConfigCheck", + k8s.VectorExcludeLabel: "true", } } -func getNameVectorConfigCheck(name, hash string) string { - n := "configcheck-" + name + "-" + hash +func (cc *ConfigCheck) getNameVectorConfigCheck() string { + n := "configcheck-" + "-" + cc.Name + "-" + cc.Hash return n } @@ -134,11 +165,11 @@ func randStringRunes() string { return string(b) } -func getCheckResult(pod *corev1.Pod, c client.Client, cs *kubernetes.Clientset) error { +func (cc *ConfigCheck) getCheckResult(pod *corev1.Pod) error { log := log.FromContext(context.TODO()).WithValues("Vector ConfigCheck", pod.Name) for { - existing, err := k8sutils.GetPod(pod, c) + existing, err := k8s.GetPod(pod, cc.Client) if err != nil { return err } @@ -148,7 +179,7 @@ func getCheckResult(pod *corev1.Pod, c client.Client, cs *kubernetes.Clientset) log.Info("wait Validate Vector Config Result") time.Sleep(5 * time.Second) case "Failed": - reason, err := k8sutils.GetPodLogs(pod, cs) + reason, err := k8s.GetPodLogs(pod, cc.ClientSet) if err != nil { return err } diff --git a/controllers/factory/config/configcheck/configcheck_config.go b/controllers/factory/config/configcheck/configcheck_config.go index 4b5e6fc9..d7a0caa8 100644 --- a/controllers/factory/config/configcheck/configcheck_config.go +++ b/controllers/factory/config/configcheck/configcheck_config.go @@ -21,17 +21,17 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func createVectorConfigCheckConfig(cfg []byte, name, ns, hash string) (*corev1.Secret, error) { +func (cc *ConfigCheck) createVectorConfigCheckConfig() (*corev1.Secret, error) { labels := labelsForVectorConfigCheck() config := map[string][]byte{ - "agent.json": cfg, + "agent.json": cc.Config, } secret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: getNameVectorConfigCheck(name, hash), - Namespace: ns, + Name: cc.getNameVectorConfigCheck(), + Namespace: cc.Namespace, Labels: labels, }, Data: config, diff --git a/controllers/factory/config/configcheck/configcheck_pod.go b/controllers/factory/config/configcheck/configcheck_pod.go index 0bbd6c16..1a2c5540 100644 --- a/controllers/factory/config/configcheck/configcheck_pod.go +++ b/controllers/factory/config/configcheck/configcheck_pod.go @@ -21,23 +21,23 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func createVectorConfigCheckPod(name, ns, image, hash string) *corev1.Pod { +func (cc *ConfigCheck) createVectorConfigCheckPod() *corev1.Pod { labels := labelsForVectorConfigCheck() pod := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ - Name: getNameVectorConfigCheck(name, hash), - Namespace: ns, + Name: cc.getNameVectorConfigCheck(), + Namespace: cc.Namespace, Labels: labels, }, Spec: corev1.PodSpec{ ServiceAccountName: "vector-configcheck", - Volumes: generateVectorConfigCheckVolume(name, hash), + Volumes: cc.generateVectorConfigCheckVolume(), SecurityContext: &corev1.PodSecurityContext{}, Containers: []corev1.Container{ { Name: "config-check", - Image: image, + Image: cc.Image, Args: []string{"validate", "/etc/vector/*.json"}, Env: generateVectorConfigCheckEnvs(), Ports: []corev1.ContainerPort{ @@ -47,7 +47,7 @@ func createVectorConfigCheckPod(name, ns, image, hash string) *corev1.Pod { Protocol: "TCP", }, }, - VolumeMounts: generateVectorConfigCheckVolumeMounts(), + VolumeMounts: cc.generateVectorConfigCheckVolumeMounts(), }, }, RestartPolicy: "Never", @@ -57,13 +57,13 @@ func createVectorConfigCheckPod(name, ns, image, hash string) *corev1.Pod { return pod } -func generateVectorConfigCheckVolume(name, hash string) []corev1.Volume { +func (cc *ConfigCheck) generateVectorConfigCheckVolume() []corev1.Volume { volume := []corev1.Volume{ { Name: "config", VolumeSource: corev1.VolumeSource{ Secret: &corev1.SecretVolumeSource{ - SecretName: getNameVectorConfigCheck(name, hash), + SecretName: cc.getNameVectorConfigCheck(), }, }, }, @@ -112,7 +112,7 @@ func generateVectorConfigCheckVolume(name, hash string) []corev1.Volume { return volume } -func generateVectorConfigCheckVolumeMounts() []corev1.VolumeMount { +func (cc *ConfigCheck) generateVectorConfigCheckVolumeMounts() []corev1.VolumeMount { volumeMount := []corev1.VolumeMount{ { Name: "config", diff --git a/controllers/factory/config/configcheck/configcheck_rbac.go b/controllers/factory/config/configcheck/configcheck_rbac.go index 08317c7f..0b4fb80b 100644 --- a/controllers/factory/config/configcheck/configcheck_rbac.go +++ b/controllers/factory/config/configcheck/configcheck_rbac.go @@ -21,13 +21,13 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func createVectorConfigCheckServiceAccount(ns string) *corev1.ServiceAccount { +func (cc *ConfigCheck) createVectorConfigCheckServiceAccount() *corev1.ServiceAccount { labels := labelsForVectorConfigCheck() serviceAccount := &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: "vector-configcheck", - Namespace: ns, + Namespace: cc.Namespace, Labels: labels, }, } diff --git a/controllers/factory/pipeline/clustervectorpipeline/vectorpipeline.go b/controllers/factory/pipeline/clustervectorpipeline/clustervectorpipeline.go similarity index 92% rename from controllers/factory/pipeline/clustervectorpipeline/vectorpipeline.go rename to controllers/factory/pipeline/clustervectorpipeline/clustervectorpipeline.go index 993d15a4..40b5e97c 100644 --- a/controllers/factory/pipeline/clustervectorpipeline/vectorpipeline.go +++ b/controllers/factory/pipeline/clustervectorpipeline/clustervectorpipeline.go @@ -20,7 +20,7 @@ import ( "context" vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" - "github.com/kaasops/vector-operator/controllers/factory/k8sutils" + "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -30,6 +30,7 @@ var ( type Controller struct { ClusterVectorPipeline *vectorv1alpha1.ClusterVectorPipeline + Config []byte } func (ctrl *Controller) Spec() vectorv1alpha1.VectorPipelineSpec { @@ -65,7 +66,7 @@ func (ctrl *Controller) SetLastAppliedPipeline(hash *uint32) { } func (ctrl *Controller) UpdateStatus(ctx context.Context, c client.Client) error { - return k8sutils.UpdateStatus(ctx, ctrl.ClusterVectorPipeline, c) + return k8s.UpdateStatus(ctx, ctrl.ClusterVectorPipeline, c) } func NewController(cvp *vectorv1alpha1.ClusterVectorPipeline) *Controller { diff --git a/controllers/factory/pipeline/hash.go b/controllers/factory/pipeline/hash.go index bfb73065..56ae2669 100644 --- a/controllers/factory/pipeline/hash.go +++ b/controllers/factory/pipeline/hash.go @@ -19,7 +19,7 @@ package pipeline import ( "encoding/json" - "github.com/kaasops/vector-operator/controllers/factory/utils" + "github.com/kaasops/vector-operator/controllers/factory/utils/hash" ) func (ctrl *Controller) GetSpecHash() (*uint32, error) { @@ -27,7 +27,7 @@ func (ctrl *Controller) GetSpecHash() (*uint32, error) { if err != nil { return nil, err } - hash := utils.GetHash(a) + hash := hash.Get(a) return &hash, nil } diff --git a/controllers/factory/pipeline/vectorpipeline/vectorpipeline.go b/controllers/factory/pipeline/vectorpipeline/vectorpipeline.go index 2503d90e..8c9d9922 100644 --- a/controllers/factory/pipeline/vectorpipeline/vectorpipeline.go +++ b/controllers/factory/pipeline/vectorpipeline/vectorpipeline.go @@ -20,7 +20,7 @@ import ( "context" vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" - "github.com/kaasops/vector-operator/controllers/factory/k8sutils" + "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -30,6 +30,7 @@ var ( type Controller struct { VectorPipeline *vectorv1alpha1.VectorPipeline + Config []byte } func (ctrl *Controller) Spec() vectorv1alpha1.VectorPipelineSpec { @@ -65,7 +66,7 @@ func (ctrl *Controller) SetLastAppliedPipeline(hash *uint32) { } func (ctrl *Controller) UpdateStatus(ctx context.Context, c client.Client) error { - return k8sutils.UpdateStatus(ctx, ctrl.VectorPipeline, c) + return k8s.UpdateStatus(ctx, ctrl.VectorPipeline, c) } func NewController(vp *vectorv1alpha1.VectorPipeline) *Controller { diff --git a/controllers/factory/utils/utils.go b/controllers/factory/utils/hash/hash.go similarity index 92% rename from controllers/factory/utils/utils.go rename to controllers/factory/utils/hash/hash.go index 4f9ceb7d..cde0db8d 100644 --- a/controllers/factory/utils/utils.go +++ b/controllers/factory/utils/hash/hash.go @@ -14,11 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -package utils +package hash import "hash/crc32" -func GetHash(input []byte) uint32 { +func Get(input []byte) uint32 { crc32q := crc32.MakeTable(crc32.IEEE) return crc32.Checksum(input, crc32q) } diff --git a/controllers/factory/helper/helper.go b/controllers/factory/utils/helper/helper.go similarity index 100% rename from controllers/factory/helper/helper.go rename to controllers/factory/utils/helper/helper.go diff --git a/controllers/factory/k8sutils/utils.go b/controllers/factory/utils/k8s/k8s.go similarity index 99% rename from controllers/factory/k8sutils/utils.go rename to controllers/factory/utils/k8s/k8s.go index 01d77300..dff1bf29 100644 --- a/controllers/factory/k8sutils/utils.go +++ b/controllers/factory/utils/k8s/k8s.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package k8sutils +package k8s import ( "bytes" diff --git a/controllers/factory/label/label.go b/controllers/factory/utils/k8s/label.go similarity index 99% rename from controllers/factory/label/label.go rename to controllers/factory/utils/k8s/label.go index 7dd3cf71..b9b86563 100644 --- a/controllers/factory/label/label.go +++ b/controllers/factory/utils/k8s/label.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package label +package k8s const ( // The following labels are recommended by kubernetes https://kubernetes.io/docs/concepts/overview/working-with-objects/common-labels/ diff --git a/controllers/factory/vector/vector.go b/controllers/factory/vector/vector.go index 963cc0b3..29d206c6 100644 --- a/controllers/factory/vector/vector.go +++ b/controllers/factory/vector/vector.go @@ -35,8 +35,7 @@ func New(dataDir string, apiEnabled bool) *VectorConfig { } func Mapper(c ConfigComponent) (map[string]interface{}, error) { - spec := make(map[string]interface{}) - spec = c.GetOptions() + spec := c.GetOptions() config := &mapstructure.DecoderConfig{ Result: &spec, ZeroFields: false, diff --git a/controllers/factory/vector/vectoragent/vectoragent.go b/controllers/factory/vector/vectoragent/vectoragent.go new file mode 100644 index 00000000..68f4616b --- /dev/null +++ b/controllers/factory/vector/vectoragent/vectoragent.go @@ -0,0 +1,67 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package vectoragent + +import ( + "context" + + vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" + "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" + "k8s.io/client-go/kubernetes" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +type Controller struct { + client.Client + Vector *vectorv1alpha1.Vector + + Config []byte + // Temp. Wait this issue - https://github.com/kubernetes-sigs/controller-runtime/issues/452 + ClientSet *kubernetes.Clientset +} + +func NewController(v *vectorv1alpha1.Vector, c client.Client, cs *kubernetes.Clientset) *Controller { + return &Controller{ + Client: c, + Vector: v, + ClientSet: cs, + } +} + +func (ctrl *Controller) SetSucceesStatus(ctx context.Context) error { + var status = true + ctrl.Vector.Status.ConfigCheckResult = &status + ctrl.Vector.Status.Reason = nil + + return k8s.UpdateStatus(ctx, ctrl.Vector, ctrl.Client) +} + +func (ctrl *Controller) SetFailedStatus(ctx context.Context, err error) error { + var status = false + var reason = err.Error() + ctrl.Vector.Status.ConfigCheckResult = &status + ctrl.Vector.Status.Reason = &reason + + return k8s.UpdateStatus(ctx, ctrl.Vector, ctrl.Client) +} + +func (ctrl *Controller) SetLastAppliedPipelineStatus(ctx context.Context, hash *uint32) error { + + ctrl.Vector.Status.LastAppliedConfigHash = hash + + return k8s.UpdateStatus(ctx, ctrl.Vector, ctrl.Client) +} diff --git a/controllers/factory/vectoragent/vector_agent_config.go b/controllers/factory/vector/vectoragent/vectoragent_config.go similarity index 66% rename from controllers/factory/vectoragent/vector_agent_config.go rename to controllers/factory/vector/vectoragent/vectoragent_config.go index b1979239..cc47bcf9 100644 --- a/controllers/factory/vectoragent/vector_agent_config.go +++ b/controllers/factory/vector/vectoragent/vectoragent_config.go @@ -21,23 +21,19 @@ import ( corev1 "k8s.io/api/core/v1" - "github.com/kaasops/vector-operator/controllers/factory/config" "github.com/kaasops/vector-operator/controllers/factory/config/configcheck" - "github.com/kaasops/vector-operator/controllers/factory/utils" + "github.com/kaasops/vector-operator/controllers/factory/utils/hash" ) func (ctrl *Controller) createVectorAgentConfig(ctx context.Context) (*corev1.Secret, error) { - cfg, err := config.Get(ctx, ctrl.Vector, ctrl.Client) - if err != nil { - return nil, err - } + cfgHash := hash.Get(ctrl.Config) - cfgHash := utils.GetHash(cfg) + configCheck := configcheck.New(ctx, ctrl.Config, ctrl.Client, ctrl.ClientSet, ctrl.Vector.Name, ctrl.Vector.Namespace, ctrl.Vector.Spec.Agent.Image) if ctrl.Vector.Status.LastAppliedConfigHash == nil || *ctrl.Vector.Status.LastAppliedConfigHash != cfgHash { - err = configcheck.Run(cfg, ctrl.Client, ctrl.Clientset, ctrl.Vector.Name, ctrl.Vector.Namespace, ctrl.Vector.Spec.Agent.Image) + err := configCheck.Run() if _, ok := err.(*configcheck.ErrConfigCheck); ok { - if err := setFailedStatus(ctx, ctrl.Vector, ctrl.Client, err); err != nil { + if err := ctrl.SetFailedStatus(ctx, err); err != nil { return nil, err } return nil, err @@ -46,11 +42,11 @@ func (ctrl *Controller) createVectorAgentConfig(ctx context.Context) (*corev1.Se return nil, err } - if err := SetLastAppliedPipelineStatus(ctx, ctrl.Vector, ctrl.Client, &cfgHash); err != nil { + if err := ctrl.SetLastAppliedPipelineStatus(ctx, &cfgHash); err != nil { return nil, err } - if err := setSucceesStatus(ctx, ctrl.Vector, ctrl.Client); err != nil { + if err := ctrl.SetSucceesStatus(ctx); err != nil { return nil, err } @@ -58,7 +54,7 @@ func (ctrl *Controller) createVectorAgentConfig(ctx context.Context) (*corev1.Se labels := ctrl.labelsForVectorAgent() config := map[string][]byte{ - "agent.json": cfg, + "agent.json": ctrl.Config, } secret := &corev1.Secret{ diff --git a/controllers/factory/vectoragent/vector_agent_controller.go b/controllers/factory/vector/vectoragent/vectoragent_controller.go similarity index 67% rename from controllers/factory/vectoragent/vector_agent_controller.go rename to controllers/factory/vector/vectoragent/vectoragent_controller.go index e747dddc..0badbf26 100644 --- a/controllers/factory/vectoragent/vector_agent_controller.go +++ b/controllers/factory/vector/vectoragent/vectoragent_controller.go @@ -19,34 +19,14 @@ package vectoragent import ( "context" - vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" - "github.com/kaasops/vector-operator/controllers/factory/helper" - "github.com/kaasops/vector-operator/controllers/factory/k8sutils" - "github.com/kaasops/vector-operator/controllers/factory/label" + "github.com/kaasops/vector-operator/controllers/factory/utils/helper" + "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" "k8s.io/utils/pointer" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" ) -type Controller struct { - client.Client - Vector *vectorv1alpha1.Vector - - // Temp. Wait this issue - https://github.com/kubernetes-sigs/controller-runtime/issues/452 - Clientset *kubernetes.Clientset -} - -func NewController(v *vectorv1alpha1.Vector, c client.Client, cs *kubernetes.Clientset) *Controller { - return &Controller{ - Client: c, - Vector: v, - Clientset: cs, - } -} - func (ctrl *Controller) EnsureVectorAgent() (done bool, result ctrl.Result, err error) { ctx := context.Background() log := log.FromContext(ctx).WithValues("vector-agent", ctrl.Vector.Name) @@ -96,7 +76,7 @@ func (ctrl *Controller) ensureVectorAgentRBAC() (bool, ctrl.Result, error) { func (ctrl *Controller) ensureVectorAgentServiceAccount() (bool, ctrl.Result, error) { vectorAgentServiceAccount := ctrl.createVectorAgentServiceAccount() - _, err := k8sutils.CreateOrUpdateServiceAccount(vectorAgentServiceAccount, ctrl.Client) + _, err := k8s.CreateOrUpdateServiceAccount(vectorAgentServiceAccount, ctrl.Client) return helper.ReconcileResult(err) } @@ -104,7 +84,7 @@ func (ctrl *Controller) ensureVectorAgentServiceAccount() (bool, ctrl.Result, er func (ctrl *Controller) ensureVectorAgentClusterRole() (bool, ctrl.Result, error) { vectorAgentClusterRole := ctrl.createVectorAgentClusterRole() - _, err := k8sutils.CreateOrUpdateClusterRole(vectorAgentClusterRole, ctrl.Client) + _, err := k8s.CreateOrUpdateClusterRole(vectorAgentClusterRole, ctrl.Client) return helper.ReconcileResult(err) } @@ -112,7 +92,7 @@ func (ctrl *Controller) ensureVectorAgentClusterRole() (bool, ctrl.Result, error func (ctrl *Controller) ensureVectorAgentClusterRoleBinding() (bool, ctrl.Result, error) { vectorAgentClusterRoleBinding := ctrl.createVectorAgentClusterRoleBinding() - _, err := k8sutils.CreateOrUpdateClusterRoleBinding(vectorAgentClusterRoleBinding, ctrl.Client) + _, err := k8s.CreateOrUpdateClusterRoleBinding(vectorAgentClusterRoleBinding, ctrl.Client) return helper.ReconcileResult(err) } @@ -125,7 +105,7 @@ func (ctrl *Controller) ensureVectorAgentService() (bool, ctrl.Result, error) { vectorAgentService := ctrl.createVectorAgentService() - _, err := k8sutils.CreateOrUpdateService(vectorAgentService, ctrl.Client) + _, err := k8s.CreateOrUpdateService(vectorAgentService, ctrl.Client) return helper.ReconcileResult(err) } @@ -141,7 +121,7 @@ func (ctrl *Controller) ensureVectorAgentConfig() (bool, ctrl.Result, error) { return helper.ReconcileResult(err) } - _, err = k8sutils.CreateOrUpdateSecret(vectorAgentSecret, ctrl.Client) + _, err = k8s.CreateOrUpdateSecret(vectorAgentSecret, ctrl.Client) return helper.ReconcileResult(err) } @@ -154,18 +134,18 @@ func (ctrl *Controller) ensureVectorAgentDaemonSet() (bool, ctrl.Result, error) vectorAgentDaemonSet := ctrl.createVectorAgentDaemonSet() - _, err := k8sutils.CreateOrUpdateDaemonSet(vectorAgentDaemonSet, ctrl.Client) + _, err := k8s.CreateOrUpdateDaemonSet(vectorAgentDaemonSet, ctrl.Client) return helper.ReconcileResult(err) } func (ctrl *Controller) labelsForVectorAgent() map[string]string { return map[string]string{ - label.ManagedByLabelKey: "vector-operator", - label.NameLabelKey: "vector", - label.ComponentLabelKey: "Agent", - label.InstanceLabelKey: ctrl.Vector.Name, - label.VectorExcludeLabel: "true", + k8s.ManagedByLabelKey: "vector-operator", + k8s.NameLabelKey: "vector", + k8s.ComponentLabelKey: "Agent", + k8s.InstanceLabelKey: ctrl.Vector.Name, + k8s.VectorExcludeLabel: "true", } } @@ -195,29 +175,3 @@ func (ctrl *Controller) getControllerReference() []metav1.OwnerReference { }, } } - -func setSucceesStatus(ctx context.Context, v *vectorv1alpha1.Vector, c client.Client) error { - var status = true - v.Status.ConfigCheckResult = &status - v.Status.Reason = nil - - return k8sutils.UpdateStatus(ctx, v, c) -} - -func setFailedStatus(ctx context.Context, v *vectorv1alpha1.Vector, c client.Client, err error) error { - var status = false - var reason = err.Error() - v.Status.ConfigCheckResult = &status - v.Status.Reason = &reason - - return k8sutils.UpdateStatus(ctx, v, c) -} - -func SetLastAppliedPipelineStatus(ctx context.Context, v *vectorv1alpha1.Vector, c client.Client, hash *uint32) error { - - v.Status.LastAppliedConfigHash = hash - if err := k8sutils.UpdateStatus(ctx, v, c); err != nil { - return err - } - return nil -} diff --git a/controllers/factory/vectoragent/vector_agent_daemonset.go b/controllers/factory/vector/vectoragent/vectoragent_daemonset.go similarity index 100% rename from controllers/factory/vectoragent/vector_agent_daemonset.go rename to controllers/factory/vector/vectoragent/vectoragent_daemonset.go diff --git a/controllers/factory/vectoragent/vector_agent_rbac.go b/controllers/factory/vector/vectoragent/vectoragent_rbac.go similarity index 100% rename from controllers/factory/vectoragent/vector_agent_rbac.go rename to controllers/factory/vector/vectoragent/vectoragent_rbac.go diff --git a/controllers/factory/vectoragent/vector_agent_service.go b/controllers/factory/vector/vectoragent/vectoragent_service.go similarity index 100% rename from controllers/factory/vectoragent/vector_agent_service.go rename to controllers/factory/vector/vectoragent/vectoragent_service.go diff --git a/controllers/vector_controller.go b/controllers/vector_controller.go index df3446a4..84572ff1 100644 --- a/controllers/vector_controller.go +++ b/controllers/vector_controller.go @@ -20,7 +20,8 @@ import ( "context" "time" - "github.com/kaasops/vector-operator/controllers/factory/vectoragent" + "github.com/kaasops/vector-operator/controllers/factory/config" + "github.com/kaasops/vector-operator/controllers/factory/vector/vectoragent" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes" @@ -69,7 +70,7 @@ func (r *VectorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr v.Spec.Agent.DataDir = "/vector-data-dir" } - return r.CreateOrUpdateVector(v) + return r.CreateOrUpdateVector(ctx, v) } func (r *VectorReconciler) findVectorCustomResourceInstance(ctx context.Context, log logr.Logger, req ctrl.Request) (*vectorv1alpha1.Vector, bool, ctrl.Result, error) { @@ -99,10 +100,26 @@ func (r *VectorReconciler) SetupWithManager(mgr ctrl.Manager) error { Complete(r) } -func (r *VectorReconciler) CreateOrUpdateVector(v *vectorv1alpha1.Vector) (ctrl.Result, error) { - vectorAgentReconciler := vectoragent.NewController(v, r.Client, r.Clientset) +func (r *VectorReconciler) CreateOrUpdateVector(ctx context.Context, v *vectorv1alpha1.Vector) (ctrl.Result, error) { + // Init Controller for Vector Agent + vaCtrl := vectoragent.NewController(v, r.Client, r.Clientset) - if done, result, err := vectorAgentReconciler.EnsureVectorAgent(); done { + // Get Vector Config file + config, err := config.New(ctx, vaCtrl) + if err != nil { + return ctrl.Result{}, err + } + config.FillForVectorAgent() + + // Get Config in Json ([]byte) + byteCongif, err := config.GetByteConfig() + if err != nil { + return ctrl.Result{}, err + } + vaCtrl.Config = byteCongif + + // Start Reconcile Vector Agent + if done, result, err := vaCtrl.EnsureVectorAgent(); done { return result, err } diff --git a/controllers/vectorpipeline_controller.go b/controllers/vectorpipeline_controller.go index 43262678..b94278bc 100644 --- a/controllers/vectorpipeline_controller.go +++ b/controllers/vectorpipeline_controller.go @@ -29,8 +29,10 @@ import ( "github.com/go-logr/logr" vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" "github.com/kaasops/vector-operator/controllers/factory/config" + "github.com/kaasops/vector-operator/controllers/factory/config/configcheck" "github.com/kaasops/vector-operator/controllers/factory/pipeline" "github.com/kaasops/vector-operator/controllers/factory/pipeline/vectorpipeline" + "github.com/kaasops/vector-operator/controllers/factory/vector/vectoragent" ) // VectorPipelineReconciler reconciles a VectorPipeline object @@ -60,6 +62,7 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque log.Info("start Reconcile VectorPipeline") + // Get CR VectorPipeline vp, done, result, err := r.findVectorPipelineCustomResourceInstance(ctx, log, req) if done { return result, err @@ -68,10 +71,10 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque // Generate VectorPipeline Controller vpCtrl := vectorpipeline.NewController(vp) // Generate Pipeline Controller - vCtrl := pipeline.NewController(ctx, r.Client, vpCtrl) + pCtrl := pipeline.NewController(ctx, r.Client, vpCtrl) // Check Pipeline hash - checkResult, err := vCtrl.CheckHash() + checkResult, err := pCtrl.CheckHash() if err != nil { return ctrl.Result{}, err } @@ -80,6 +83,7 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque return ctrl.Result{}, nil } + // Generate Pipeline ConfigCheck for all Vectors vectorInstances := &vectorv1alpha1.VectorList{} err = r.List(ctx, vectorInstances) if err != nil { @@ -95,11 +99,46 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque if v.DeletionTimestamp != nil { continue } - if err = config.Check(ctx, &v, vCtrl, r.Client, r.Clientset); err != nil { + + // Init Controller for Vector Agent + vaCtrl := vectoragent.NewController(&v, r.Client, r.Clientset) + + // Get Vector Config file + config, err := config.New(ctx, vaCtrl) + if err != nil { + return ctrl.Result{}, err + } + config.FillForVectorPipeline(pCtrl) + + // Get Config in Json ([]byte) + byteConfig, err := config.GetByteConfig() + if err != nil { return ctrl.Result{}, err } - if err = vCtrl.SetLastAppliedPipelineStatus(); err != nil { + // Init CheckConfig + configCheck := configcheck.New(ctx, byteConfig, vaCtrl.Client, vaCtrl.ClientSet, vaCtrl.Vector.Name, vaCtrl.Vector.Namespace, vaCtrl.Vector.Spec.Agent.Image) + + // Start ConfigCheck + err = configCheck.Run() + if _, ok := err.(*configcheck.ErrConfigCheck); ok { + if err := pCtrl.SetFailedStatus(err); err != nil { + return ctrl.Result{}, err + } + if err = pCtrl.SetLastAppliedPipelineStatus(); err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{}, nil + } + if err != nil { + return ctrl.Result{}, err + } + + if err = pCtrl.SetSucceesStatus(); err != nil { + return ctrl.Result{}, err + } + + if err = pCtrl.SetLastAppliedPipelineStatus(); err != nil { return ctrl.Result{}, err } @@ -135,3 +174,7 @@ func (r *VectorPipelineReconciler) SetupWithManager(mgr ctrl.Manager) error { For(&vectorv1alpha1.VectorPipeline{}). Complete(r) } + +func checkHash() { + +} From e826bd1bce72a802836ef9141a98cf4da9e7de8e Mon Sep 17 00:00:00 2001 From: Vladimir <31961982+zvlb@users.noreply.github.com> Date: Fri, 28 Oct 2022 14:26:42 +0200 Subject: [PATCH 034/316] CleanUp --- controllers/vectorpipeline_controller.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/controllers/vectorpipeline_controller.go b/controllers/vectorpipeline_controller.go index b94278bc..96afc3ca 100644 --- a/controllers/vectorpipeline_controller.go +++ b/controllers/vectorpipeline_controller.go @@ -175,6 +175,3 @@ func (r *VectorPipelineReconciler) SetupWithManager(mgr ctrl.Manager) error { Complete(r) } -func checkHash() { - -} From fc7bc1182c24fa530483f6c54fe3584e37394454 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Fri, 28 Oct 2022 15:38:07 +0300 Subject: [PATCH 035/316] Delete config_check.go Signed-off-by: Zemtsov Vladimir --- controllers/factory/config/config_check.go | 64 ---------------------- 1 file changed, 64 deletions(-) delete mode 100644 controllers/factory/config/config_check.go diff --git a/controllers/factory/config/config_check.go b/controllers/factory/config/config_check.go deleted file mode 100644 index 68921970..00000000 --- a/controllers/factory/config/config_check.go +++ /dev/null @@ -1,64 +0,0 @@ -/* -Copyright 2022. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package config - -import ( - "context" - - vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" - "github.com/kaasops/vector-operator/controllers/factory/config/configcheck" - "github.com/kaasops/vector-operator/controllers/factory/pipeline" - "k8s.io/client-go/kubernetes" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/log" -) - -func Check(ctx context.Context, v *vectorv1alpha1.Vector, vCtrl *pipeline.Controller, c client.Client, cs *kubernetes.Clientset) error { - - log := log.FromContext(context.TODO()).WithValues("ConfigCheck Vector Pipeline", vCtrl.Pipeline.Name()) - - var vCtrls []pipeline.Controller - vCtrls = append(vCtrls, *vCtrl) - - cfg, err := GenerateVectorConfig(v, vCtrls) - if err != nil { - return err - } - - // if p.Type() == vectorpipeline.Type { - // log.Info("It's VectorPipeline") - // } - - cfgJson, err := VectorConfigToJson(cfg) - if err != nil { - return err - } - - err = configcheck.Run(cfgJson, c, cs, v.Name, v.Namespace, v.Spec.Agent.Image) - if _, ok := err.(*configcheck.ErrConfigCheck); ok { - if err := vCtrl.SetFailedStatus(err); err != nil { - return err - } - log.Error(err, "Vector Config has error") - return nil - } - if err != nil { - return err - } - - return vCtrl.SetSucceesStatus() -} From e1ef26d0b406fe6f45b376e3203655c9e01ca809 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Fri, 28 Oct 2022 16:44:18 +0300 Subject: [PATCH 036/316] Cleanup findCustomResourceInstance method Signed-off-by: Zemtsov Vladimir --- .../clustervectorpipeline_controller.go | 27 ++++++++--------- controllers/vector_controller.go | 30 +++++++++---------- controllers/vectorpipeline_controller.go | 28 ++++++++--------- 3 files changed, 38 insertions(+), 47 deletions(-) diff --git a/controllers/clustervectorpipeline_controller.go b/controllers/clustervectorpipeline_controller.go index 16339b4c..f196f660 100644 --- a/controllers/clustervectorpipeline_controller.go +++ b/controllers/clustervectorpipeline_controller.go @@ -26,7 +26,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" - "github.com/go-logr/logr" vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" ) @@ -57,9 +56,14 @@ func (r *ClusterVectorPipelineReconciler) Reconcile(ctx context.Context, req ctr log.Info("start Reconcile ClusterVectorPipeline") - // cvp, done, result, err := r.findClusterVectorPipelineCustomResourceInstance(ctx, log, req) - // if done { - // return result, err + // cvp, err := r.findClusterVectorPipelineCustomResourceInstance(ctx, req) + // if err != nil { + // log.Error(err, "Failed to get Cluster Vector Pipeline") + // return ctrl.Result{}, err + // } + // if cvp == nil { + // log.Info("Cluster Vector Pipeline CR not found. Ignoring since object must be deleted") + // return ctrl.Result{}, nil // } // hash, err := vectorpipeline.GetSpecHash(cvp.Spec) // if err != nil { @@ -98,24 +102,17 @@ func (r *ClusterVectorPipelineReconciler) Reconcile(ctx context.Context, req ctr return ctrl.Result{}, nil } -func (r *ClusterVectorPipelineReconciler) findClusterVectorPipelineCustomResourceInstance(ctx context.Context, log logr.Logger, req ctrl.Request) (*vectorv1alpha1.ClusterVectorPipeline, bool, ctrl.Result, error) { +func (r *ClusterVectorPipelineReconciler) findClusterVectorPipelineCustomResourceInstance(ctx context.Context, req ctrl.Request) (*vectorv1alpha1.ClusterVectorPipeline, error) { // fetch the master instance cvp := &vectorv1alpha1.ClusterVectorPipeline{} err := r.Get(ctx, req.NamespacedName, cvp) if err != nil { if errors.IsNotFound(err) { - // Request object not found, could have been deleted after reconcile request. - // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers. - // Return and don't requeue - log.Info("ClusterVectorPipeline CR not found. Ignoring since object must be deleted") - return nil, true, ctrl.Result{}, nil + return nil, nil } - // Error reading the object - requeue the request. - log.Error(err, "Failed to get Vector") - return nil, true, ctrl.Result{}, err + return nil, err } - log.Info("Get Vector Pipeline" + cvp.Name) - return cvp, false, ctrl.Result{}, nil + return cvp, nil } // SetupWithManager sets up the controller with the Manager. diff --git a/controllers/vector_controller.go b/controllers/vector_controller.go index 84572ff1..67a0eb2b 100644 --- a/controllers/vector_controller.go +++ b/controllers/vector_controller.go @@ -29,7 +29,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" - "github.com/go-logr/logr" vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" ) @@ -61,9 +60,14 @@ func (r *VectorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr log.Info("start Reconcile Vector") - v, done, result, err := r.findVectorCustomResourceInstance(ctx, log, req) - if done { - return result, err + v, err := r.findVectorCustomResourceInstance(ctx, req) + if err != nil { + log.Error(err, "Failed to get Vector") + return ctrl.Result{}, err + } + if v == nil { + log.Info("Vector CR not found. Ignoring since object must be deleted") + return ctrl.Result{}, nil } if v.Spec.Agent.DataDir == "" { @@ -73,24 +77,18 @@ func (r *VectorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr return r.CreateOrUpdateVector(ctx, v) } -func (r *VectorReconciler) findVectorCustomResourceInstance(ctx context.Context, log logr.Logger, req ctrl.Request) (*vectorv1alpha1.Vector, bool, ctrl.Result, error) { +func (r *VectorReconciler) findVectorCustomResourceInstance(ctx context.Context, req ctrl.Request) (*vectorv1alpha1.Vector, error) { // fetch the master instance v := &vectorv1alpha1.Vector{} err := r.Get(ctx, req.NamespacedName, v) if err != nil { if errors.IsNotFound(err) { - // Request object not found, could have been deleted after reconcile request. - // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers. - // Return and don't requeue - log.Info("Vector CR not found. Ignoring since object must be deleted") - return nil, true, ctrl.Result{}, nil + return nil, nil } - // Error reading the object - requeue the request. - log.Error(err, "Failed to get Vector") - return nil, true, ctrl.Result{}, err + return nil, err } - return v, false, ctrl.Result{}, nil + return v, nil } // SetupWithManager sets up the controller with the Manager. @@ -112,11 +110,11 @@ func (r *VectorReconciler) CreateOrUpdateVector(ctx context.Context, v *vectorv1 config.FillForVectorAgent() // Get Config in Json ([]byte) - byteCongif, err := config.GetByteConfig() + byteConfig, err := config.GetByteConfig() if err != nil { return ctrl.Result{}, err } - vaCtrl.Config = byteCongif + vaCtrl.Config = byteConfig // Start Reconcile Vector Agent if done, result, err := vaCtrl.EnsureVectorAgent(); done { diff --git a/controllers/vectorpipeline_controller.go b/controllers/vectorpipeline_controller.go index 96afc3ca..72791e69 100644 --- a/controllers/vectorpipeline_controller.go +++ b/controllers/vectorpipeline_controller.go @@ -26,7 +26,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" - "github.com/go-logr/logr" vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" "github.com/kaasops/vector-operator/controllers/factory/config" "github.com/kaasops/vector-operator/controllers/factory/config/configcheck" @@ -63,9 +62,14 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque log.Info("start Reconcile VectorPipeline") // Get CR VectorPipeline - vp, done, result, err := r.findVectorPipelineCustomResourceInstance(ctx, log, req) - if done { - return result, err + vp, err := r.findVectorPipelineCustomResourceInstance(ctx, req) + if err != nil { + log.Error(err, "Failed to get Vector Pipeline") + return ctrl.Result{}, err + } + if vp == nil { + log.Info("VectorPIpeline CR not found. Ignoring since object must be deleted") + return ctrl.Result{}, nil } // Generate VectorPipeline Controller @@ -148,24 +152,17 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque return ctrl.Result{}, nil } -func (r *VectorPipelineReconciler) findVectorPipelineCustomResourceInstance(ctx context.Context, log logr.Logger, req ctrl.Request) (*vectorv1alpha1.VectorPipeline, bool, ctrl.Result, error) { +func (r *VectorPipelineReconciler) findVectorPipelineCustomResourceInstance(ctx context.Context, req ctrl.Request) (*vectorv1alpha1.VectorPipeline, error) { // fetch the master instance vp := &vectorv1alpha1.VectorPipeline{} err := r.Get(ctx, req.NamespacedName, vp) if err != nil { if errors.IsNotFound(err) { - // Request object not found, could have been deleted after reconcile request. - // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers. - // Return and don't requeue - log.Info("VectorPipeline CR not found. Ignoring since object must be deleted") - return nil, true, ctrl.Result{}, nil + return nil, nil } - // Error reading the object - requeue the request. - log.Error(err, "Failed to get Vector") - return nil, true, ctrl.Result{}, err + return nil, err } - log.Info("Get Vector Pipeline" + vp.Name) - return vp, false, ctrl.Result{}, nil + return vp, nil } // SetupWithManager sets up the controller with the Manager. @@ -174,4 +171,3 @@ func (r *VectorPipelineReconciler) SetupWithManager(mgr ctrl.Manager) error { For(&vectorv1alpha1.VectorPipeline{}). Complete(r) } - From 150dfb2e2931fe60359d0e78d862abe9375e71a1 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Fri, 28 Oct 2022 17:18:51 +0300 Subject: [PATCH 037/316] Init tests for hash Signed-off-by: Zemtsov Vladimir --- controllers/factory/utils/hash/hash_test.go | 21 +++++++++++++++++++++ go.mod | 4 +++- 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 controllers/factory/utils/hash/hash_test.go diff --git a/controllers/factory/utils/hash/hash_test.go b/controllers/factory/utils/hash/hash_test.go new file mode 100644 index 00000000..a5d913ff --- /dev/null +++ b/controllers/factory/utils/hash/hash_test.go @@ -0,0 +1,21 @@ +package hash + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestGet(t *testing.T) { + hashCase := func(bytes []byte, want uint32) func(t *testing.T) { + return func(t *testing.T) { + req := require.New(t) + + result := Get(bytes) + req.Equal(result, want) + } + } + + t.Run("Simple case", hashCase([]byte("test"), uint32(3632233996))) + t.Run("Zero case", hashCase([]byte(""), uint32(0))) +} diff --git a/go.mod b/go.mod index d9825a97..a2c10637 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,10 @@ module github.com/kaasops/vector-operator go 1.18 require ( - github.com/go-logr/logr v1.2.0 github.com/mitchellh/mapstructure v1.5.0 github.com/onsi/ginkgo v1.16.5 github.com/onsi/gomega v1.18.1 + github.com/stretchr/testify v1.7.0 k8s.io/api v0.24.2 k8s.io/apimachinery v0.24.2 k8s.io/client-go v0.24.2 @@ -31,6 +31,7 @@ require ( github.com/evanphx/json-patch v4.12.0+incompatible // indirect github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect + github.com/go-logr/logr v1.2.0 // indirect github.com/go-logr/zapr v1.2.0 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.19.5 // indirect @@ -52,6 +53,7 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/nxadm/tail v1.4.8 // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.12.1 // indirect github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.32.1 // indirect From 68328d88f664e07883ecd4a5e04197d361946663 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Fri, 28 Oct 2022 17:19:03 +0300 Subject: [PATCH 038/316] remove package helpers Signed-off-by: Zemtsov Vladimir --- .../factory/config/configcheck/configcheck.go | 18 +--- controllers/factory/utils/helper/helper.go | 26 ------ controllers/factory/utils/k8s/k8s.go | 83 +++++++++---------- .../vectoragent/vectoragent_controller.go | 76 +++++++---------- controllers/vector_controller.go | 4 +- 5 files changed, 78 insertions(+), 129 deletions(-) delete mode 100644 controllers/factory/utils/helper/helper.go diff --git a/controllers/factory/config/configcheck/configcheck.go b/controllers/factory/config/configcheck/configcheck.go index 9ecfc337..b99c75d2 100644 --- a/controllers/factory/config/configcheck/configcheck.go +++ b/controllers/factory/config/configcheck/configcheck.go @@ -21,11 +21,9 @@ import ( "math/rand" "time" - "github.com/kaasops/vector-operator/controllers/factory/utils/helper" "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" corev1 "k8s.io/api/core/v1" "k8s.io/client-go/kubernetes" - ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" ) @@ -100,19 +98,13 @@ func (cc *ConfigCheck) Run() error { } func (cc *ConfigCheck) ensureVectorConfigCheckRBAC() error { - if done, _, err := cc.ensureVectorConfigCheckServiceAccount(); done { - return err - } - - return nil + return cc.ensureVectorConfigCheckServiceAccount() } -func (cc *ConfigCheck) ensureVectorConfigCheckServiceAccount() (bool, ctrl.Result, error) { +func (cc *ConfigCheck) ensureVectorConfigCheckServiceAccount() error { vectorAgentServiceAccount := cc.createVectorConfigCheckServiceAccount() - _, err := k8s.CreateOrUpdateServiceAccount(vectorAgentServiceAccount, cc.Client) - - return helper.ReconcileResult(err) + return k8s.CreateOrUpdateServiceAccount(vectorAgentServiceAccount, cc.Client) } func (cc *ConfigCheck) ensureVectorConfigCheckConfig() error { vectorConfigCheckSecret, err := cc.createVectorConfigCheckConfig() @@ -120,9 +112,7 @@ func (cc *ConfigCheck) ensureVectorConfigCheckConfig() error { return err } - _, err = k8s.CreateOrUpdateSecret(vectorConfigCheckSecret, cc.Client) - - return err + return k8s.CreateOrUpdateSecret(vectorConfigCheckSecret, cc.Client) } func (cc *ConfigCheck) checkVectorConfigCheckPod() error { diff --git a/controllers/factory/utils/helper/helper.go b/controllers/factory/utils/helper/helper.go deleted file mode 100644 index 5e3e0c64..00000000 --- a/controllers/factory/utils/helper/helper.go +++ /dev/null @@ -1,26 +0,0 @@ -/* -Copyright 2022. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package helper - -import ctrl "sigs.k8s.io/controller-runtime" - -func ReconcileResult(err error) (bool, ctrl.Result, error) { - if err != nil { - return true, ctrl.Result{}, err - } - return false, ctrl.Result{}, nil -} diff --git a/controllers/factory/utils/k8s/k8s.go b/controllers/factory/utils/k8s/k8s.go index dff1bf29..c244b739 100644 --- a/controllers/factory/utils/k8s/k8s.go +++ b/controllers/factory/utils/k8s/k8s.go @@ -29,34 +29,33 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/reconcile" ) -func CreateOrUpdateService(svc *corev1.Service, c client.Client) (*reconcile.Result, error) { +func CreateOrUpdateService(svc *corev1.Service, c client.Client) error { return reconcileService(svc, c) } -func CreateOrUpdateSecret(secret *corev1.Secret, c client.Client) (*reconcile.Result, error) { +func CreateOrUpdateSecret(secret *corev1.Secret, c client.Client) error { return reconcileSecret(secret, c) } -func CreateOrUpdateDaemonSet(daemonSet *appsv1.DaemonSet, c client.Client) (*reconcile.Result, error) { +func CreateOrUpdateDaemonSet(daemonSet *appsv1.DaemonSet, c client.Client) error { return reconcileDaemonSet(daemonSet, c) } -func CreateOrUpdateStatefulSet(statefulSet *appsv1.StatefulSet, c client.Client) (*reconcile.Result, error) { +func CreateOrUpdateStatefulSet(statefulSet *appsv1.StatefulSet, c client.Client) error { return reconcileStatefulSet(statefulSet, c) } -func CreateOrUpdateServiceAccount(secret *corev1.ServiceAccount, c client.Client) (*reconcile.Result, error) { +func CreateOrUpdateServiceAccount(secret *corev1.ServiceAccount, c client.Client) error { return reconcileServiceAccount(secret, c) } -func CreateOrUpdateClusterRole(secret *rbacv1.ClusterRole, c client.Client) (*reconcile.Result, error) { +func CreateOrUpdateClusterRole(secret *rbacv1.ClusterRole, c client.Client) error { return reconcileClusterRole(secret, c) } -func CreateOrUpdateClusterRoleBinding(secret *rbacv1.ClusterRoleBinding, c client.Client) (*reconcile.Result, error) { +func CreateOrUpdateClusterRoleBinding(secret *rbacv1.ClusterRoleBinding, c client.Client) error { return reconcileClusterRoleBinding(secret, c) } @@ -104,7 +103,7 @@ func UpdateStatus(ctx context.Context, obj client.Object, c client.Client) error return c.Status().Update(ctx, obj) } -func reconcileService(obj runtime.Object, c client.Client) (*reconcile.Result, error) { +func reconcileService(obj runtime.Object, c client.Client) error { existing := &corev1.Service{} desired := obj.(*corev1.Service) @@ -113,23 +112,23 @@ func reconcileService(obj runtime.Object, c client.Client) (*reconcile.Result, e if err != nil && errors.IsAlreadyExists(err) { err := c.Get(context.TODO(), client.ObjectKeyFromObject(desired), existing) if err != nil { - return nil, err + return err } if !equality.Semantic.DeepEqual(existing, desired) { existing.Spec = desired.Spec existing.Labels = desired.Labels err := c.Update(context.TODO(), existing) - return nil, err + return err } } if err != nil && !errors.IsAlreadyExists(err) { - return nil, err + return err } - return nil, nil + return nil } -func reconcileSecret(obj runtime.Object, c client.Client) (*reconcile.Result, error) { +func reconcileSecret(obj runtime.Object, c client.Client) error { existing := &corev1.Secret{} desired := obj.(*corev1.Secret) @@ -138,23 +137,23 @@ func reconcileSecret(obj runtime.Object, c client.Client) (*reconcile.Result, er if err != nil && errors.IsAlreadyExists(err) { err := c.Get(context.TODO(), client.ObjectKeyFromObject(desired), existing) if err != nil { - return nil, err + return err } if !equality.Semantic.DeepEqual(existing, desired) { existing.Data = desired.Data existing.Labels = desired.Labels err := c.Update(context.TODO(), existing) - return nil, err + return err } } if err != nil && !errors.IsAlreadyExists(err) { - return nil, err + return err } - return nil, nil + return nil } -func reconcileDaemonSet(obj runtime.Object, c client.Client) (*reconcile.Result, error) { +func reconcileDaemonSet(obj runtime.Object, c client.Client) error { existing := &appsv1.DaemonSet{} desired := obj.(*appsv1.DaemonSet) @@ -163,23 +162,23 @@ func reconcileDaemonSet(obj runtime.Object, c client.Client) (*reconcile.Result, if err != nil && errors.IsAlreadyExists(err) { err := c.Get(context.TODO(), client.ObjectKeyFromObject(desired), existing) if err != nil { - return nil, err + return err } if !equality.Semantic.DeepEqual(existing, desired) { existing.Spec = desired.Spec existing.Labels = desired.Labels err := c.Update(context.TODO(), existing) - return nil, err + return err } } if err != nil && !errors.IsAlreadyExists(err) { - return nil, err + return err } - return nil, nil + return nil } -func reconcileStatefulSet(obj runtime.Object, c client.Client) (*reconcile.Result, error) { +func reconcileStatefulSet(obj runtime.Object, c client.Client) error { existing := &appsv1.StatefulSet{} desired := obj.(*appsv1.StatefulSet) @@ -188,23 +187,23 @@ func reconcileStatefulSet(obj runtime.Object, c client.Client) (*reconcile.Resul if err != nil && errors.IsAlreadyExists(err) { err := c.Get(context.TODO(), client.ObjectKeyFromObject(desired), existing) if err != nil { - return nil, err + return err } if !equality.Semantic.DeepEqual(existing, desired) { existing.Spec = desired.Spec existing.Labels = desired.Labels err := c.Update(context.TODO(), existing) - return nil, err + return err } } if err != nil && !errors.IsAlreadyExists(err) { - return nil, err + return err } - return nil, nil + return nil } -func reconcileServiceAccount(obj runtime.Object, c client.Client) (*reconcile.Result, error) { +func reconcileServiceAccount(obj runtime.Object, c client.Client) error { existing := &corev1.ServiceAccount{} desired := obj.(*corev1.ServiceAccount) @@ -213,17 +212,17 @@ func reconcileServiceAccount(obj runtime.Object, c client.Client) (*reconcile.Re if err != nil && errors.IsAlreadyExists(err) { err := c.Get(context.TODO(), client.ObjectKeyFromObject(desired), existing) if err != nil { - return nil, err + return err } } if err != nil && !errors.IsAlreadyExists(err) { - return nil, err + return err } - return nil, nil + return nil } -func reconcileClusterRole(obj runtime.Object, c client.Client) (*reconcile.Result, error) { +func reconcileClusterRole(obj runtime.Object, c client.Client) error { existing := &rbacv1.ClusterRole{} desired := obj.(*rbacv1.ClusterRole) @@ -232,22 +231,22 @@ func reconcileClusterRole(obj runtime.Object, c client.Client) (*reconcile.Resul if err != nil && errors.IsAlreadyExists(err) { err := c.Get(context.TODO(), client.ObjectKeyFromObject(desired), existing) if err != nil { - return nil, err + return err } if !equality.Semantic.DeepEqual(existing, desired) { existing.Rules = desired.Rules err := c.Update(context.TODO(), existing) - return nil, err + return err } } if err != nil && !errors.IsAlreadyExists(err) { - return nil, err + return err } - return nil, nil + return nil } -func reconcileClusterRoleBinding(obj runtime.Object, c client.Client) (*reconcile.Result, error) { +func reconcileClusterRoleBinding(obj runtime.Object, c client.Client) error { existing := &rbacv1.ClusterRoleBinding{} desired := obj.(*rbacv1.ClusterRoleBinding) @@ -256,18 +255,18 @@ func reconcileClusterRoleBinding(obj runtime.Object, c client.Client) (*reconcil if err != nil && errors.IsAlreadyExists(err) { err := c.Get(context.TODO(), client.ObjectKeyFromObject(desired), existing) if err != nil { - return nil, err + return err } if !equality.Semantic.DeepEqual(existing, desired) { existing.RoleRef = desired.RoleRef existing.Subjects = desired.Subjects err := c.Update(context.TODO(), existing) - return nil, err + return err } } if err != nil && !errors.IsAlreadyExists(err) { - return nil, err + return err } - return nil, nil + return nil } diff --git a/controllers/factory/vector/vectoragent/vectoragent_controller.go b/controllers/factory/vector/vectoragent/vectoragent_controller.go index 0badbf26..84e75d3b 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_controller.go +++ b/controllers/factory/vector/vectoragent/vectoragent_controller.go @@ -19,85 +19,77 @@ package vectoragent import ( "context" - "github.com/kaasops/vector-operator/controllers/factory/utils/helper" "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/utils/pointer" - ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/log" ) -func (ctrl *Controller) EnsureVectorAgent() (done bool, result ctrl.Result, err error) { +func (ctrl *Controller) EnsureVectorAgent() error { ctx := context.Background() log := log.FromContext(ctx).WithValues("vector-agent", ctrl.Vector.Name) log.Info("start Reconcile Vector Agent") - if done, result, err = ctrl.ensureVectorAgentRBAC(); done { - return + if err := ctrl.ensureVectorAgentRBAC(); err != nil { + return err } if ctrl.Vector.Spec.Agent.Service { - if done, result, err = ctrl.ensureVectorAgentService(); done { - return + if err := ctrl.ensureVectorAgentService(); err != nil { + return err } } - if done, result, err = ctrl.ensureVectorAgentConfig(); done { - return + if err := ctrl.ensureVectorAgentConfig(); err != nil { + return err } - if done, result, err = ctrl.ensureVectorAgentDaemonSet(); done { - return + if err := ctrl.ensureVectorAgentDaemonSet(); err != nil { + return err } - return + return nil } -func (ctrl *Controller) ensureVectorAgentRBAC() (bool, ctrl.Result, error) { +func (ctrl *Controller) ensureVectorAgentRBAC() error { ctx := context.Background() log := log.FromContext(ctx).WithValues("vector-agent-rbac", ctrl.Vector.Name) log.Info("start Reconcile Vector Agent RBAC") - if done, _, err := ctrl.ensureVectorAgentServiceAccount(); done { - return helper.ReconcileResult(err) + if err := ctrl.ensureVectorAgentServiceAccount(); err != nil { + return err } - if done, _, err := ctrl.ensureVectorAgentClusterRole(); done { - return helper.ReconcileResult(err) + if err := ctrl.ensureVectorAgentClusterRole(); err != nil { + return err } - if done, _, err := ctrl.ensureVectorAgentClusterRoleBinding(); done { - return helper.ReconcileResult(err) + if err := ctrl.ensureVectorAgentClusterRoleBinding(); err != nil { + return err } - return helper.ReconcileResult(nil) + return nil } -func (ctrl *Controller) ensureVectorAgentServiceAccount() (bool, ctrl.Result, error) { +func (ctrl *Controller) ensureVectorAgentServiceAccount() error { vectorAgentServiceAccount := ctrl.createVectorAgentServiceAccount() - _, err := k8s.CreateOrUpdateServiceAccount(vectorAgentServiceAccount, ctrl.Client) - - return helper.ReconcileResult(err) + return k8s.CreateOrUpdateServiceAccount(vectorAgentServiceAccount, ctrl.Client) } -func (ctrl *Controller) ensureVectorAgentClusterRole() (bool, ctrl.Result, error) { +func (ctrl *Controller) ensureVectorAgentClusterRole() error { vectorAgentClusterRole := ctrl.createVectorAgentClusterRole() - _, err := k8s.CreateOrUpdateClusterRole(vectorAgentClusterRole, ctrl.Client) - - return helper.ReconcileResult(err) + return k8s.CreateOrUpdateClusterRole(vectorAgentClusterRole, ctrl.Client) } -func (ctrl *Controller) ensureVectorAgentClusterRoleBinding() (bool, ctrl.Result, error) { +func (ctrl *Controller) ensureVectorAgentClusterRoleBinding() error { vectorAgentClusterRoleBinding := ctrl.createVectorAgentClusterRoleBinding() - _, err := k8s.CreateOrUpdateClusterRoleBinding(vectorAgentClusterRoleBinding, ctrl.Client) - - return helper.ReconcileResult(err) + return k8s.CreateOrUpdateClusterRoleBinding(vectorAgentClusterRoleBinding, ctrl.Client) } -func (ctrl *Controller) ensureVectorAgentService() (bool, ctrl.Result, error) { +func (ctrl *Controller) ensureVectorAgentService() error { ctx := context.Background() log := log.FromContext(ctx).WithValues("vector-agent-service", ctrl.Vector.Name) @@ -105,12 +97,10 @@ func (ctrl *Controller) ensureVectorAgentService() (bool, ctrl.Result, error) { vectorAgentService := ctrl.createVectorAgentService() - _, err := k8s.CreateOrUpdateService(vectorAgentService, ctrl.Client) - - return helper.ReconcileResult(err) + return k8s.CreateOrUpdateService(vectorAgentService, ctrl.Client) } -func (ctrl *Controller) ensureVectorAgentConfig() (bool, ctrl.Result, error) { +func (ctrl *Controller) ensureVectorAgentConfig() error { ctx := context.Background() log := log.FromContext(ctx).WithValues("vector-agent-secret", ctrl.Vector.Name) @@ -118,15 +108,13 @@ func (ctrl *Controller) ensureVectorAgentConfig() (bool, ctrl.Result, error) { vectorAgentSecret, err := ctrl.createVectorAgentConfig(ctx) if err != nil { - return helper.ReconcileResult(err) + return err } - _, err = k8s.CreateOrUpdateSecret(vectorAgentSecret, ctrl.Client) - - return helper.ReconcileResult(err) + return k8s.CreateOrUpdateSecret(vectorAgentSecret, ctrl.Client) } -func (ctrl *Controller) ensureVectorAgentDaemonSet() (bool, ctrl.Result, error) { +func (ctrl *Controller) ensureVectorAgentDaemonSet() error { ctx := context.Background() log := log.FromContext(ctx).WithValues("vector-agent-daemon-set", ctrl.Vector.Name) @@ -134,9 +122,7 @@ func (ctrl *Controller) ensureVectorAgentDaemonSet() (bool, ctrl.Result, error) vectorAgentDaemonSet := ctrl.createVectorAgentDaemonSet() - _, err := k8s.CreateOrUpdateDaemonSet(vectorAgentDaemonSet, ctrl.Client) - - return helper.ReconcileResult(err) + return k8s.CreateOrUpdateDaemonSet(vectorAgentDaemonSet, ctrl.Client) } func (ctrl *Controller) labelsForVectorAgent() map[string]string { diff --git a/controllers/vector_controller.go b/controllers/vector_controller.go index 67a0eb2b..efab81ab 100644 --- a/controllers/vector_controller.go +++ b/controllers/vector_controller.go @@ -117,8 +117,8 @@ func (r *VectorReconciler) CreateOrUpdateVector(ctx context.Context, v *vectorv1 vaCtrl.Config = byteConfig // Start Reconcile Vector Agent - if done, result, err := vaCtrl.EnsureVectorAgent(); done { - return result, err + if err := vaCtrl.EnsureVectorAgent(); err != nil { + return ctrl.Result{}, err } return ctrl.Result{RequeueAfter: 15 * time.Second}, nil From 217bb0e30dc08384163e969f6ba0a9a3fe9ce339 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Sat, 29 Oct 2022 16:14:55 +0300 Subject: [PATCH 039/316] Add test for k8s utils package Signed-off-by: Zemtsov Vladimir --- controllers/factory/utils/hash/hash_test.go | 16 + controllers/factory/utils/k8s/k8s.go | 15 +- controllers/factory/utils/k8s/k8s_test.go | 564 ++++++++++++++++++++ 3 files changed, 585 insertions(+), 10 deletions(-) create mode 100644 controllers/factory/utils/k8s/k8s_test.go diff --git a/controllers/factory/utils/hash/hash_test.go b/controllers/factory/utils/hash/hash_test.go index a5d913ff..615b676d 100644 --- a/controllers/factory/utils/hash/hash_test.go +++ b/controllers/factory/utils/hash/hash_test.go @@ -1,3 +1,19 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package hash import ( diff --git a/controllers/factory/utils/k8s/k8s.go b/controllers/factory/utils/k8s/k8s.go index c244b739..5ee3c494 100644 --- a/controllers/factory/utils/k8s/k8s.go +++ b/controllers/factory/utils/k8s/k8s.go @@ -142,8 +142,7 @@ func reconcileSecret(obj runtime.Object, c client.Client) error { if !equality.Semantic.DeepEqual(existing, desired) { existing.Data = desired.Data existing.Labels = desired.Labels - err := c.Update(context.TODO(), existing) - return err + return c.Update(context.TODO(), existing) } } if err != nil && !errors.IsAlreadyExists(err) { @@ -167,8 +166,7 @@ func reconcileDaemonSet(obj runtime.Object, c client.Client) error { if !equality.Semantic.DeepEqual(existing, desired) { existing.Spec = desired.Spec existing.Labels = desired.Labels - err := c.Update(context.TODO(), existing) - return err + return c.Update(context.TODO(), existing) } } if err != nil && !errors.IsAlreadyExists(err) { @@ -192,8 +190,7 @@ func reconcileStatefulSet(obj runtime.Object, c client.Client) error { if !equality.Semantic.DeepEqual(existing, desired) { existing.Spec = desired.Spec existing.Labels = desired.Labels - err := c.Update(context.TODO(), existing) - return err + return c.Update(context.TODO(), existing) } } if err != nil && !errors.IsAlreadyExists(err) { @@ -235,8 +232,7 @@ func reconcileClusterRole(obj runtime.Object, c client.Client) error { } if !equality.Semantic.DeepEqual(existing, desired) { existing.Rules = desired.Rules - err := c.Update(context.TODO(), existing) - return err + return c.Update(context.TODO(), existing) } } if err != nil && !errors.IsAlreadyExists(err) { @@ -260,8 +256,7 @@ func reconcileClusterRoleBinding(obj runtime.Object, c client.Client) error { if !equality.Semantic.DeepEqual(existing, desired) { existing.RoleRef = desired.RoleRef existing.Subjects = desired.Subjects - err := c.Update(context.TODO(), existing) - return err + return c.Update(context.TODO(), existing) } } if err != nil && !errors.IsAlreadyExists(err) { diff --git a/controllers/factory/utils/k8s/k8s_test.go b/controllers/factory/utils/k8s/k8s_test.go new file mode 100644 index 00000000..ed31231d --- /dev/null +++ b/controllers/factory/utils/k8s/k8s_test.go @@ -0,0 +1,564 @@ +// /* +// Copyright 2022. + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// */ + +package k8s + +import ( + "context" + "testing" + + // . "github.com/onsi/ginkgo/v2" + // . "github.com/onsi/gomega" + "github.com/stretchr/testify/require" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" +) + +// type objects interface { +// *corev1.Service | *corev1.Secret | *appsv1.DaemonSet | *appsv1.StatefulSet | *corev1.ServiceAccount | *rbacv1.ClusterRole | *rbacv1.ClusterRoleBinding +// } + +func TestCreatePod(t *testing.T) { + createPodCase := func(objInit, obj *corev1.Pod, want error) func(t *testing.T) { + return func(t *testing.T) { + req := require.New(t) + + cl := fake.NewClientBuilder().WithObjects(objInit).Build() + + err := CreatePod(obj, cl) + req.Equal(err, want) + } + } + + obj_init := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "init", + Namespace: "test-namespace", + ResourceVersion: "", + }, + } + obj_case1 := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace", + }, + } + obj_case2 := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "init", + Namespace: "test-namespace", + }, + } + gr_case2 := schema.GroupResource{ + Group: "", + Resource: "pods", + } + error_case2 := apierrors.NewAlreadyExists(gr_case2, obj_case2.ObjectMeta.Name) + + t.Run("Create not exist pod case", createPodCase(obj_init, obj_case1, nil)) + t.Run("Create alredy exist pod case", createPodCase(obj_init, obj_case2, error_case2)) +} + +// func CreatePod(pod *corev1.Pod, c client.Client) error { +// err := c.Create(context.TODO(), pod) +// if err != nil { +// return err +// } +// return nil +// } + +func TestGetPod(t *testing.T) { + getPodCase := func(objInit, obj, wantPod *corev1.Pod, want error) func(t *testing.T) { + return func(t *testing.T) { + req := require.New(t) + + cl := fake.NewClientBuilder().WithObjects(objInit).Build() + + result, err := GetPod(obj, cl) + if result != nil { + req.Equal(result.ObjectMeta, wantPod.ObjectMeta) + } + req.Equal(err, want) + } + } + + obj_init := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "init", + Namespace: "test-namespace", + ResourceVersion: "", + }, + } + obj_case1 := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace", + }, + } + gvr_case1 := schema.GroupVersionResource{ + Group: "", + Version: "v1", + Resource: "pods", + } + error_case1 := apierrors.NewNotFound(gvr_case1.GroupResource(), obj_case1.ObjectMeta.Name) + obj_case2 := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "init", + Namespace: "test-namespace", + }, + } + + t.Run("Get not exist pod case", getPodCase(obj_init, obj_case1, nil, error_case1)) + t.Run("Get exist pod case", getPodCase(obj_init, obj_case2, obj_init, nil)) +} + +func TestUpdateStatus(t *testing.T) { + updateStatusCase := func(objInit, obj client.Object, want error) func(t *testing.T) { + return func(t *testing.T) { + req := require.New(t) + + cl := fake.NewClientBuilder().WithObjects(objInit).Build() + + err := UpdateStatus(context.Background(), obj, cl) + + req.Equal(err, want) + } + } + + obj_init := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "init", + Namespace: "test-namespace", + ResourceVersion: "", + }, + } + obj_case1 := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace", + }, + } + obj_case2 := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "init", + Namespace: "test-namespace", + }, + } + obj_case3 := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace2", + }, + } + obj_case4 := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{}, + } + error_case4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) + + obj_case5 := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace", + }, + Status: appsv1.DeploymentStatus{ + Replicas: 10, + }, + } + gvr_case5 := schema.GroupVersionResource{ + Group: "apps", + Version: "v1", + Resource: "deployments", + } + error_case5 := apierrors.NewNotFound(gvr_case5.GroupResource(), obj_case5.ObjectMeta.Name) + + init_obj_case6 := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace", + }, + Status: appsv1.DeploymentStatus{ + Replicas: 5, + }, + } + obj_case6 := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace", + }, + Status: appsv1.DeploymentStatus{ + Replicas: 10, + }, + } + + t.Run("Update Simple case", updateStatusCase(obj_init, obj_case1, nil)) + t.Run("Update Alredy exist case", updateStatusCase(obj_init, obj_case2, nil)) + t.Run("Update with Another Namespace case", updateStatusCase(obj_init, obj_case3, nil)) + t.Run("Update without Name case", updateStatusCase(obj_init, obj_case4, error_case4)) + t.Run("Update not exist Deployment with wrong init case", updateStatusCase(obj_init, obj_case5, error_case5)) + t.Run("Update Deployment case", updateStatusCase(init_obj_case6, obj_case6, nil)) + +} + +func TestCreateOrUpdateService(t *testing.T) { + reconcileServiceCase := func(objInit, obj *corev1.Service, want error) func(t *testing.T) { + return func(t *testing.T) { + req := require.New(t) + + cl := fake.NewClientBuilder().WithObjects(objInit).Build() + + err := CreateOrUpdateService(obj, cl) + + req.Equal(err, want) + } + } + + service_init := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "init", + Namespace: "test-namespace", + ResourceVersion: "", + }, + } + + service_case1 := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace", + }, + } + service_case2 := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "init", + Namespace: "test-namespace", + }, + } + service_case3 := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace2", + }, + } + service_case4 := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{}, + } + error_case4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) + + t.Run("Create Simple case", reconcileServiceCase(service_init, service_case1, nil)) + t.Run("Create Alredy exist case", reconcileServiceCase(service_init, service_case2, nil)) + t.Run("Create with Another Namespace case", reconcileServiceCase(service_init, service_case3, nil)) + t.Run("Create without Name case", reconcileServiceCase(service_init, service_case4, error_case4)) +} + +func TestCreateOrUpdateSecret(t *testing.T) { + reconcileSecretCase := func(objInit, obj *corev1.Secret, want error) func(t *testing.T) { + return func(t *testing.T) { + req := require.New(t) + + cl := fake.NewClientBuilder().WithObjects(objInit).Build() + + err := CreateOrUpdateSecret(obj, cl) + + req.Equal(err, want) + } + } + + secret_init := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "init", + Namespace: "test-namespace", + }, + } + + secret_case1 := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace", + }, + } + secret_case2 := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "init", + Namespace: "test-namespace", + }, + } + secret_case3 := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace2", + }, + } + secret_case4 := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{}, + } + error_case4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) + + t.Run("Create Simple case", reconcileSecretCase(secret_init, secret_case1, nil)) + t.Run("Create Alredy exist case", reconcileSecretCase(secret_init, secret_case2, nil)) + t.Run("Create with Another Namespace case", reconcileSecretCase(secret_init, secret_case3, nil)) + t.Run("Create without Name case", reconcileSecretCase(secret_init, secret_case4, error_case4)) +} + +func TestCreateOrUpdateDaemonSet(t *testing.T) { + reconcileDaemonSetCase := func(objInit, obj *appsv1.DaemonSet, want error) func(t *testing.T) { + return func(t *testing.T) { + req := require.New(t) + + cl := fake.NewClientBuilder().WithObjects(objInit).Build() + + err := CreateOrUpdateDaemonSet(obj, cl) + + req.Equal(err, want) + } + } + + daemonSet_init := &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "init", + Namespace: "test-namespace", + }, + } + + daemonSet_case1 := &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace", + }, + } + daemonSet_case2 := &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "init", + Namespace: "test-namespace", + }, + } + daemonSet_case3 := &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace2", + }, + } + daemonSet_case4 := &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{}, + } + error_case4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) + + t.Run("Create Simple case", reconcileDaemonSetCase(daemonSet_init, daemonSet_case1, nil)) + t.Run("Create Alredy exist case", reconcileDaemonSetCase(daemonSet_init, daemonSet_case2, nil)) + t.Run("Create with Another Namespace case", reconcileDaemonSetCase(daemonSet_init, daemonSet_case3, nil)) + t.Run("Create without Name case", reconcileDaemonSetCase(daemonSet_init, daemonSet_case4, error_case4)) +} + +func TestCreateOrUpdateStatefulSet(t *testing.T) { + reconcileStatefulSetCase := func(objInit, obj *appsv1.StatefulSet, want error) func(t *testing.T) { + return func(t *testing.T) { + req := require.New(t) + + cl := fake.NewClientBuilder().WithObjects(objInit).Build() + + err := CreateOrUpdateStatefulSet(obj, cl) + + req.Equal(err, want) + } + } + + statefulSet_init := &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "init", + Namespace: "test-namespace", + }, + } + + statefulSet_case1 := &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace", + }, + } + statefulSet_case2 := &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "init", + Namespace: "test-namespace", + }, + } + statefulSet_case3 := &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace2", + }, + } + statefulSet_case4 := &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{}, + } + error_case4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) + + t.Run("Create Simple case", reconcileStatefulSetCase(statefulSet_init, statefulSet_case1, nil)) + t.Run("Create Alredy exist case", reconcileStatefulSetCase(statefulSet_init, statefulSet_case2, nil)) + t.Run("Create with Another Namespace case", reconcileStatefulSetCase(statefulSet_init, statefulSet_case3, nil)) + t.Run("Create without Name case", reconcileStatefulSetCase(statefulSet_init, statefulSet_case4, error_case4)) +} + +func TestCreateOrUpdateServiceAccount(t *testing.T) { + reconcileServiceAccountCase := func(objInit, obj *corev1.ServiceAccount, want error) func(t *testing.T) { + return func(t *testing.T) { + req := require.New(t) + + cl := fake.NewClientBuilder().WithObjects(objInit).Build() + + err := CreateOrUpdateServiceAccount(obj, cl) + + req.Equal(err, want) + } + } + + serviceAccount_init := &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: "init", + Namespace: "test-namespace", + }, + } + + serviceAccount_case1 := &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace", + }, + } + serviceAccount_case2 := &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: "init", + Namespace: "test-namespace", + }, + } + serviceAccount_case3 := &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace2", + }, + } + serviceAccount_case4 := &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{}, + } + error_case4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) + + t.Run("Create Simple case", reconcileServiceAccountCase(serviceAccount_init, serviceAccount_case1, nil)) + t.Run("Create Alredy exist case", reconcileServiceAccountCase(serviceAccount_init, serviceAccount_case2, nil)) + t.Run("Create with Another Namespace case", reconcileServiceAccountCase(serviceAccount_init, serviceAccount_case3, nil)) + t.Run("Create without Name case", reconcileServiceAccountCase(serviceAccount_init, serviceAccount_case4, error_case4)) +} + +func TestCreateOrUpdateClusterRole(t *testing.T) { + reconcileClusterRole := func(objInit, obj *rbacv1.ClusterRole, want error) func(t *testing.T) { + return func(t *testing.T) { + req := require.New(t) + + cl := fake.NewClientBuilder().WithObjects(objInit).Build() + + err := CreateOrUpdateClusterRole(obj, cl) + + req.Equal(err, want) + } + } + + clusterRole_init := &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "init", + Namespace: "test-namespace", + }, + } + + clusterRole_case1 := &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace", + }, + } + clusterRole_case2 := &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "init", + Namespace: "test-namespace", + }, + } + clusterRole_case3 := &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace2", + }, + } + clusterRole_case4 := &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{}, + } + error_case4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) + + t.Run("Create Simple case", reconcileClusterRole(clusterRole_init, clusterRole_case1, nil)) + t.Run("Create Alredy exist case", reconcileClusterRole(clusterRole_init, clusterRole_case2, nil)) + t.Run("Create with Another Namespace case", reconcileClusterRole(clusterRole_init, clusterRole_case3, nil)) + t.Run("Create without Name case", reconcileClusterRole(clusterRole_init, clusterRole_case4, error_case4)) +} + +func TestCreateOrUpdateClusterRoleBinding(t *testing.T) { + reconcileClusterRoleBinding := func(objInit, obj *rbacv1.ClusterRoleBinding, want error) func(t *testing.T) { + return func(t *testing.T) { + req := require.New(t) + + cl := fake.NewClientBuilder().WithObjects(objInit).Build() + + err := CreateOrUpdateClusterRoleBinding(obj, cl) + + req.Equal(err, want) + } + } + + clusterRoleBinding_init := &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "init", + Namespace: "test-namespace", + }, + } + + clusterRoleBinding_case1 := &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace", + }, + } + clusterRoleBinding_case2 := &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "init", + Namespace: "test-namespace", + }, + } + clusterRoleBinding_case3 := &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace2", + }, + } + clusterRoleBinding_case4 := &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{}, + } + error_case4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) + + t.Run("Create Simple case", reconcileClusterRoleBinding(clusterRoleBinding_init, clusterRoleBinding_case1, nil)) + t.Run("Create Alredy exist case", reconcileClusterRoleBinding(clusterRoleBinding_init, clusterRoleBinding_case2, nil)) + t.Run("Create with Another Namespace case", reconcileClusterRoleBinding(clusterRoleBinding_init, clusterRoleBinding_case3, nil)) + t.Run("Create without Name case", reconcileClusterRoleBinding(clusterRoleBinding_init, clusterRoleBinding_case4, error_case4)) +} From 08ca16decaad703e13bdffb042e1a7fb98b8ee09 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Sat, 29 Oct 2022 17:09:24 +0300 Subject: [PATCH 040/316] Add clusterrole and clusterrolebinding to ClientDisableCacheFor Signed-off-by: Zemtsov Vladimir --- main.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/main.go b/main.go index c1cf1585..870b9be9 100644 --- a/main.go +++ b/main.go @@ -22,16 +22,18 @@ import ( // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) // to ensure that exec-entrypoint and run can make use of them. - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" + "k8s.io/client-go/kubernetes" _ "k8s.io/client-go/plugin/pkg/client/auth" - "sigs.k8s.io/controller-runtime/pkg/client" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/log/zap" @@ -84,7 +86,7 @@ func main() { LeaderElection: enableLeaderElection, LeaderElectionID: "79cbe7f3.kaasops.io", ClientDisableCacheFor: []client.Object{&corev1.Secret{}, &corev1.ConfigMap{}, &corev1.Pod{}, &appsv1.Deployment{}, - &appsv1.StatefulSet{}}, + &appsv1.StatefulSet{}, &rbacv1.ClusterRole{}, &rbacv1.ClusterRoleBinding{}}, // LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily // when the Manager ends. This requires the binary to immediately end when the // Manager is stopped, otherwise, this setting is unsafe. Setting this significantly From 6cd12143c8a7604c1141bf46fd2a479e58b97a23 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Sat, 29 Oct 2022 17:09:52 +0300 Subject: [PATCH 041/316] tmp disable clustevectorpipeline Signed-off-by: Zemtsov Vladimir --- .../clustervectorpipeline_controller.go | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/controllers/clustervectorpipeline_controller.go b/controllers/clustervectorpipeline_controller.go index f196f660..f785e859 100644 --- a/controllers/clustervectorpipeline_controller.go +++ b/controllers/clustervectorpipeline_controller.go @@ -19,7 +19,6 @@ package controllers import ( "context" - "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes" ctrl "sigs.k8s.io/controller-runtime" @@ -102,18 +101,18 @@ func (r *ClusterVectorPipelineReconciler) Reconcile(ctx context.Context, req ctr return ctrl.Result{}, nil } -func (r *ClusterVectorPipelineReconciler) findClusterVectorPipelineCustomResourceInstance(ctx context.Context, req ctrl.Request) (*vectorv1alpha1.ClusterVectorPipeline, error) { - // fetch the master instance - cvp := &vectorv1alpha1.ClusterVectorPipeline{} - err := r.Get(ctx, req.NamespacedName, cvp) - if err != nil { - if errors.IsNotFound(err) { - return nil, nil - } - return nil, err - } - return cvp, nil -} +// func (r *ClusterVectorPipelineReconciler) findClusterVectorPipelineCustomResourceInstance(ctx context.Context, req ctrl.Request) (*vectorv1alpha1.ClusterVectorPipeline, error) { +// // fetch the master instance +// cvp := &vectorv1alpha1.ClusterVectorPipeline{} +// err := r.Get(ctx, req.NamespacedName, cvp) +// if err != nil { +// if errors.IsNotFound(err) { +// return nil, nil +// } +// return nil, err +// } +// return cvp, nil +// } // SetupWithManager sets up the controller with the Manager. func (r *ClusterVectorPipelineReconciler) SetupWithManager(mgr ctrl.Manager) error { From feea3810aea50844b802f2c23cf5797cbe0fb0ce Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Sat, 29 Oct 2022 17:10:23 +0300 Subject: [PATCH 042/316] Fix return errors for fillConfigFile Signed-off-by: Zemtsov Vladimir --- controllers/vector_controller.go | 4 +++- controllers/vectorpipeline_controller.go | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/controllers/vector_controller.go b/controllers/vector_controller.go index efab81ab..608f9aaa 100644 --- a/controllers/vector_controller.go +++ b/controllers/vector_controller.go @@ -107,7 +107,9 @@ func (r *VectorReconciler) CreateOrUpdateVector(ctx context.Context, v *vectorv1 if err != nil { return ctrl.Result{}, err } - config.FillForVectorAgent() + if err := config.FillForVectorAgent(); err != nil { + return ctrl.Result{}, err + } // Get Config in Json ([]byte) byteConfig, err := config.GetByteConfig() diff --git a/controllers/vectorpipeline_controller.go b/controllers/vectorpipeline_controller.go index 72791e69..6852e3ff 100644 --- a/controllers/vectorpipeline_controller.go +++ b/controllers/vectorpipeline_controller.go @@ -112,7 +112,9 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque if err != nil { return ctrl.Result{}, err } - config.FillForVectorPipeline(pCtrl) + if err := config.FillForVectorPipeline(pCtrl); err != nil { + return ctrl.Result{}, err + } // Get Config in Json ([]byte) byteConfig, err := config.GetByteConfig() From bef38a16fa931c59939a975de383a1ab14e622f8 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Sat, 29 Oct 2022 17:16:18 +0300 Subject: [PATCH 043/316] Fix testpackage error Signed-off-by: Zemtsov Vladimir --- controllers/factory/utils/hash/hash_test.go | 5 +++-- controllers/factory/utils/k8s/k8s_test.go | 23 +++++++++++---------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/controllers/factory/utils/hash/hash_test.go b/controllers/factory/utils/hash/hash_test.go index 615b676d..19dd69a0 100644 --- a/controllers/factory/utils/hash/hash_test.go +++ b/controllers/factory/utils/hash/hash_test.go @@ -14,11 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -package hash +package hash_test import ( "testing" + "github.com/kaasops/vector-operator/controllers/factory/utils/hash" "github.com/stretchr/testify/require" ) @@ -27,7 +28,7 @@ func TestGet(t *testing.T) { return func(t *testing.T) { req := require.New(t) - result := Get(bytes) + result := hash.Get(bytes) req.Equal(result, want) } } diff --git a/controllers/factory/utils/k8s/k8s_test.go b/controllers/factory/utils/k8s/k8s_test.go index ed31231d..ab45fbbf 100644 --- a/controllers/factory/utils/k8s/k8s_test.go +++ b/controllers/factory/utils/k8s/k8s_test.go @@ -14,7 +14,7 @@ // limitations under the License. // */ -package k8s +package k8s_test import ( "context" @@ -22,6 +22,7 @@ import ( // . "github.com/onsi/ginkgo/v2" // . "github.com/onsi/gomega" + "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" "github.com/stretchr/testify/require" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -45,7 +46,7 @@ func TestCreatePod(t *testing.T) { cl := fake.NewClientBuilder().WithObjects(objInit).Build() - err := CreatePod(obj, cl) + err := k8s.CreatePod(obj, cl) req.Equal(err, want) } } @@ -94,7 +95,7 @@ func TestGetPod(t *testing.T) { cl := fake.NewClientBuilder().WithObjects(objInit).Build() - result, err := GetPod(obj, cl) + result, err := k8s.GetPod(obj, cl) if result != nil { req.Equal(result.ObjectMeta, wantPod.ObjectMeta) } @@ -139,7 +140,7 @@ func TestUpdateStatus(t *testing.T) { cl := fake.NewClientBuilder().WithObjects(objInit).Build() - err := UpdateStatus(context.Background(), obj, cl) + err := k8s.UpdateStatus(context.Background(), obj, cl) req.Equal(err, want) } @@ -226,7 +227,7 @@ func TestCreateOrUpdateService(t *testing.T) { cl := fake.NewClientBuilder().WithObjects(objInit).Build() - err := CreateOrUpdateService(obj, cl) + err := k8s.CreateOrUpdateService(obj, cl) req.Equal(err, want) } @@ -276,7 +277,7 @@ func TestCreateOrUpdateSecret(t *testing.T) { cl := fake.NewClientBuilder().WithObjects(objInit).Build() - err := CreateOrUpdateSecret(obj, cl) + err := k8s.CreateOrUpdateSecret(obj, cl) req.Equal(err, want) } @@ -325,7 +326,7 @@ func TestCreateOrUpdateDaemonSet(t *testing.T) { cl := fake.NewClientBuilder().WithObjects(objInit).Build() - err := CreateOrUpdateDaemonSet(obj, cl) + err := k8s.CreateOrUpdateDaemonSet(obj, cl) req.Equal(err, want) } @@ -374,7 +375,7 @@ func TestCreateOrUpdateStatefulSet(t *testing.T) { cl := fake.NewClientBuilder().WithObjects(objInit).Build() - err := CreateOrUpdateStatefulSet(obj, cl) + err := k8s.CreateOrUpdateStatefulSet(obj, cl) req.Equal(err, want) } @@ -423,7 +424,7 @@ func TestCreateOrUpdateServiceAccount(t *testing.T) { cl := fake.NewClientBuilder().WithObjects(objInit).Build() - err := CreateOrUpdateServiceAccount(obj, cl) + err := k8s.CreateOrUpdateServiceAccount(obj, cl) req.Equal(err, want) } @@ -472,7 +473,7 @@ func TestCreateOrUpdateClusterRole(t *testing.T) { cl := fake.NewClientBuilder().WithObjects(objInit).Build() - err := CreateOrUpdateClusterRole(obj, cl) + err := k8s.CreateOrUpdateClusterRole(obj, cl) req.Equal(err, want) } @@ -521,7 +522,7 @@ func TestCreateOrUpdateClusterRoleBinding(t *testing.T) { cl := fake.NewClientBuilder().WithObjects(objInit).Build() - err := CreateOrUpdateClusterRoleBinding(obj, cl) + err := k8s.CreateOrUpdateClusterRoleBinding(obj, cl) req.Equal(err, want) } From d54f4b02d3ab45be42c6cce7d1246b540333dd29 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Sat, 29 Oct 2022 17:18:15 +0300 Subject: [PATCH 044/316] Fix nosnakecase error Signed-off-by: Zemtsov Vladimir --- controllers/factory/utils/k8s/k8s_test.go | 202 +++++++++++----------- 1 file changed, 101 insertions(+), 101 deletions(-) diff --git a/controllers/factory/utils/k8s/k8s_test.go b/controllers/factory/utils/k8s/k8s_test.go index ab45fbbf..ef3afa20 100644 --- a/controllers/factory/utils/k8s/k8s_test.go +++ b/controllers/factory/utils/k8s/k8s_test.go @@ -51,33 +51,33 @@ func TestCreatePod(t *testing.T) { } } - obj_init := &corev1.Pod{ + objInit := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "init", Namespace: "test-namespace", ResourceVersion: "", }, } - obj_case1 := &corev1.Pod{ + objCase1 := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace", }, } - obj_case2 := &corev1.Pod{ + objCase2 := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "init", Namespace: "test-namespace", }, } - gr_case2 := schema.GroupResource{ + grCase2 := schema.GroupResource{ Group: "", Resource: "pods", } - error_case2 := apierrors.NewAlreadyExists(gr_case2, obj_case2.ObjectMeta.Name) + errorCase2 := apierrors.NewAlreadyExists(grCase2, objCase2.ObjectMeta.Name) - t.Run("Create not exist pod case", createPodCase(obj_init, obj_case1, nil)) - t.Run("Create alredy exist pod case", createPodCase(obj_init, obj_case2, error_case2)) + t.Run("Create not exist pod case", createPodCase(objInit, objCase1, nil)) + t.Run("Create alredy exist pod case", createPodCase(objInit, objCase2, errorCase2)) } // func CreatePod(pod *corev1.Pod, c client.Client) error { @@ -103,34 +103,34 @@ func TestGetPod(t *testing.T) { } } - obj_init := &corev1.Pod{ + objInit := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "init", Namespace: "test-namespace", ResourceVersion: "", }, } - obj_case1 := &corev1.Pod{ + objCase1 := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace", }, } - gvr_case1 := schema.GroupVersionResource{ + gvrCase1 := schema.GroupVersionResource{ Group: "", Version: "v1", Resource: "pods", } - error_case1 := apierrors.NewNotFound(gvr_case1.GroupResource(), obj_case1.ObjectMeta.Name) - obj_case2 := &corev1.Pod{ + errorCase1 := apierrors.NewNotFound(gvrCase1.GroupResource(), objCase1.ObjectMeta.Name) + objCase2 := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "init", Namespace: "test-namespace", }, } - t.Run("Get not exist pod case", getPodCase(obj_init, obj_case1, nil, error_case1)) - t.Run("Get exist pod case", getPodCase(obj_init, obj_case2, obj_init, nil)) + t.Run("Get not exist pod case", getPodCase(objInit, objCase1, nil, errorCase1)) + t.Run("Get exist pod case", getPodCase(objInit, objCase2, objInit, nil)) } func TestUpdateStatus(t *testing.T) { @@ -146,37 +146,37 @@ func TestUpdateStatus(t *testing.T) { } } - obj_init := &corev1.Secret{ + objInit := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "init", Namespace: "test-namespace", ResourceVersion: "", }, } - obj_case1 := &corev1.Service{ + objCase1 := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace", }, } - obj_case2 := &corev1.Service{ + objCase2 := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: "init", Namespace: "test-namespace", }, } - obj_case3 := &corev1.Service{ + objCase3 := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace2", }, } - obj_case4 := &corev1.Service{ + objCase4 := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{}, } - error_case4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) + errorCase4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) - obj_case5 := &appsv1.Deployment{ + objCase5 := &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace", @@ -185,14 +185,14 @@ func TestUpdateStatus(t *testing.T) { Replicas: 10, }, } - gvr_case5 := schema.GroupVersionResource{ + gvrCase5 := schema.GroupVersionResource{ Group: "apps", Version: "v1", Resource: "deployments", } - error_case5 := apierrors.NewNotFound(gvr_case5.GroupResource(), obj_case5.ObjectMeta.Name) + errorCase5 := apierrors.NewNotFound(gvrCase5.GroupResource(), objCase5.ObjectMeta.Name) - init_obj_case6 := &appsv1.Deployment{ + init_objCase6 := &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace", @@ -201,7 +201,7 @@ func TestUpdateStatus(t *testing.T) { Replicas: 5, }, } - obj_case6 := &appsv1.Deployment{ + objCase6 := &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace", @@ -211,12 +211,12 @@ func TestUpdateStatus(t *testing.T) { }, } - t.Run("Update Simple case", updateStatusCase(obj_init, obj_case1, nil)) - t.Run("Update Alredy exist case", updateStatusCase(obj_init, obj_case2, nil)) - t.Run("Update with Another Namespace case", updateStatusCase(obj_init, obj_case3, nil)) - t.Run("Update without Name case", updateStatusCase(obj_init, obj_case4, error_case4)) - t.Run("Update not exist Deployment with wrong init case", updateStatusCase(obj_init, obj_case5, error_case5)) - t.Run("Update Deployment case", updateStatusCase(init_obj_case6, obj_case6, nil)) + t.Run("Update Simple case", updateStatusCase(objInit, objCase1, nil)) + t.Run("Update Alredy exist case", updateStatusCase(objInit, objCase2, nil)) + t.Run("Update with Another Namespace case", updateStatusCase(objInit, objCase3, nil)) + t.Run("Update without Name case", updateStatusCase(objInit, objCase4, errorCase4)) + t.Run("Update not exist Deployment with wrong init case", updateStatusCase(objInit, objCase5, errorCase5)) + t.Run("Update Deployment case", updateStatusCase(init_objCase6, objCase6, nil)) } @@ -233,7 +233,7 @@ func TestCreateOrUpdateService(t *testing.T) { } } - service_init := &corev1.Service{ + serviceInit := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: "init", Namespace: "test-namespace", @@ -241,33 +241,33 @@ func TestCreateOrUpdateService(t *testing.T) { }, } - service_case1 := &corev1.Service{ + serviceCase1 := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace", }, } - service_case2 := &corev1.Service{ + serviceCase2 := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: "init", Namespace: "test-namespace", }, } - service_case3 := &corev1.Service{ + serviceCase3 := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace2", }, } - service_case4 := &corev1.Service{ + serviceCase4 := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{}, } - error_case4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) + errorCase4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) - t.Run("Create Simple case", reconcileServiceCase(service_init, service_case1, nil)) - t.Run("Create Alredy exist case", reconcileServiceCase(service_init, service_case2, nil)) - t.Run("Create with Another Namespace case", reconcileServiceCase(service_init, service_case3, nil)) - t.Run("Create without Name case", reconcileServiceCase(service_init, service_case4, error_case4)) + t.Run("Create Simple case", reconcileServiceCase(serviceInit, serviceCase1, nil)) + t.Run("Create Alredy exist case", reconcileServiceCase(serviceInit, serviceCase2, nil)) + t.Run("Create with Another Namespace case", reconcileServiceCase(serviceInit, serviceCase3, nil)) + t.Run("Create without Name case", reconcileServiceCase(serviceInit, serviceCase4, errorCase4)) } func TestCreateOrUpdateSecret(t *testing.T) { @@ -283,40 +283,40 @@ func TestCreateOrUpdateSecret(t *testing.T) { } } - secret_init := &corev1.Secret{ + secretInit := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "init", Namespace: "test-namespace", }, } - secret_case1 := &corev1.Secret{ + secretCase1 := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace", }, } - secret_case2 := &corev1.Secret{ + secretCase2 := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "init", Namespace: "test-namespace", }, } - secret_case3 := &corev1.Secret{ + secretCase3 := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace2", }, } - secret_case4 := &corev1.Secret{ + secretCase4 := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{}, } - error_case4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) + errorCase4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) - t.Run("Create Simple case", reconcileSecretCase(secret_init, secret_case1, nil)) - t.Run("Create Alredy exist case", reconcileSecretCase(secret_init, secret_case2, nil)) - t.Run("Create with Another Namespace case", reconcileSecretCase(secret_init, secret_case3, nil)) - t.Run("Create without Name case", reconcileSecretCase(secret_init, secret_case4, error_case4)) + t.Run("Create Simple case", reconcileSecretCase(secretInit, secretCase1, nil)) + t.Run("Create Alredy exist case", reconcileSecretCase(secretInit, secretCase2, nil)) + t.Run("Create with Another Namespace case", reconcileSecretCase(secretInit, secretCase3, nil)) + t.Run("Create without Name case", reconcileSecretCase(secretInit, secretCase4, errorCase4)) } func TestCreateOrUpdateDaemonSet(t *testing.T) { @@ -332,40 +332,40 @@ func TestCreateOrUpdateDaemonSet(t *testing.T) { } } - daemonSet_init := &appsv1.DaemonSet{ + daemonSetInit := &appsv1.DaemonSet{ ObjectMeta: metav1.ObjectMeta{ Name: "init", Namespace: "test-namespace", }, } - daemonSet_case1 := &appsv1.DaemonSet{ + daemonSetCase1 := &appsv1.DaemonSet{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace", }, } - daemonSet_case2 := &appsv1.DaemonSet{ + daemonSetCase2 := &appsv1.DaemonSet{ ObjectMeta: metav1.ObjectMeta{ Name: "init", Namespace: "test-namespace", }, } - daemonSet_case3 := &appsv1.DaemonSet{ + daemonSetCase3 := &appsv1.DaemonSet{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace2", }, } - daemonSet_case4 := &appsv1.DaemonSet{ + daemonSetCase4 := &appsv1.DaemonSet{ ObjectMeta: metav1.ObjectMeta{}, } - error_case4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) + errorCase4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) - t.Run("Create Simple case", reconcileDaemonSetCase(daemonSet_init, daemonSet_case1, nil)) - t.Run("Create Alredy exist case", reconcileDaemonSetCase(daemonSet_init, daemonSet_case2, nil)) - t.Run("Create with Another Namespace case", reconcileDaemonSetCase(daemonSet_init, daemonSet_case3, nil)) - t.Run("Create without Name case", reconcileDaemonSetCase(daemonSet_init, daemonSet_case4, error_case4)) + t.Run("Create Simple case", reconcileDaemonSetCase(daemonSetInit, daemonSetCase1, nil)) + t.Run("Create Alredy exist case", reconcileDaemonSetCase(daemonSetInit, daemonSetCase2, nil)) + t.Run("Create with Another Namespace case", reconcileDaemonSetCase(daemonSetInit, daemonSetCase3, nil)) + t.Run("Create without Name case", reconcileDaemonSetCase(daemonSetInit, daemonSetCase4, errorCase4)) } func TestCreateOrUpdateStatefulSet(t *testing.T) { @@ -381,40 +381,40 @@ func TestCreateOrUpdateStatefulSet(t *testing.T) { } } - statefulSet_init := &appsv1.StatefulSet{ + statefulSetInit := &appsv1.StatefulSet{ ObjectMeta: metav1.ObjectMeta{ Name: "init", Namespace: "test-namespace", }, } - statefulSet_case1 := &appsv1.StatefulSet{ + statefulSetCase1 := &appsv1.StatefulSet{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace", }, } - statefulSet_case2 := &appsv1.StatefulSet{ + statefulSetCase2 := &appsv1.StatefulSet{ ObjectMeta: metav1.ObjectMeta{ Name: "init", Namespace: "test-namespace", }, } - statefulSet_case3 := &appsv1.StatefulSet{ + statefulSetCase3 := &appsv1.StatefulSet{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace2", }, } - statefulSet_case4 := &appsv1.StatefulSet{ + statefulSetCase4 := &appsv1.StatefulSet{ ObjectMeta: metav1.ObjectMeta{}, } - error_case4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) + errorCase4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) - t.Run("Create Simple case", reconcileStatefulSetCase(statefulSet_init, statefulSet_case1, nil)) - t.Run("Create Alredy exist case", reconcileStatefulSetCase(statefulSet_init, statefulSet_case2, nil)) - t.Run("Create with Another Namespace case", reconcileStatefulSetCase(statefulSet_init, statefulSet_case3, nil)) - t.Run("Create without Name case", reconcileStatefulSetCase(statefulSet_init, statefulSet_case4, error_case4)) + t.Run("Create Simple case", reconcileStatefulSetCase(statefulSetInit, statefulSetCase1, nil)) + t.Run("Create Alredy exist case", reconcileStatefulSetCase(statefulSetInit, statefulSetCase2, nil)) + t.Run("Create with Another Namespace case", reconcileStatefulSetCase(statefulSetInit, statefulSetCase3, nil)) + t.Run("Create without Name case", reconcileStatefulSetCase(statefulSetInit, statefulSetCase4, errorCase4)) } func TestCreateOrUpdateServiceAccount(t *testing.T) { @@ -430,40 +430,40 @@ func TestCreateOrUpdateServiceAccount(t *testing.T) { } } - serviceAccount_init := &corev1.ServiceAccount{ + serviceAccountInit := &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: "init", Namespace: "test-namespace", }, } - serviceAccount_case1 := &corev1.ServiceAccount{ + serviceAccountCase1 := &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace", }, } - serviceAccount_case2 := &corev1.ServiceAccount{ + serviceAccountCase2 := &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: "init", Namespace: "test-namespace", }, } - serviceAccount_case3 := &corev1.ServiceAccount{ + serviceAccountCase3 := &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace2", }, } - serviceAccount_case4 := &corev1.ServiceAccount{ + serviceAccountCase4 := &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{}, } - error_case4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) + errorCase4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) - t.Run("Create Simple case", reconcileServiceAccountCase(serviceAccount_init, serviceAccount_case1, nil)) - t.Run("Create Alredy exist case", reconcileServiceAccountCase(serviceAccount_init, serviceAccount_case2, nil)) - t.Run("Create with Another Namespace case", reconcileServiceAccountCase(serviceAccount_init, serviceAccount_case3, nil)) - t.Run("Create without Name case", reconcileServiceAccountCase(serviceAccount_init, serviceAccount_case4, error_case4)) + t.Run("Create Simple case", reconcileServiceAccountCase(serviceAccountInit, serviceAccountCase1, nil)) + t.Run("Create Alredy exist case", reconcileServiceAccountCase(serviceAccountInit, serviceAccountCase2, nil)) + t.Run("Create with Another Namespace case", reconcileServiceAccountCase(serviceAccountInit, serviceAccountCase3, nil)) + t.Run("Create without Name case", reconcileServiceAccountCase(serviceAccountInit, serviceAccountCase4, errorCase4)) } func TestCreateOrUpdateClusterRole(t *testing.T) { @@ -479,40 +479,40 @@ func TestCreateOrUpdateClusterRole(t *testing.T) { } } - clusterRole_init := &rbacv1.ClusterRole{ + clusterRoleInit := &rbacv1.ClusterRole{ ObjectMeta: metav1.ObjectMeta{ Name: "init", Namespace: "test-namespace", }, } - clusterRole_case1 := &rbacv1.ClusterRole{ + clusterRoleCase1 := &rbacv1.ClusterRole{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace", }, } - clusterRole_case2 := &rbacv1.ClusterRole{ + clusterRoleCase2 := &rbacv1.ClusterRole{ ObjectMeta: metav1.ObjectMeta{ Name: "init", Namespace: "test-namespace", }, } - clusterRole_case3 := &rbacv1.ClusterRole{ + clusterRoleCase3 := &rbacv1.ClusterRole{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace2", }, } - clusterRole_case4 := &rbacv1.ClusterRole{ + clusterRoleCase4 := &rbacv1.ClusterRole{ ObjectMeta: metav1.ObjectMeta{}, } - error_case4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) + errorCase4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) - t.Run("Create Simple case", reconcileClusterRole(clusterRole_init, clusterRole_case1, nil)) - t.Run("Create Alredy exist case", reconcileClusterRole(clusterRole_init, clusterRole_case2, nil)) - t.Run("Create with Another Namespace case", reconcileClusterRole(clusterRole_init, clusterRole_case3, nil)) - t.Run("Create without Name case", reconcileClusterRole(clusterRole_init, clusterRole_case4, error_case4)) + t.Run("Create Simple case", reconcileClusterRole(clusterRoleInit, clusterRoleCase1, nil)) + t.Run("Create Alredy exist case", reconcileClusterRole(clusterRoleInit, clusterRoleCase2, nil)) + t.Run("Create with Another Namespace case", reconcileClusterRole(clusterRoleInit, clusterRoleCase3, nil)) + t.Run("Create without Name case", reconcileClusterRole(clusterRoleInit, clusterRoleCase4, errorCase4)) } func TestCreateOrUpdateClusterRoleBinding(t *testing.T) { @@ -528,38 +528,38 @@ func TestCreateOrUpdateClusterRoleBinding(t *testing.T) { } } - clusterRoleBinding_init := &rbacv1.ClusterRoleBinding{ + clusterRoleBindingInit := &rbacv1.ClusterRoleBinding{ ObjectMeta: metav1.ObjectMeta{ Name: "init", Namespace: "test-namespace", }, } - clusterRoleBinding_case1 := &rbacv1.ClusterRoleBinding{ + clusterRoleBindingCase1 := &rbacv1.ClusterRoleBinding{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace", }, } - clusterRoleBinding_case2 := &rbacv1.ClusterRoleBinding{ + clusterRoleBindingCase2 := &rbacv1.ClusterRoleBinding{ ObjectMeta: metav1.ObjectMeta{ Name: "init", Namespace: "test-namespace", }, } - clusterRoleBinding_case3 := &rbacv1.ClusterRoleBinding{ + clusterRoleBindingCase3 := &rbacv1.ClusterRoleBinding{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace2", }, } - clusterRoleBinding_case4 := &rbacv1.ClusterRoleBinding{ + clusterRoleBindingCase4 := &rbacv1.ClusterRoleBinding{ ObjectMeta: metav1.ObjectMeta{}, } - error_case4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) + errorCase4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) - t.Run("Create Simple case", reconcileClusterRoleBinding(clusterRoleBinding_init, clusterRoleBinding_case1, nil)) - t.Run("Create Alredy exist case", reconcileClusterRoleBinding(clusterRoleBinding_init, clusterRoleBinding_case2, nil)) - t.Run("Create with Another Namespace case", reconcileClusterRoleBinding(clusterRoleBinding_init, clusterRoleBinding_case3, nil)) - t.Run("Create without Name case", reconcileClusterRoleBinding(clusterRoleBinding_init, clusterRoleBinding_case4, error_case4)) + t.Run("Create Simple case", reconcileClusterRoleBinding(clusterRoleBindingInit, clusterRoleBindingCase1, nil)) + t.Run("Create Alredy exist case", reconcileClusterRoleBinding(clusterRoleBindingInit, clusterRoleBindingCase2, nil)) + t.Run("Create with Another Namespace case", reconcileClusterRoleBinding(clusterRoleBindingInit, clusterRoleBindingCase3, nil)) + t.Run("Create without Name case", reconcileClusterRoleBinding(clusterRoleBindingInit, clusterRoleBindingCase4, errorCase4)) } From 16abb70c6d85e7237d1aa66af13746761fa09c08 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Sat, 29 Oct 2022 17:36:59 +0300 Subject: [PATCH 045/316] Add reconcileObjectCase for k8s-test Signed-off-by: Zemtsov Vladimir --- controllers/factory/utils/k8s/k8s_test.go | 501 ++++++++++------------ 1 file changed, 238 insertions(+), 263 deletions(-) diff --git a/controllers/factory/utils/k8s/k8s_test.go b/controllers/factory/utils/k8s/k8s_test.go index ef3afa20..9ac356ca 100644 --- a/controllers/factory/utils/k8s/k8s_test.go +++ b/controllers/factory/utils/k8s/k8s_test.go @@ -39,527 +39,502 @@ import ( // *corev1.Service | *corev1.Secret | *appsv1.DaemonSet | *appsv1.StatefulSet | *corev1.ServiceAccount | *rbacv1.ClusterRole | *rbacv1.ClusterRoleBinding // } -func TestCreatePod(t *testing.T) { - createPodCase := func(objInit, obj *corev1.Pod, want error) func(t *testing.T) { - return func(t *testing.T) { - req := require.New(t) +var reconcileObjectCase = func(objInit, obj interface{}, want error) func(t *testing.T) { + return func(t *testing.T) { + t.Helper() + req := require.New(t) + + switch obj.(type) { + case *corev1.Service: + serviceInit := objInit.(*corev1.Service) + service := obj.(*corev1.Service) + + cl := fake.NewClientBuilder().WithObjects(serviceInit).Build() + err := k8s.CreateOrUpdateService(service, cl) + req.Equal(err, want) + case *corev1.Secret: + secretInit := objInit.(*corev1.Secret) + secret := obj.(*corev1.Secret) - cl := fake.NewClientBuilder().WithObjects(objInit).Build() + cl := fake.NewClientBuilder().WithObjects(secretInit).Build() + err := k8s.CreateOrUpdateSecret(secret, cl) + req.Equal(err, want) + case *appsv1.DaemonSet: + daemonSetInit := objInit.(*appsv1.DaemonSet) + daemonSet := obj.(*appsv1.DaemonSet) - err := k8s.CreatePod(obj, cl) + cl := fake.NewClientBuilder().WithObjects(daemonSetInit).Build() + err := k8s.CreateOrUpdateDaemonSet(daemonSet, cl) + req.Equal(err, want) + case *appsv1.StatefulSet: + statefulSetInit := objInit.(*appsv1.StatefulSet) + statefulSet := obj.(*appsv1.StatefulSet) + + cl := fake.NewClientBuilder().WithObjects(statefulSetInit).Build() + err := k8s.CreateOrUpdateStatefulSet(statefulSet, cl) + req.Equal(err, want) + case *corev1.ServiceAccount: + serviceAccountInit := objInit.(*corev1.ServiceAccount) + serviceAccount := obj.(*corev1.ServiceAccount) + + cl := fake.NewClientBuilder().WithObjects(serviceAccountInit).Build() + err := k8s.CreateOrUpdateServiceAccount(serviceAccount, cl) + req.Equal(err, want) + case *rbacv1.ClusterRole: + clusterRoleInit := objInit.(*rbacv1.ClusterRole) + clusterRole := obj.(*rbacv1.ClusterRole) + + cl := fake.NewClientBuilder().WithObjects(clusterRoleInit).Build() + err := k8s.CreateOrUpdateClusterRole(clusterRole, cl) + req.Equal(err, want) + case *rbacv1.ClusterRoleBinding: + clusterRoleBindingInit := objInit.(*rbacv1.ClusterRoleBinding) + clusterRoleBinding := obj.(*rbacv1.ClusterRoleBinding) + + cl := fake.NewClientBuilder().WithObjects(clusterRoleBindingInit).Build() + err := k8s.CreateOrUpdateClusterRoleBinding(clusterRoleBinding, cl) req.Equal(err, want) } } +} - objInit := &corev1.Pod{ +func TestCreateOrUpdateService(t *testing.T) { + serviceInit := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: "init", Namespace: "test-namespace", ResourceVersion: "", }, } - objCase1 := &corev1.Pod{ + + serviceCase1 := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace", }, } - objCase2 := &corev1.Pod{ + serviceCase2 := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: "init", Namespace: "test-namespace", }, } - grCase2 := schema.GroupResource{ - Group: "", - Resource: "pods", + serviceCase3 := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace2", + }, } - errorCase2 := apierrors.NewAlreadyExists(grCase2, objCase2.ObjectMeta.Name) + serviceCase4 := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{}, + } + errorCase4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) - t.Run("Create not exist pod case", createPodCase(objInit, objCase1, nil)) - t.Run("Create alredy exist pod case", createPodCase(objInit, objCase2, errorCase2)) + t.Run("Create Simple case", reconcileObjectCase(serviceInit, serviceCase1, nil)) + t.Run("Create Alredy exist case", reconcileObjectCase(serviceInit, serviceCase2, nil)) + t.Run("Create with Another Namespace case", reconcileObjectCase(serviceInit, serviceCase3, nil)) + t.Run("Create without Name case", reconcileObjectCase(serviceInit, serviceCase4, errorCase4)) } -// func CreatePod(pod *corev1.Pod, c client.Client) error { -// err := c.Create(context.TODO(), pod) -// if err != nil { -// return err -// } -// return nil -// } - -func TestGetPod(t *testing.T) { - getPodCase := func(objInit, obj, wantPod *corev1.Pod, want error) func(t *testing.T) { - return func(t *testing.T) { - req := require.New(t) - - cl := fake.NewClientBuilder().WithObjects(objInit).Build() - - result, err := k8s.GetPod(obj, cl) - if result != nil { - req.Equal(result.ObjectMeta, wantPod.ObjectMeta) - } - req.Equal(err, want) - } - } - - objInit := &corev1.Pod{ +func TestCreateOrUpdateSecret(t *testing.T) { + secretInit := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: "init", - Namespace: "test-namespace", - ResourceVersion: "", + Name: "init", + Namespace: "test-namespace", }, } - objCase1 := &corev1.Pod{ + + secretCase1 := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace", }, } - gvrCase1 := schema.GroupVersionResource{ - Group: "", - Version: "v1", - Resource: "pods", - } - errorCase1 := apierrors.NewNotFound(gvrCase1.GroupResource(), objCase1.ObjectMeta.Name) - objCase2 := &corev1.Pod{ + secretCase2 := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "init", Namespace: "test-namespace", }, } + secretCase3 := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace2", + }, + } + secretCase4 := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{}, + } + errorCase4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) - t.Run("Get not exist pod case", getPodCase(objInit, objCase1, nil, errorCase1)) - t.Run("Get exist pod case", getPodCase(objInit, objCase2, objInit, nil)) + t.Run("Create Simple case", reconcileObjectCase(secretInit, secretCase1, nil)) + t.Run("Create Alredy exist case", reconcileObjectCase(secretInit, secretCase2, nil)) + t.Run("Create with Another Namespace case", reconcileObjectCase(secretInit, secretCase3, nil)) + t.Run("Create without Name case", reconcileObjectCase(secretInit, secretCase4, errorCase4)) } -func TestUpdateStatus(t *testing.T) { - updateStatusCase := func(objInit, obj client.Object, want error) func(t *testing.T) { - return func(t *testing.T) { - req := require.New(t) - - cl := fake.NewClientBuilder().WithObjects(objInit).Build() - - err := k8s.UpdateStatus(context.Background(), obj, cl) - - req.Equal(err, want) - } - } - - objInit := &corev1.Secret{ +func TestCreateOrUpdateDaemonSet(t *testing.T) { + daemonSetInit := &appsv1.DaemonSet{ ObjectMeta: metav1.ObjectMeta{ - Name: "init", - Namespace: "test-namespace", - ResourceVersion: "", + Name: "init", + Namespace: "test-namespace", }, } - objCase1 := &corev1.Service{ + + daemonSetCase1 := &appsv1.DaemonSet{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace", }, } - objCase2 := &corev1.Service{ + daemonSetCase2 := &appsv1.DaemonSet{ ObjectMeta: metav1.ObjectMeta{ Name: "init", Namespace: "test-namespace", }, } - objCase3 := &corev1.Service{ + daemonSetCase3 := &appsv1.DaemonSet{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace2", }, } - objCase4 := &corev1.Service{ + daemonSetCase4 := &appsv1.DaemonSet{ ObjectMeta: metav1.ObjectMeta{}, } errorCase4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) - objCase5 := &appsv1.Deployment{ + t.Run("Create Simple case", reconcileObjectCase(daemonSetInit, daemonSetCase1, nil)) + t.Run("Create Alredy exist case", reconcileObjectCase(daemonSetInit, daemonSetCase2, nil)) + t.Run("Create with Another Namespace case", reconcileObjectCase(daemonSetInit, daemonSetCase3, nil)) + t.Run("Create without Name case", reconcileObjectCase(daemonSetInit, daemonSetCase4, errorCase4)) +} + +func TestCreateOrUpdateStatefulSet(t *testing.T) { + statefulSetInit := &appsv1.StatefulSet{ ObjectMeta: metav1.ObjectMeta{ - Name: "test", + Name: "init", Namespace: "test-namespace", }, - Status: appsv1.DeploymentStatus{ - Replicas: 10, - }, - } - gvrCase5 := schema.GroupVersionResource{ - Group: "apps", - Version: "v1", - Resource: "deployments", } - errorCase5 := apierrors.NewNotFound(gvrCase5.GroupResource(), objCase5.ObjectMeta.Name) - init_objCase6 := &appsv1.Deployment{ + statefulSetCase1 := &appsv1.StatefulSet{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace", }, - Status: appsv1.DeploymentStatus{ - Replicas: 5, - }, } - objCase6 := &appsv1.Deployment{ + statefulSetCase2 := &appsv1.StatefulSet{ ObjectMeta: metav1.ObjectMeta{ - Name: "test", + Name: "init", Namespace: "test-namespace", }, - Status: appsv1.DeploymentStatus{ - Replicas: 10, + } + statefulSetCase3 := &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace2", }, } + statefulSetCase4 := &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{}, + } + errorCase4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) - t.Run("Update Simple case", updateStatusCase(objInit, objCase1, nil)) - t.Run("Update Alredy exist case", updateStatusCase(objInit, objCase2, nil)) - t.Run("Update with Another Namespace case", updateStatusCase(objInit, objCase3, nil)) - t.Run("Update without Name case", updateStatusCase(objInit, objCase4, errorCase4)) - t.Run("Update not exist Deployment with wrong init case", updateStatusCase(objInit, objCase5, errorCase5)) - t.Run("Update Deployment case", updateStatusCase(init_objCase6, objCase6, nil)) - + t.Run("Create Simple case", reconcileObjectCase(statefulSetInit, statefulSetCase1, nil)) + t.Run("Create Alredy exist case", reconcileObjectCase(statefulSetInit, statefulSetCase2, nil)) + t.Run("Create with Another Namespace case", reconcileObjectCase(statefulSetInit, statefulSetCase3, nil)) + t.Run("Create without Name case", reconcileObjectCase(statefulSetInit, statefulSetCase4, errorCase4)) } -func TestCreateOrUpdateService(t *testing.T) { - reconcileServiceCase := func(objInit, obj *corev1.Service, want error) func(t *testing.T) { - return func(t *testing.T) { - req := require.New(t) - - cl := fake.NewClientBuilder().WithObjects(objInit).Build() - - err := k8s.CreateOrUpdateService(obj, cl) - - req.Equal(err, want) - } - } - - serviceInit := &corev1.Service{ +func TestCreateOrUpdateServiceAccount(t *testing.T) { + serviceAccountInit := &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ - Name: "init", - Namespace: "test-namespace", - ResourceVersion: "", + Name: "init", + Namespace: "test-namespace", }, } - serviceCase1 := &corev1.Service{ + serviceAccountCase1 := &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace", }, } - serviceCase2 := &corev1.Service{ + serviceAccountCase2 := &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: "init", Namespace: "test-namespace", }, } - serviceCase3 := &corev1.Service{ + serviceAccountCase3 := &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace2", }, } - serviceCase4 := &corev1.Service{ + serviceAccountCase4 := &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{}, } errorCase4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) - t.Run("Create Simple case", reconcileServiceCase(serviceInit, serviceCase1, nil)) - t.Run("Create Alredy exist case", reconcileServiceCase(serviceInit, serviceCase2, nil)) - t.Run("Create with Another Namespace case", reconcileServiceCase(serviceInit, serviceCase3, nil)) - t.Run("Create without Name case", reconcileServiceCase(serviceInit, serviceCase4, errorCase4)) + t.Run("Create Simple case", reconcileObjectCase(serviceAccountInit, serviceAccountCase1, nil)) + t.Run("Create Alredy exist case", reconcileObjectCase(serviceAccountInit, serviceAccountCase2, nil)) + t.Run("Create with Another Namespace case", reconcileObjectCase(serviceAccountInit, serviceAccountCase3, nil)) + t.Run("Create without Name case", reconcileObjectCase(serviceAccountInit, serviceAccountCase4, errorCase4)) } -func TestCreateOrUpdateSecret(t *testing.T) { - reconcileSecretCase := func(objInit, obj *corev1.Secret, want error) func(t *testing.T) { - return func(t *testing.T) { - req := require.New(t) - - cl := fake.NewClientBuilder().WithObjects(objInit).Build() - - err := k8s.CreateOrUpdateSecret(obj, cl) - - req.Equal(err, want) - } - } - - secretInit := &corev1.Secret{ +func TestCreateOrUpdateClusterRole(t *testing.T) { + clusterRoleInit := &rbacv1.ClusterRole{ ObjectMeta: metav1.ObjectMeta{ Name: "init", Namespace: "test-namespace", }, } - secretCase1 := &corev1.Secret{ + clusterRoleCase1 := &rbacv1.ClusterRole{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace", }, } - secretCase2 := &corev1.Secret{ + clusterRoleCase2 := &rbacv1.ClusterRole{ ObjectMeta: metav1.ObjectMeta{ Name: "init", Namespace: "test-namespace", }, } - secretCase3 := &corev1.Secret{ + clusterRoleCase3 := &rbacv1.ClusterRole{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace2", }, } - secretCase4 := &corev1.Secret{ + clusterRoleCase4 := &rbacv1.ClusterRole{ ObjectMeta: metav1.ObjectMeta{}, } errorCase4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) - t.Run("Create Simple case", reconcileSecretCase(secretInit, secretCase1, nil)) - t.Run("Create Alredy exist case", reconcileSecretCase(secretInit, secretCase2, nil)) - t.Run("Create with Another Namespace case", reconcileSecretCase(secretInit, secretCase3, nil)) - t.Run("Create without Name case", reconcileSecretCase(secretInit, secretCase4, errorCase4)) + t.Run("Create Simple case", reconcileObjectCase(clusterRoleInit, clusterRoleCase1, nil)) + t.Run("Create Alredy exist case", reconcileObjectCase(clusterRoleInit, clusterRoleCase2, nil)) + t.Run("Create with Another Namespace case", reconcileObjectCase(clusterRoleInit, clusterRoleCase3, nil)) + t.Run("Create without Name case", reconcileObjectCase(clusterRoleInit, clusterRoleCase4, errorCase4)) } -func TestCreateOrUpdateDaemonSet(t *testing.T) { - reconcileDaemonSetCase := func(objInit, obj *appsv1.DaemonSet, want error) func(t *testing.T) { - return func(t *testing.T) { - req := require.New(t) - - cl := fake.NewClientBuilder().WithObjects(objInit).Build() - - err := k8s.CreateOrUpdateDaemonSet(obj, cl) - - req.Equal(err, want) - } - } - - daemonSetInit := &appsv1.DaemonSet{ +func TestCreateOrUpdateClusterRoleBinding(t *testing.T) { + clusterRoleBindingInit := &rbacv1.ClusterRoleBinding{ ObjectMeta: metav1.ObjectMeta{ Name: "init", Namespace: "test-namespace", }, } - daemonSetCase1 := &appsv1.DaemonSet{ + clusterRoleBindingCase1 := &rbacv1.ClusterRoleBinding{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace", }, } - daemonSetCase2 := &appsv1.DaemonSet{ + clusterRoleBindingCase2 := &rbacv1.ClusterRoleBinding{ ObjectMeta: metav1.ObjectMeta{ Name: "init", Namespace: "test-namespace", }, } - daemonSetCase3 := &appsv1.DaemonSet{ + clusterRoleBindingCase3 := &rbacv1.ClusterRoleBinding{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace2", }, } - daemonSetCase4 := &appsv1.DaemonSet{ + clusterRoleBindingCase4 := &rbacv1.ClusterRoleBinding{ ObjectMeta: metav1.ObjectMeta{}, } errorCase4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) - t.Run("Create Simple case", reconcileDaemonSetCase(daemonSetInit, daemonSetCase1, nil)) - t.Run("Create Alredy exist case", reconcileDaemonSetCase(daemonSetInit, daemonSetCase2, nil)) - t.Run("Create with Another Namespace case", reconcileDaemonSetCase(daemonSetInit, daemonSetCase3, nil)) - t.Run("Create without Name case", reconcileDaemonSetCase(daemonSetInit, daemonSetCase4, errorCase4)) + t.Run("Create Simple case", reconcileObjectCase(clusterRoleBindingInit, clusterRoleBindingCase1, nil)) + t.Run("Create Alredy exist case", reconcileObjectCase(clusterRoleBindingInit, clusterRoleBindingCase2, nil)) + t.Run("Create with Another Namespace case", reconcileObjectCase(clusterRoleBindingInit, clusterRoleBindingCase3, nil)) + t.Run("Create without Name case", reconcileObjectCase(clusterRoleBindingInit, clusterRoleBindingCase4, errorCase4)) } -func TestCreateOrUpdateStatefulSet(t *testing.T) { - reconcileStatefulSetCase := func(objInit, obj *appsv1.StatefulSet, want error) func(t *testing.T) { +func TestCreatePod(t *testing.T) { + createPodCase := func(objInit, obj *corev1.Pod, want error) func(t *testing.T) { return func(t *testing.T) { req := require.New(t) cl := fake.NewClientBuilder().WithObjects(objInit).Build() - err := k8s.CreateOrUpdateStatefulSet(obj, cl) - + err := k8s.CreatePod(obj, cl) req.Equal(err, want) } } - statefulSetInit := &appsv1.StatefulSet{ + objInit := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ - Name: "init", - Namespace: "test-namespace", + Name: "init", + Namespace: "test-namespace", + ResourceVersion: "", }, } - - statefulSetCase1 := &appsv1.StatefulSet{ + objCase1 := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace", }, } - statefulSetCase2 := &appsv1.StatefulSet{ + objCase2 := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "init", Namespace: "test-namespace", }, } - statefulSetCase3 := &appsv1.StatefulSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "test-namespace2", - }, - } - statefulSetCase4 := &appsv1.StatefulSet{ - ObjectMeta: metav1.ObjectMeta{}, + grCase2 := schema.GroupResource{ + Group: "", + Resource: "pods", } - errorCase4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) + errorCase2 := apierrors.NewAlreadyExists(grCase2, objCase2.ObjectMeta.Name) - t.Run("Create Simple case", reconcileStatefulSetCase(statefulSetInit, statefulSetCase1, nil)) - t.Run("Create Alredy exist case", reconcileStatefulSetCase(statefulSetInit, statefulSetCase2, nil)) - t.Run("Create with Another Namespace case", reconcileStatefulSetCase(statefulSetInit, statefulSetCase3, nil)) - t.Run("Create without Name case", reconcileStatefulSetCase(statefulSetInit, statefulSetCase4, errorCase4)) + t.Run("Create not exist pod case", createPodCase(objInit, objCase1, nil)) + t.Run("Create alredy exist pod case", createPodCase(objInit, objCase2, errorCase2)) } -func TestCreateOrUpdateServiceAccount(t *testing.T) { - reconcileServiceAccountCase := func(objInit, obj *corev1.ServiceAccount, want error) func(t *testing.T) { +// func CreatePod(pod *corev1.Pod, c client.Client) error { +// err := c.Create(context.TODO(), pod) +// if err != nil { +// return err +// } +// return nil +// } + +func TestGetPod(t *testing.T) { + getPodCase := func(objInit, obj, wantPod *corev1.Pod, want error) func(t *testing.T) { return func(t *testing.T) { req := require.New(t) cl := fake.NewClientBuilder().WithObjects(objInit).Build() - err := k8s.CreateOrUpdateServiceAccount(obj, cl) - + result, err := k8s.GetPod(obj, cl) + if result != nil { + req.Equal(result.ObjectMeta, wantPod.ObjectMeta) + } req.Equal(err, want) } } - serviceAccountInit := &corev1.ServiceAccount{ + objInit := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ - Name: "init", - Namespace: "test-namespace", + Name: "init", + Namespace: "test-namespace", + ResourceVersion: "", }, } - - serviceAccountCase1 := &corev1.ServiceAccount{ + objCase1 := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace", }, } - serviceAccountCase2 := &corev1.ServiceAccount{ + gvrCase1 := schema.GroupVersionResource{ + Group: "", + Version: "v1", + Resource: "pods", + } + errorCase1 := apierrors.NewNotFound(gvrCase1.GroupResource(), objCase1.ObjectMeta.Name) + objCase2 := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "init", Namespace: "test-namespace", }, } - serviceAccountCase3 := &corev1.ServiceAccount{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "test-namespace2", - }, - } - serviceAccountCase4 := &corev1.ServiceAccount{ - ObjectMeta: metav1.ObjectMeta{}, - } - errorCase4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) - t.Run("Create Simple case", reconcileServiceAccountCase(serviceAccountInit, serviceAccountCase1, nil)) - t.Run("Create Alredy exist case", reconcileServiceAccountCase(serviceAccountInit, serviceAccountCase2, nil)) - t.Run("Create with Another Namespace case", reconcileServiceAccountCase(serviceAccountInit, serviceAccountCase3, nil)) - t.Run("Create without Name case", reconcileServiceAccountCase(serviceAccountInit, serviceAccountCase4, errorCase4)) + t.Run("Get not exist pod case", getPodCase(objInit, objCase1, nil, errorCase1)) + t.Run("Get exist pod case", getPodCase(objInit, objCase2, objInit, nil)) } -func TestCreateOrUpdateClusterRole(t *testing.T) { - reconcileClusterRole := func(objInit, obj *rbacv1.ClusterRole, want error) func(t *testing.T) { +func TestUpdateStatus(t *testing.T) { + updateStatusCase := func(objInit, obj client.Object, want error) func(t *testing.T) { return func(t *testing.T) { req := require.New(t) cl := fake.NewClientBuilder().WithObjects(objInit).Build() - err := k8s.CreateOrUpdateClusterRole(obj, cl) + err := k8s.UpdateStatus(context.Background(), obj, cl) req.Equal(err, want) } } - clusterRoleInit := &rbacv1.ClusterRole{ + objInit := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: "init", - Namespace: "test-namespace", + Name: "init", + Namespace: "test-namespace", + ResourceVersion: "", }, } - - clusterRoleCase1 := &rbacv1.ClusterRole{ + objCase1 := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace", }, } - clusterRoleCase2 := &rbacv1.ClusterRole{ + objCase2 := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: "init", Namespace: "test-namespace", }, } - clusterRoleCase3 := &rbacv1.ClusterRole{ + objCase3 := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace2", }, } - clusterRoleCase4 := &rbacv1.ClusterRole{ + objCase4 := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{}, } errorCase4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) - t.Run("Create Simple case", reconcileClusterRole(clusterRoleInit, clusterRoleCase1, nil)) - t.Run("Create Alredy exist case", reconcileClusterRole(clusterRoleInit, clusterRoleCase2, nil)) - t.Run("Create with Another Namespace case", reconcileClusterRole(clusterRoleInit, clusterRoleCase3, nil)) - t.Run("Create without Name case", reconcileClusterRole(clusterRoleInit, clusterRoleCase4, errorCase4)) -} - -func TestCreateOrUpdateClusterRoleBinding(t *testing.T) { - reconcileClusterRoleBinding := func(objInit, obj *rbacv1.ClusterRoleBinding, want error) func(t *testing.T) { - return func(t *testing.T) { - req := require.New(t) - - cl := fake.NewClientBuilder().WithObjects(objInit).Build() - - err := k8s.CreateOrUpdateClusterRoleBinding(obj, cl) - - req.Equal(err, want) - } - } - - clusterRoleBindingInit := &rbacv1.ClusterRoleBinding{ + objCase5 := &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ - Name: "init", + Name: "test", Namespace: "test-namespace", }, + Status: appsv1.DeploymentStatus{ + Replicas: 10, + }, + } + gvrCase5 := schema.GroupVersionResource{ + Group: "apps", + Version: "v1", + Resource: "deployments", } + errorCase5 := apierrors.NewNotFound(gvrCase5.GroupResource(), objCase5.ObjectMeta.Name) - clusterRoleBindingCase1 := &rbacv1.ClusterRoleBinding{ + init_objCase6 := &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace", }, - } - clusterRoleBindingCase2 := &rbacv1.ClusterRoleBinding{ - ObjectMeta: metav1.ObjectMeta{ - Name: "init", - Namespace: "test-namespace", + Status: appsv1.DeploymentStatus{ + Replicas: 5, }, } - clusterRoleBindingCase3 := &rbacv1.ClusterRoleBinding{ + objCase6 := &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: "test", - Namespace: "test-namespace2", + Namespace: "test-namespace", + }, + Status: appsv1.DeploymentStatus{ + Replicas: 10, }, } - clusterRoleBindingCase4 := &rbacv1.ClusterRoleBinding{ - ObjectMeta: metav1.ObjectMeta{}, - } - errorCase4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) - t.Run("Create Simple case", reconcileClusterRoleBinding(clusterRoleBindingInit, clusterRoleBindingCase1, nil)) - t.Run("Create Alredy exist case", reconcileClusterRoleBinding(clusterRoleBindingInit, clusterRoleBindingCase2, nil)) - t.Run("Create with Another Namespace case", reconcileClusterRoleBinding(clusterRoleBindingInit, clusterRoleBindingCase3, nil)) - t.Run("Create without Name case", reconcileClusterRoleBinding(clusterRoleBindingInit, clusterRoleBindingCase4, errorCase4)) + t.Run("Update Simple case", updateStatusCase(objInit, objCase1, nil)) + t.Run("Update Alredy exist case", updateStatusCase(objInit, objCase2, nil)) + t.Run("Update with Another Namespace case", updateStatusCase(objInit, objCase3, nil)) + t.Run("Update without Name case", updateStatusCase(objInit, objCase4, errorCase4)) + t.Run("Update not exist Deployment with wrong init case", updateStatusCase(objInit, objCase5, errorCase5)) + t.Run("Update Deployment case", updateStatusCase(init_objCase6, objCase6, nil)) + } From 19c557262848389773147b917b755621cc12f2c9 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Sat, 29 Oct 2022 17:38:55 +0300 Subject: [PATCH 046/316] Add t.Helper() Signed-off-by: Zemtsov Vladimir --- controllers/factory/utils/hash/hash_test.go | 1 + controllers/factory/utils/k8s/k8s_test.go | 3 +++ 2 files changed, 4 insertions(+) diff --git a/controllers/factory/utils/hash/hash_test.go b/controllers/factory/utils/hash/hash_test.go index 19dd69a0..950a06f9 100644 --- a/controllers/factory/utils/hash/hash_test.go +++ b/controllers/factory/utils/hash/hash_test.go @@ -26,6 +26,7 @@ import ( func TestGet(t *testing.T) { hashCase := func(bytes []byte, want uint32) func(t *testing.T) { return func(t *testing.T) { + t.Helper() req := require.New(t) result := hash.Get(bytes) diff --git a/controllers/factory/utils/k8s/k8s_test.go b/controllers/factory/utils/k8s/k8s_test.go index 9ac356ca..1683aa38 100644 --- a/controllers/factory/utils/k8s/k8s_test.go +++ b/controllers/factory/utils/k8s/k8s_test.go @@ -361,6 +361,7 @@ func TestCreateOrUpdateClusterRoleBinding(t *testing.T) { func TestCreatePod(t *testing.T) { createPodCase := func(objInit, obj *corev1.Pod, want error) func(t *testing.T) { return func(t *testing.T) { + t.Helper() req := require.New(t) cl := fake.NewClientBuilder().WithObjects(objInit).Build() @@ -410,6 +411,7 @@ func TestCreatePod(t *testing.T) { func TestGetPod(t *testing.T) { getPodCase := func(objInit, obj, wantPod *corev1.Pod, want error) func(t *testing.T) { return func(t *testing.T) { + t.Helper() req := require.New(t) cl := fake.NewClientBuilder().WithObjects(objInit).Build() @@ -455,6 +457,7 @@ func TestGetPod(t *testing.T) { func TestUpdateStatus(t *testing.T) { updateStatusCase := func(objInit, obj client.Object, want error) func(t *testing.T) { return func(t *testing.T) { + t.Helper() req := require.New(t) cl := fake.NewClientBuilder().WithObjects(objInit).Build() From 973021741316b11219ba240afb1fbb44932d4cee Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Mon, 31 Oct 2022 08:30:25 +0300 Subject: [PATCH 047/316] Fix varnamelen Signed-off-by: Zemtsov Vladimir --- controllers/vector_controller.go | 16 ++++++++-------- controllers/vectorpipeline_controller.go | 18 +++++++++--------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/controllers/vector_controller.go b/controllers/vector_controller.go index 608f9aaa..18699f0a 100644 --- a/controllers/vector_controller.go +++ b/controllers/vector_controller.go @@ -60,27 +60,27 @@ func (r *VectorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr log.Info("start Reconcile Vector") - v, err := r.findVectorCustomResourceInstance(ctx, req) + vectorCR, err := r.findVectorCustomResourceInstance(ctx, req) if err != nil { log.Error(err, "Failed to get Vector") return ctrl.Result{}, err } - if v == nil { + if vectorCR == nil { log.Info("Vector CR not found. Ignoring since object must be deleted") return ctrl.Result{}, nil } - if v.Spec.Agent.DataDir == "" { - v.Spec.Agent.DataDir = "/vector-data-dir" + if vectorCR.Spec.Agent.DataDir == "" { + vectorCR.Spec.Agent.DataDir = "/vector-data-dir" } - return r.CreateOrUpdateVector(ctx, v) + return r.CreateOrUpdateVector(ctx, vectorCR) } func (r *VectorReconciler) findVectorCustomResourceInstance(ctx context.Context, req ctrl.Request) (*vectorv1alpha1.Vector, error) { // fetch the master instance - v := &vectorv1alpha1.Vector{} - err := r.Get(ctx, req.NamespacedName, v) + vectorCR := &vectorv1alpha1.Vector{} + err := r.Get(ctx, req.NamespacedName, vectorCR) if err != nil { if errors.IsNotFound(err) { return nil, nil @@ -88,7 +88,7 @@ func (r *VectorReconciler) findVectorCustomResourceInstance(ctx context.Context, return nil, err } - return v, nil + return vectorCR, nil } // SetupWithManager sets up the controller with the Manager. diff --git a/controllers/vectorpipeline_controller.go b/controllers/vectorpipeline_controller.go index 6852e3ff..c4db42bb 100644 --- a/controllers/vectorpipeline_controller.go +++ b/controllers/vectorpipeline_controller.go @@ -62,18 +62,18 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque log.Info("start Reconcile VectorPipeline") // Get CR VectorPipeline - vp, err := r.findVectorPipelineCustomResourceInstance(ctx, req) + vectorPipelineCR, err := r.findVectorPipelineCustomResourceInstance(ctx, req) if err != nil { log.Error(err, "Failed to get Vector Pipeline") return ctrl.Result{}, err } - if vp == nil { + if vectorPipelineCR == nil { log.Info("VectorPIpeline CR not found. Ignoring since object must be deleted") return ctrl.Result{}, nil } // Generate VectorPipeline Controller - vpCtrl := vectorpipeline.NewController(vp) + vpCtrl := vectorpipeline.NewController(vectorPipelineCR) // Generate Pipeline Controller pCtrl := pipeline.NewController(ctx, r.Client, vpCtrl) @@ -99,13 +99,13 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque return ctrl.Result{}, nil } - for _, v := range vectorInstances.Items { - if v.DeletionTimestamp != nil { + for _, vector := range vectorInstances.Items { + if vector.DeletionTimestamp != nil { continue } // Init Controller for Vector Agent - vaCtrl := vectoragent.NewController(&v, r.Client, r.Clientset) + vaCtrl := vectoragent.NewController(&vector, r.Client, r.Clientset) // Get Vector Config file config, err := config.New(ctx, vaCtrl) @@ -156,15 +156,15 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque func (r *VectorPipelineReconciler) findVectorPipelineCustomResourceInstance(ctx context.Context, req ctrl.Request) (*vectorv1alpha1.VectorPipeline, error) { // fetch the master instance - vp := &vectorv1alpha1.VectorPipeline{} - err := r.Get(ctx, req.NamespacedName, vp) + vectorPipelineCR := &vectorv1alpha1.VectorPipeline{} + err := r.Get(ctx, req.NamespacedName, vectorPipelineCR) if err != nil { if errors.IsNotFound(err) { return nil, nil } return nil, err } - return vp, nil + return vectorPipelineCR, nil } // SetupWithManager sets up the controller with the Manager. From 7e5a69f158e5577a9ee8621ead6ebe87e18fb6c1 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Mon, 31 Oct 2022 10:59:59 +0300 Subject: [PATCH 048/316] refactor config struct (#23) --- controllers/factory/config/config.go | 45 ++++++++++++++++++- controllers/factory/config/config_build.go | 52 +++++++++------------- controllers/factory/vector/types.go | 10 ++--- controllers/factory/vector/vector.go | 4 +- controllers/vector_controller.go | 4 +- 5 files changed, 76 insertions(+), 39 deletions(-) diff --git a/controllers/factory/config/config.go b/controllers/factory/config/config.go index 8abb097d..da4f8159 100644 --- a/controllers/factory/config/config.go +++ b/controllers/factory/config/config.go @@ -23,6 +23,7 @@ import ( "github.com/kaasops/vector-operator/controllers/factory/pipeline" "github.com/kaasops/vector-operator/controllers/factory/vector" "github.com/kaasops/vector-operator/controllers/factory/vector/vectoragent" + "github.com/mitchellh/mapstructure" ) type Config struct { @@ -77,10 +78,52 @@ func (cfg *Config) FillForVectorPipeline(vCtrl *pipeline.Controller) error { } func (cfg *Config) GetByteConfig() ([]byte, error) { - data, err := json.Marshal(cfg.VectorConfig) + cfgMap, err := CfgToMap(*cfg.VectorConfig) + if err != nil { + return nil, err + } + data, err := json.Marshal(cfgMap) if err != nil { return nil, err } return data, nil } + +func CfgToMap(cfg vector.VectorConfig) (cfgMap map[string]interface{}, err error) { + sources := make(map[string]interface{}) + transforms := make(map[string]interface{}) + sinks := make(map[string]interface{}) + for _, source := range cfg.Sources { + spec, err := vector.Mapper(source) + if err != nil { + return nil, err + } + sources[source.Name] = spec + } + for _, transform := range cfg.Transforms { + spec, err := vector.Mapper(transform) + if err != nil { + return nil, err + } + transforms[transform.Name] = spec + } + for _, sink := range cfg.Sinks { + spec, err := vector.Mapper(sink) + if err != nil { + return nil, err + } + sinks[sink.Name] = spec + } + + err = mapstructure.Decode(cfg, &cfgMap) + if err != nil { + return nil, err + } + // TODO: remove hardcoded map keys + cfgMap["sources"] = sources + cfgMap["transforms"] = transforms + cfgMap["sinks"] = sinks + + return cfgMap, nil +} diff --git a/controllers/factory/config/config_build.go b/controllers/factory/config/config_build.go index 7672fe7c..e5a8ce80 100644 --- a/controllers/factory/config/config_build.go +++ b/controllers/factory/config/config_build.go @@ -21,18 +21,16 @@ import ( ) var ( - sourceDefault = map[string]interface{}{ - "defaultSource": map[string]string{ - "type": "kubernetes_logs", - }, + sourceDefault = vector.Source{ + Name: "defaultSource", + Type: "kubernetes_logs", } - - rate int32 = 100 - sinkDefault = map[string]interface{}{ - "defaultSink": map[string]interface{}{ - "type": "blackhole", - "inputs": []string{"defaultSource"}, - "rate": rate, + sinkDefault = vector.Sink{ + Name: "defaultSink", + Type: "blackhole", + Inputs: []string{"defaultSource"}, + Options: map[string]interface{}{ + "rate": 100, "print_interval_secs": 60, }, } @@ -47,10 +45,10 @@ func (cfg *Config) GenerateVectorConfig() error { } if len(sources) == 0 { - sources = sourceDefault + sources = []vector.Source{sourceDefault} } if len(sinks) == 0 { - sinks = sinkDefault + sinks = []vector.Sink{sinkDefault} } vectorConfig.Sinks = sinks @@ -62,45 +60,39 @@ func (cfg *Config) GenerateVectorConfig() error { return nil } -func (cfg *Config) getComponents() (map[string]interface{}, map[string]interface{}, map[string]interface{}, error) { - sourcesMap := make(map[string]interface{}) - transformsMap := make(map[string]interface{}) - sinksMap := make(map[string]interface{}) +func (cfg *Config) getComponents() (sources []vector.Source, transforms []vector.Transform, sinks []vector.Sink, err error) { for _, vCtrl := range cfg.pCtrls { - sources, err := vCtrl.GetSources(nil) + pipelineSources, err := vCtrl.GetSources(nil) if err != nil { return nil, nil, nil, err } - for _, source := range sources { - spec, err := vector.Mapper(source) + for _, source := range pipelineSources { if err != nil { return nil, nil, nil, err } - sourcesMap[source.Name] = spec + sources = append(sources, source) } - transforms, err := vCtrl.GetTransforms() + pipelineTransforms, err := vCtrl.GetTransforms() if err != nil { return nil, nil, nil, err } - for _, transform := range transforms { - spec, err := vector.Mapper(transform) + for _, transform := range pipelineTransforms { if err != nil { return nil, nil, nil, err } - transformsMap[transform.Name] = spec + transforms = append(transforms, transform) } - sinks, err := vCtrl.GetSinks() + pipelineSinks, err := vCtrl.GetSinks() if err != nil { return nil, nil, nil, err } - for _, sink := range sinks { - spec, err := vector.Mapper(sink) + for _, sink := range pipelineSinks { if err != nil { return nil, nil, nil, err } - sinksMap[sink.Name] = spec + sinks = append(sinks, sink) } } - return sourcesMap, transformsMap, sinksMap, nil + return sources, transforms, sinks, nil } diff --git a/controllers/factory/vector/types.go b/controllers/factory/vector/types.go index f766446b..3349dd19 100644 --- a/controllers/factory/vector/types.go +++ b/controllers/factory/vector/types.go @@ -17,11 +17,11 @@ limitations under the License. package vector type VectorConfig struct { - DataDir string `json:"data_dir,omitempty"` - Api *ApiSpec `json:"api,omitempty"` - Sources map[string]interface{} `json:"sources,omitempty"` - Transforms map[string]interface{} `json:"transforms,omitempty"` - Sinks map[string]interface{} `json:"sinks,omitempty"` + DataDir string `mapstructure:"data_dir"` + Api *ApiSpec `mapstructure:"api"` + Sources []Source `mapstructure:"sources"` + Transforms []Transform `mapstructure:"transforms"` + Sinks []Sink `mapstructure:"sinks"` } type ApiSpec struct { diff --git a/controllers/factory/vector/vector.go b/controllers/factory/vector/vector.go index 29d206c6..fbff5dbb 100644 --- a/controllers/factory/vector/vector.go +++ b/controllers/factory/vector/vector.go @@ -21,8 +21,8 @@ import ( ) func New(dataDir string, apiEnabled bool) *VectorConfig { - sources := make(map[string]interface{}) - sinks := make(map[string]interface{}) + sources := []Source{} + sinks := []Sink{} return &VectorConfig{ DataDir: dataDir, diff --git a/controllers/vector_controller.go b/controllers/vector_controller.go index efab81ab..608f9aaa 100644 --- a/controllers/vector_controller.go +++ b/controllers/vector_controller.go @@ -107,7 +107,9 @@ func (r *VectorReconciler) CreateOrUpdateVector(ctx context.Context, v *vectorv1 if err != nil { return ctrl.Result{}, err } - config.FillForVectorAgent() + if err := config.FillForVectorAgent(); err != nil { + return ctrl.Result{}, err + } // Get Config in Json ([]byte) byteConfig, err := config.GetByteConfig() From 978e46ffb1e9c07a71109f3b4dc8dfda0bfa40f0 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Mon, 31 Oct 2022 12:12:40 +0300 Subject: [PATCH 049/316] refactor k8s utils tests Signed-off-by: Zemtsov Vladimir --- controllers/factory/utils/k8s/k8s_test.go | 902 +++++++++++++--------- 1 file changed, 539 insertions(+), 363 deletions(-) diff --git a/controllers/factory/utils/k8s/k8s_test.go b/controllers/factory/utils/k8s/k8s_test.go index 1683aa38..4f08a501 100644 --- a/controllers/factory/utils/k8s/k8s_test.go +++ b/controllers/factory/utils/k8s/k8s_test.go @@ -22,6 +22,7 @@ import ( // . "github.com/onsi/ginkgo/v2" // . "github.com/onsi/gomega" + "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" "github.com/stretchr/testify/require" appsv1 "k8s.io/api/apps/v1" @@ -35,12 +36,18 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client/fake" ) -// type objects interface { -// *corev1.Service | *corev1.Secret | *appsv1.DaemonSet | *appsv1.StatefulSet | *corev1.ServiceAccount | *rbacv1.ClusterRole | *rbacv1.ClusterRoleBinding -// } +func getInitObjectMeta() metav1.ObjectMeta { + ObjectMeta := metav1.ObjectMeta{ + Name: "init", + Namespace: "test-namespace", + } + + return ObjectMeta +} var reconcileObjectCase = func(objInit, obj interface{}, want error) func(t *testing.T) { return func(t *testing.T) { + t.Parallel() t.Helper() req := require.New(t) @@ -98,269 +105,408 @@ var reconcileObjectCase = func(objInit, obj interface{}, want error) func(t *tes } } -func TestCreateOrUpdateService(t *testing.T) { - serviceInit := &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "init", - Namespace: "test-namespace", - ResourceVersion: "", - }, - } +var nameRequeriedError = apierrors.NewInvalid( + schema.GroupKind{}, + "", + field.ErrorList{ + field.Required( + field.NewPath("metadata.name"), + "name is required", + ), + }, +) - serviceCase1 := &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "test-namespace", - }, - } - serviceCase2 := &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "init", - Namespace: "test-namespace", - }, - } - serviceCase3 := &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "test-namespace2", - }, - } - serviceCase4 := &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{}, +func TestCreateOrUpdateService(t *testing.T) { + t.Parallel() + + initObj := &corev1.Service{ + ObjectMeta: getInitObjectMeta(), + } + + type secriveCase struct { + name string + obj *corev1.Service + err error + } + + secriveCases := []secriveCase{ + { + name: "Create Simple case", + obj: &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace", + }, + }, + err: nil, + }, + { + name: "Create Alredy exist case", + obj: &corev1.Service{ + ObjectMeta: getInitObjectMeta(), + }, + err: nil, + }, + { + name: "Create with Another Namespace case", + obj: &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace2", + }, + }, + err: nil, + }, + { + name: "Create without Name case", + obj: &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{}, + }, + err: nameRequeriedError, + }, + } + + for _, tc := range secriveCases { + t.Run(tc.name, reconcileObjectCase(initObj, tc.obj, tc.err)) } - errorCase4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) - - t.Run("Create Simple case", reconcileObjectCase(serviceInit, serviceCase1, nil)) - t.Run("Create Alredy exist case", reconcileObjectCase(serviceInit, serviceCase2, nil)) - t.Run("Create with Another Namespace case", reconcileObjectCase(serviceInit, serviceCase3, nil)) - t.Run("Create without Name case", reconcileObjectCase(serviceInit, serviceCase4, errorCase4)) } func TestCreateOrUpdateSecret(t *testing.T) { - secretInit := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "init", - Namespace: "test-namespace", - }, - } - - secretCase1 := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "test-namespace", - }, - } - secretCase2 := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "init", - Namespace: "test-namespace", - }, + t.Parallel() + + initObj := &corev1.Secret{ + ObjectMeta: getInitObjectMeta(), + } + + type secretCase struct { + name string + obj *corev1.Secret + err error + } + + secretCases := []secretCase{ + { + name: "Create Simple case", + obj: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace", + }, + }, + err: nil, + }, + { + name: "Create Alredy exist case", + obj: &corev1.Secret{ + ObjectMeta: getInitObjectMeta(), + }, + err: nil, + }, + { + name: "Create with Another Namespace case", + obj: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace2", + }, + }, + err: nil, + }, + { + name: "Create without Name case", + obj: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{}, + }, + err: nameRequeriedError, + }, + } + + for _, tc := range secretCases { + t.Run(tc.name, reconcileObjectCase(initObj, tc.obj, tc.err)) } - secretCase3 := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "test-namespace2", - }, - } - secretCase4 := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{}, - } - errorCase4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) - - t.Run("Create Simple case", reconcileObjectCase(secretInit, secretCase1, nil)) - t.Run("Create Alredy exist case", reconcileObjectCase(secretInit, secretCase2, nil)) - t.Run("Create with Another Namespace case", reconcileObjectCase(secretInit, secretCase3, nil)) - t.Run("Create without Name case", reconcileObjectCase(secretInit, secretCase4, errorCase4)) } func TestCreateOrUpdateDaemonSet(t *testing.T) { - daemonSetInit := &appsv1.DaemonSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "init", - Namespace: "test-namespace", - }, - } - - daemonSetCase1 := &appsv1.DaemonSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "test-namespace", - }, - } - daemonSetCase2 := &appsv1.DaemonSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "init", - Namespace: "test-namespace", - }, - } - daemonSetCase3 := &appsv1.DaemonSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "test-namespace2", - }, - } - daemonSetCase4 := &appsv1.DaemonSet{ - ObjectMeta: metav1.ObjectMeta{}, + t.Parallel() + + initObj := &appsv1.DaemonSet{ + ObjectMeta: getInitObjectMeta(), + } + + type daemonSetCase struct { + name string + obj *appsv1.DaemonSet + err error + } + + daemonSetCases := []daemonSetCase{ + { + name: "Create Simple case", + obj: &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "init", + Namespace: "test-namespace", + }, + }, + err: nil, + }, + { + name: "Create Alredy exist case", + obj: &appsv1.DaemonSet{ + ObjectMeta: getInitObjectMeta(), + }, + err: nil, + }, + { + name: "Create with Another Namespace case", + obj: &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace2", + }, + }, + err: nil, + }, + { + name: "Create without Name case", + obj: &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{}, + }, + err: nameRequeriedError, + }, + } + + for _, tc := range daemonSetCases { + t.Run(tc.name, reconcileObjectCase(initObj, tc.obj, tc.err)) } - errorCase4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) - - t.Run("Create Simple case", reconcileObjectCase(daemonSetInit, daemonSetCase1, nil)) - t.Run("Create Alredy exist case", reconcileObjectCase(daemonSetInit, daemonSetCase2, nil)) - t.Run("Create with Another Namespace case", reconcileObjectCase(daemonSetInit, daemonSetCase3, nil)) - t.Run("Create without Name case", reconcileObjectCase(daemonSetInit, daemonSetCase4, errorCase4)) } func TestCreateOrUpdateStatefulSet(t *testing.T) { - statefulSetInit := &appsv1.StatefulSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "init", - Namespace: "test-namespace", - }, - } - - statefulSetCase1 := &appsv1.StatefulSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "test-namespace", - }, - } - statefulSetCase2 := &appsv1.StatefulSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "init", - Namespace: "test-namespace", - }, - } - statefulSetCase3 := &appsv1.StatefulSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "test-namespace2", - }, + t.Parallel() + + initObj := &appsv1.StatefulSet{ + ObjectMeta: getInitObjectMeta(), + } + + type statefulSetCase struct { + name string + obj *appsv1.StatefulSet + err error + } + + statefulSetCases := []statefulSetCase{ + { + name: "Create Simple case", + obj: &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "init", + Namespace: "test-namespace", + }, + }, + err: nil, + }, + { + name: "Create Alredy exist case", + obj: &appsv1.StatefulSet{ + ObjectMeta: getInitObjectMeta(), + }, + err: nil, + }, + { + name: "Create with Another Namespace case", + obj: &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace2", + }, + }, + err: nil, + }, + { + name: "Create without Name case", + obj: &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{}, + }, + err: nameRequeriedError, + }, + } + + for _, tc := range statefulSetCases { + t.Run(tc.name, reconcileObjectCase(initObj, tc.obj, tc.err)) } - statefulSetCase4 := &appsv1.StatefulSet{ - ObjectMeta: metav1.ObjectMeta{}, - } - errorCase4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) - - t.Run("Create Simple case", reconcileObjectCase(statefulSetInit, statefulSetCase1, nil)) - t.Run("Create Alredy exist case", reconcileObjectCase(statefulSetInit, statefulSetCase2, nil)) - t.Run("Create with Another Namespace case", reconcileObjectCase(statefulSetInit, statefulSetCase3, nil)) - t.Run("Create without Name case", reconcileObjectCase(statefulSetInit, statefulSetCase4, errorCase4)) } func TestCreateOrUpdateServiceAccount(t *testing.T) { - serviceAccountInit := &corev1.ServiceAccount{ - ObjectMeta: metav1.ObjectMeta{ - Name: "init", - Namespace: "test-namespace", - }, - } - - serviceAccountCase1 := &corev1.ServiceAccount{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "test-namespace", - }, - } - serviceAccountCase2 := &corev1.ServiceAccount{ - ObjectMeta: metav1.ObjectMeta{ - Name: "init", - Namespace: "test-namespace", - }, - } - serviceAccountCase3 := &corev1.ServiceAccount{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "test-namespace2", - }, - } - serviceAccountCase4 := &corev1.ServiceAccount{ - ObjectMeta: metav1.ObjectMeta{}, + t.Parallel() + + initObj := &corev1.ServiceAccount{ + ObjectMeta: getInitObjectMeta(), + } + + type serviceAccountCase struct { + name string + obj *corev1.ServiceAccount + err error + } + + serviceAccountCases := []serviceAccountCase{ + { + name: "Create Simple case", + obj: &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: "init", + Namespace: "test-namespace", + }, + }, + err: nil, + }, + { + name: "Create Alredy exist case", + obj: &corev1.ServiceAccount{ + ObjectMeta: getInitObjectMeta(), + }, + err: nil, + }, + { + name: "Create with Another Namespace case", + obj: &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace2", + }, + }, + err: nil, + }, + { + name: "Create without Name case", + obj: &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{}, + }, + err: nameRequeriedError, + }, + } + + for _, tc := range serviceAccountCases { + t.Run(tc.name, reconcileObjectCase(initObj, tc.obj, tc.err)) } - errorCase4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) - - t.Run("Create Simple case", reconcileObjectCase(serviceAccountInit, serviceAccountCase1, nil)) - t.Run("Create Alredy exist case", reconcileObjectCase(serviceAccountInit, serviceAccountCase2, nil)) - t.Run("Create with Another Namespace case", reconcileObjectCase(serviceAccountInit, serviceAccountCase3, nil)) - t.Run("Create without Name case", reconcileObjectCase(serviceAccountInit, serviceAccountCase4, errorCase4)) } func TestCreateOrUpdateClusterRole(t *testing.T) { - clusterRoleInit := &rbacv1.ClusterRole{ - ObjectMeta: metav1.ObjectMeta{ - Name: "init", - Namespace: "test-namespace", - }, - } - - clusterRoleCase1 := &rbacv1.ClusterRole{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "test-namespace", - }, - } - clusterRoleCase2 := &rbacv1.ClusterRole{ - ObjectMeta: metav1.ObjectMeta{ - Name: "init", - Namespace: "test-namespace", - }, + t.Parallel() + + initObj := &rbacv1.ClusterRole{ + ObjectMeta: getInitObjectMeta(), + } + + type clusterRoleCase struct { + name string + obj *rbacv1.ClusterRole + err error + } + + clusterRoleCases := []clusterRoleCase{ + { + name: "Create Simple case", + obj: &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "init", + Namespace: "test-namespace", + }, + }, + err: nil, + }, + { + name: "Create Alredy exist case", + obj: &rbacv1.ClusterRole{ + ObjectMeta: getInitObjectMeta(), + }, + err: nil, + }, + { + name: "Create with Another Namespace case", + obj: &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace2", + }, + }, + err: nil, + }, + { + name: "Create without Name case", + obj: &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{}, + }, + err: nameRequeriedError, + }, + } + + for _, tc := range clusterRoleCases { + t.Run(tc.name, reconcileObjectCase(initObj, tc.obj, tc.err)) } - clusterRoleCase3 := &rbacv1.ClusterRole{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "test-namespace2", - }, - } - clusterRoleCase4 := &rbacv1.ClusterRole{ - ObjectMeta: metav1.ObjectMeta{}, - } - errorCase4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) - - t.Run("Create Simple case", reconcileObjectCase(clusterRoleInit, clusterRoleCase1, nil)) - t.Run("Create Alredy exist case", reconcileObjectCase(clusterRoleInit, clusterRoleCase2, nil)) - t.Run("Create with Another Namespace case", reconcileObjectCase(clusterRoleInit, clusterRoleCase3, nil)) - t.Run("Create without Name case", reconcileObjectCase(clusterRoleInit, clusterRoleCase4, errorCase4)) } func TestCreateOrUpdateClusterRoleBinding(t *testing.T) { - clusterRoleBindingInit := &rbacv1.ClusterRoleBinding{ - ObjectMeta: metav1.ObjectMeta{ - Name: "init", - Namespace: "test-namespace", - }, - } - - clusterRoleBindingCase1 := &rbacv1.ClusterRoleBinding{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "test-namespace", - }, + t.Parallel() + + initObj := &rbacv1.ClusterRoleBinding{ + ObjectMeta: getInitObjectMeta(), + } + + type clusterRoleBindingCase struct { + name string + obj *rbacv1.ClusterRoleBinding + err error + } + + clusterRoleBindingCases := []clusterRoleBindingCase{ + { + name: "Create Simple case", + obj: &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "init", + Namespace: "test-namespace", + }, + }, + err: nil, + }, + { + name: "Create Alredy exist case", + obj: &rbacv1.ClusterRoleBinding{ + ObjectMeta: getInitObjectMeta(), + }, + err: nil, + }, + { + name: "Create with Another Namespace case", + obj: &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace2", + }, + }, + err: nil, + }, + { + name: "Create without Name case", + obj: &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{}, + }, + err: nameRequeriedError, + }, + } + + for _, tc := range clusterRoleBindingCases { + t.Run(tc.name, reconcileObjectCase(initObj, tc.obj, tc.err)) } - clusterRoleBindingCase2 := &rbacv1.ClusterRoleBinding{ - ObjectMeta: metav1.ObjectMeta{ - Name: "init", - Namespace: "test-namespace", - }, - } - clusterRoleBindingCase3 := &rbacv1.ClusterRoleBinding{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "test-namespace2", - }, - } - clusterRoleBindingCase4 := &rbacv1.ClusterRoleBinding{ - ObjectMeta: metav1.ObjectMeta{}, - } - errorCase4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) - - t.Run("Create Simple case", reconcileObjectCase(clusterRoleBindingInit, clusterRoleBindingCase1, nil)) - t.Run("Create Alredy exist case", reconcileObjectCase(clusterRoleBindingInit, clusterRoleBindingCase2, nil)) - t.Run("Create with Another Namespace case", reconcileObjectCase(clusterRoleBindingInit, clusterRoleBindingCase3, nil)) - t.Run("Create without Name case", reconcileObjectCase(clusterRoleBindingInit, clusterRoleBindingCase4, errorCase4)) } func TestCreatePod(t *testing.T) { + t.Parallel() + createPodCase := func(objInit, obj *corev1.Pod, want error) func(t *testing.T) { return func(t *testing.T) { + t.Parallel() t.Helper() req := require.New(t) @@ -371,46 +517,53 @@ func TestCreatePod(t *testing.T) { } } - objInit := &corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: "init", - Namespace: "test-namespace", - ResourceVersion: "", - }, + initObj := &corev1.Pod{ + ObjectMeta: getInitObjectMeta(), } - objCase1 := &corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "test-namespace", - }, + + type podCase struct { + name string + obj *corev1.Pod + err error } - objCase2 := &corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: "init", - Namespace: "test-namespace", + + podCases := []podCase{ + { + name: "Create not exist pod case", + obj: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace", + }, + }, + err: nil, + }, + { + name: "Create alredy exist pod case", + obj: &corev1.Pod{ + ObjectMeta: getInitObjectMeta(), + }, + err: apierrors.NewAlreadyExists( + schema.GroupResource{ + Group: "", + Resource: "pods", + }, + "init", + ), }, } - grCase2 := schema.GroupResource{ - Group: "", - Resource: "pods", - } - errorCase2 := apierrors.NewAlreadyExists(grCase2, objCase2.ObjectMeta.Name) - t.Run("Create not exist pod case", createPodCase(objInit, objCase1, nil)) - t.Run("Create alredy exist pod case", createPodCase(objInit, objCase2, errorCase2)) + for _, tc := range podCases { + t.Run(tc.name, createPodCase(initObj, tc.obj, tc.err)) + } } -// func CreatePod(pod *corev1.Pod, c client.Client) error { -// err := c.Create(context.TODO(), pod) -// if err != nil { -// return err -// } -// return nil -// } - func TestGetPod(t *testing.T) { + t.Parallel() + getPodCase := func(objInit, obj, wantPod *corev1.Pod, want error) func(t *testing.T) { return func(t *testing.T) { + t.Parallel() t.Helper() req := require.New(t) @@ -424,39 +577,62 @@ func TestGetPod(t *testing.T) { } } - objInit := &corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: "init", - Namespace: "test-namespace", - ResourceVersion: "", - }, + initObj := &corev1.Pod{ + ObjectMeta: getInitObjectMeta(), + } + + type podCase struct { + name string + obj *corev1.Pod + wantObj *corev1.Pod + err error + } + + podCases := []podCase{ + { + name: "Get not exist pod case", + obj: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace", + }, + }, + wantObj: nil, + err: apierrors.NewNotFound( + schema.GroupResource{ + Group: "", + Resource: "pods", + }, + "test", + ), + }, + { + name: "Get exist pod case", + obj: &corev1.Pod{ + ObjectMeta: getInitObjectMeta(), + }, + wantObj: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "init", + Namespace: "test-namespace", + ResourceVersion: "999", + }, + }, + err: nil, + }, + } + + for _, tc := range podCases { + t.Run(tc.name, getPodCase(initObj, tc.obj, tc.wantObj, tc.err)) } - objCase1 := &corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "test-namespace", - }, - } - gvrCase1 := schema.GroupVersionResource{ - Group: "", - Version: "v1", - Resource: "pods", - } - errorCase1 := apierrors.NewNotFound(gvrCase1.GroupResource(), objCase1.ObjectMeta.Name) - objCase2 := &corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: "init", - Namespace: "test-namespace", - }, - } - - t.Run("Get not exist pod case", getPodCase(objInit, objCase1, nil, errorCase1)) - t.Run("Get exist pod case", getPodCase(objInit, objCase2, objInit, nil)) } func TestUpdateStatus(t *testing.T) { + t.Parallel() + updateStatusCase := func(objInit, obj client.Object, want error) func(t *testing.T) { return func(t *testing.T) { + t.Parallel() t.Helper() req := require.New(t) @@ -468,76 +644,76 @@ func TestUpdateStatus(t *testing.T) { } } - objInit := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "init", - Namespace: "test-namespace", - ResourceVersion: "", - }, - } - objCase1 := &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "test-namespace", - }, - } - objCase2 := &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "init", - Namespace: "test-namespace", - }, - } - objCase3 := &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "test-namespace2", - }, - } - objCase4 := &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{}, + type testCase struct { + name string + initObj *appsv1.Deployment + updateObj *appsv1.Deployment + err error + } + + testCases := []testCase{ + { + name: "Update Simple case", + initObj: &appsv1.Deployment{ + ObjectMeta: getInitObjectMeta(), + }, + updateObj: &appsv1.Deployment{ + ObjectMeta: getInitObjectMeta(), + }, + err: nil, + }, + { + name: "Update Alredy exist case", + initObj: &appsv1.Deployment{ + ObjectMeta: getInitObjectMeta(), + }, + updateObj: &appsv1.Deployment{ + ObjectMeta: getInitObjectMeta(), + }, + err: nil, + }, + { + name: "Update without Name case", + initObj: &appsv1.Deployment{ + ObjectMeta: getInitObjectMeta(), + }, + updateObj: &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{}, + }, + err: nameRequeriedError, + }, + { + name: "Update status case", + initObj: &appsv1.Deployment{ + ObjectMeta: getInitObjectMeta(), + }, + updateObj: &appsv1.Deployment{ + ObjectMeta: getInitObjectMeta(), + Status: appsv1.DeploymentStatus{ + Replicas: 10, + }, + }, + err: nil, + }, + } + + // objCase5 := &appsv1.Deployment{ + // ObjectMeta: metav1.ObjectMeta{ + // Name: "test", + // Namespace: "test-namespace", + // }, + // Status: appsv1.DeploymentStatus{ + // Replicas: 10, + // }, + // } + // gvrCase5 := schema.GroupVersionResource{ + // Group: "apps", + // Version: "v1", + // Resource: "deployments", + // } + // errorCase5 := apierrors.NewNotFound(gvrCase5.GroupResource(), objCase5.ObjectMeta.Name) + + for _, tc := range testCases { + t.Run(tc.name, updateStatusCase(tc.initObj, tc.updateObj, tc.err)) } - errorCase4 := apierrors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) - - objCase5 := &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "test-namespace", - }, - Status: appsv1.DeploymentStatus{ - Replicas: 10, - }, - } - gvrCase5 := schema.GroupVersionResource{ - Group: "apps", - Version: "v1", - Resource: "deployments", - } - errorCase5 := apierrors.NewNotFound(gvrCase5.GroupResource(), objCase5.ObjectMeta.Name) - - init_objCase6 := &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "test-namespace", - }, - Status: appsv1.DeploymentStatus{ - Replicas: 5, - }, - } - objCase6 := &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "test-namespace", - }, - Status: appsv1.DeploymentStatus{ - Replicas: 10, - }, - } - - t.Run("Update Simple case", updateStatusCase(objInit, objCase1, nil)) - t.Run("Update Alredy exist case", updateStatusCase(objInit, objCase2, nil)) - t.Run("Update with Another Namespace case", updateStatusCase(objInit, objCase3, nil)) - t.Run("Update without Name case", updateStatusCase(objInit, objCase4, errorCase4)) - t.Run("Update not exist Deployment with wrong init case", updateStatusCase(objInit, objCase5, errorCase5)) - t.Run("Update Deployment case", updateStatusCase(init_objCase6, objCase6, nil)) - } From 88c53d1ff742c41bce8d747f304e1ce4b9e67229 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Mon, 31 Oct 2022 12:27:22 +0300 Subject: [PATCH 050/316] fix some errors Signed-off-by: Zemtsov Vladimir --- .../factory/config/configcheck/configcheck.go | 3 +- .../config/configcheck/configcheck_error.go | 4 +- controllers/factory/utils/hash/hash.go | 1 + controllers/factory/utils/hash/hash_test.go | 26 ++++++++- controllers/factory/utils/k8s/k8s_test.go | 58 ++++++------------- .../vector/vectoragent/vectoragent_config.go | 2 +- controllers/vectorpipeline_controller.go | 2 +- 7 files changed, 49 insertions(+), 47 deletions(-) diff --git a/controllers/factory/config/configcheck/configcheck.go b/controllers/factory/config/configcheck/configcheck.go index b99c75d2..7698656a 100644 --- a/controllers/factory/config/configcheck/configcheck.go +++ b/controllers/factory/config/configcheck/configcheck.go @@ -142,6 +142,7 @@ func labelsForVectorConfigCheck() map[string]string { func (cc *ConfigCheck) getNameVectorConfigCheck() string { n := "configcheck-" + "-" + cc.Name + "-" + cc.Hash + return n } @@ -173,7 +174,7 @@ func (cc *ConfigCheck) getCheckResult(pod *corev1.Pod) error { if err != nil { return err } - return &ErrConfigCheck{ + return &ConfigCheckError{ Reason: reason, } case "Succeeded": diff --git a/controllers/factory/config/configcheck/configcheck_error.go b/controllers/factory/config/configcheck/configcheck_error.go index 2fd40672..4ecf5e85 100644 --- a/controllers/factory/config/configcheck/configcheck_error.go +++ b/controllers/factory/config/configcheck/configcheck_error.go @@ -20,11 +20,11 @@ type error interface { Error() string } -type ErrConfigCheck struct { +type ConfigCheckError struct { Reason string Err error } -func (e *ErrConfigCheck) Error() string { +func (e *ConfigCheckError) Error() string { return e.Reason } diff --git a/controllers/factory/utils/hash/hash.go b/controllers/factory/utils/hash/hash.go index cde0db8d..0681fe6c 100644 --- a/controllers/factory/utils/hash/hash.go +++ b/controllers/factory/utils/hash/hash.go @@ -20,5 +20,6 @@ import "hash/crc32" func Get(input []byte) uint32 { crc32q := crc32.MakeTable(crc32.IEEE) + return crc32.Checksum(input, crc32q) } diff --git a/controllers/factory/utils/hash/hash_test.go b/controllers/factory/utils/hash/hash_test.go index 950a06f9..35ac2f68 100644 --- a/controllers/factory/utils/hash/hash_test.go +++ b/controllers/factory/utils/hash/hash_test.go @@ -27,6 +27,7 @@ func TestGet(t *testing.T) { hashCase := func(bytes []byte, want uint32) func(t *testing.T) { return func(t *testing.T) { t.Helper() + t.Parallel() req := require.New(t) result := hash.Get(bytes) @@ -34,6 +35,27 @@ func TestGet(t *testing.T) { } } - t.Run("Simple case", hashCase([]byte("test"), uint32(3632233996))) - t.Run("Zero case", hashCase([]byte(""), uint32(0))) + type testCase struct { + name string + bytes []byte + want uint32 + } + + testCases := []testCase{ + { + name: "Simple case", + bytes: []byte("test"), + want: uint32(3632233996), + }, + { + name: "Zero case", + bytes: []byte(""), + want: uint32(0), + }, + } + + t.Parallel() + for _, tc := range testCases { + t.Run(tc.name, hashCase(tc.bytes, tc.want)) + } } diff --git a/controllers/factory/utils/k8s/k8s_test.go b/controllers/factory/utils/k8s/k8s_test.go index 4f08a501..33beb0a2 100644 --- a/controllers/factory/utils/k8s/k8s_test.go +++ b/controllers/factory/utils/k8s/k8s_test.go @@ -47,8 +47,9 @@ func getInitObjectMeta() metav1.ObjectMeta { var reconcileObjectCase = func(objInit, obj interface{}, want error) func(t *testing.T) { return func(t *testing.T) { - t.Parallel() t.Helper() + t.Parallel() + req := require.New(t) switch obj.(type) { @@ -117,8 +118,6 @@ var nameRequeriedError = apierrors.NewInvalid( ) func TestCreateOrUpdateService(t *testing.T) { - t.Parallel() - initObj := &corev1.Service{ ObjectMeta: getInitObjectMeta(), } @@ -166,14 +165,13 @@ func TestCreateOrUpdateService(t *testing.T) { }, } + t.Parallel() for _, tc := range secriveCases { t.Run(tc.name, reconcileObjectCase(initObj, tc.obj, tc.err)) } } func TestCreateOrUpdateSecret(t *testing.T) { - t.Parallel() - initObj := &corev1.Secret{ ObjectMeta: getInitObjectMeta(), } @@ -221,14 +219,13 @@ func TestCreateOrUpdateSecret(t *testing.T) { }, } + t.Parallel() for _, tc := range secretCases { t.Run(tc.name, reconcileObjectCase(initObj, tc.obj, tc.err)) } } func TestCreateOrUpdateDaemonSet(t *testing.T) { - t.Parallel() - initObj := &appsv1.DaemonSet{ ObjectMeta: getInitObjectMeta(), } @@ -276,14 +273,13 @@ func TestCreateOrUpdateDaemonSet(t *testing.T) { }, } + t.Parallel() for _, tc := range daemonSetCases { t.Run(tc.name, reconcileObjectCase(initObj, tc.obj, tc.err)) } } func TestCreateOrUpdateStatefulSet(t *testing.T) { - t.Parallel() - initObj := &appsv1.StatefulSet{ ObjectMeta: getInitObjectMeta(), } @@ -331,14 +327,13 @@ func TestCreateOrUpdateStatefulSet(t *testing.T) { }, } + t.Parallel() for _, tc := range statefulSetCases { t.Run(tc.name, reconcileObjectCase(initObj, tc.obj, tc.err)) } } func TestCreateOrUpdateServiceAccount(t *testing.T) { - t.Parallel() - initObj := &corev1.ServiceAccount{ ObjectMeta: getInitObjectMeta(), } @@ -386,14 +381,13 @@ func TestCreateOrUpdateServiceAccount(t *testing.T) { }, } + t.Parallel() for _, tc := range serviceAccountCases { t.Run(tc.name, reconcileObjectCase(initObj, tc.obj, tc.err)) } } func TestCreateOrUpdateClusterRole(t *testing.T) { - t.Parallel() - initObj := &rbacv1.ClusterRole{ ObjectMeta: getInitObjectMeta(), } @@ -441,14 +435,13 @@ func TestCreateOrUpdateClusterRole(t *testing.T) { }, } + t.Parallel() for _, tc := range clusterRoleCases { t.Run(tc.name, reconcileObjectCase(initObj, tc.obj, tc.err)) } } func TestCreateOrUpdateClusterRoleBinding(t *testing.T) { - t.Parallel() - initObj := &rbacv1.ClusterRoleBinding{ ObjectMeta: getInitObjectMeta(), } @@ -496,18 +489,18 @@ func TestCreateOrUpdateClusterRoleBinding(t *testing.T) { }, } + t.Parallel() for _, tc := range clusterRoleBindingCases { t.Run(tc.name, reconcileObjectCase(initObj, tc.obj, tc.err)) } } func TestCreatePod(t *testing.T) { - t.Parallel() - createPodCase := func(objInit, obj *corev1.Pod, want error) func(t *testing.T) { + t.Parallel() return func(t *testing.T) { - t.Parallel() t.Helper() + req := require.New(t) cl := fake.NewClientBuilder().WithObjects(objInit).Build() @@ -553,18 +546,18 @@ func TestCreatePod(t *testing.T) { }, } + t.Parallel() for _, tc := range podCases { t.Run(tc.name, createPodCase(initObj, tc.obj, tc.err)) } } func TestGetPod(t *testing.T) { - t.Parallel() - getPodCase := func(objInit, obj, wantPod *corev1.Pod, want error) func(t *testing.T) { + t.Parallel() return func(t *testing.T) { - t.Parallel() t.Helper() + req := require.New(t) cl := fake.NewClientBuilder().WithObjects(objInit).Build() @@ -622,18 +615,18 @@ func TestGetPod(t *testing.T) { }, } + t.Parallel() for _, tc := range podCases { t.Run(tc.name, getPodCase(initObj, tc.obj, tc.wantObj, tc.err)) } } func TestUpdateStatus(t *testing.T) { - t.Parallel() - updateStatusCase := func(objInit, obj client.Object, want error) func(t *testing.T) { + t.Parallel() return func(t *testing.T) { - t.Parallel() t.Helper() + req := require.New(t) cl := fake.NewClientBuilder().WithObjects(objInit).Build() @@ -697,22 +690,7 @@ func TestUpdateStatus(t *testing.T) { }, } - // objCase5 := &appsv1.Deployment{ - // ObjectMeta: metav1.ObjectMeta{ - // Name: "test", - // Namespace: "test-namespace", - // }, - // Status: appsv1.DeploymentStatus{ - // Replicas: 10, - // }, - // } - // gvrCase5 := schema.GroupVersionResource{ - // Group: "apps", - // Version: "v1", - // Resource: "deployments", - // } - // errorCase5 := apierrors.NewNotFound(gvrCase5.GroupResource(), objCase5.ObjectMeta.Name) - + t.Parallel() for _, tc := range testCases { t.Run(tc.name, updateStatusCase(tc.initObj, tc.updateObj, tc.err)) } diff --git a/controllers/factory/vector/vectoragent/vectoragent_config.go b/controllers/factory/vector/vectoragent/vectoragent_config.go index cc47bcf9..b17bfdd8 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_config.go +++ b/controllers/factory/vector/vectoragent/vectoragent_config.go @@ -32,7 +32,7 @@ func (ctrl *Controller) createVectorAgentConfig(ctx context.Context) (*corev1.Se if ctrl.Vector.Status.LastAppliedConfigHash == nil || *ctrl.Vector.Status.LastAppliedConfigHash != cfgHash { err := configCheck.Run() - if _, ok := err.(*configcheck.ErrConfigCheck); ok { + if _, ok := err.(*configcheck.ConfigCheckError); ok { if err := ctrl.SetFailedStatus(ctx, err); err != nil { return nil, err } diff --git a/controllers/vectorpipeline_controller.go b/controllers/vectorpipeline_controller.go index c4db42bb..b542c2d0 100644 --- a/controllers/vectorpipeline_controller.go +++ b/controllers/vectorpipeline_controller.go @@ -127,7 +127,7 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque // Start ConfigCheck err = configCheck.Run() - if _, ok := err.(*configcheck.ErrConfigCheck); ok { + if _, ok := err.(*configcheck.ConfigCheckError); ok { if err := pCtrl.SetFailedStatus(err); err != nil { return ctrl.Result{}, err } From 192aba9efbfb6511568447d193e7a43406778521 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Mon, 31 Oct 2022 12:43:16 +0300 Subject: [PATCH 051/316] disable parallel for k8s utils Signed-off-by: Zemtsov Vladimir --- controllers/factory/utils/k8s/k8s_test.go | 26 +++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/controllers/factory/utils/k8s/k8s_test.go b/controllers/factory/utils/k8s/k8s_test.go index 33beb0a2..58ffa2e1 100644 --- a/controllers/factory/utils/k8s/k8s_test.go +++ b/controllers/factory/utils/k8s/k8s_test.go @@ -165,7 +165,7 @@ func TestCreateOrUpdateService(t *testing.T) { }, } - t.Parallel() + // t.Parallel() for _, tc := range secriveCases { t.Run(tc.name, reconcileObjectCase(initObj, tc.obj, tc.err)) } @@ -219,7 +219,7 @@ func TestCreateOrUpdateSecret(t *testing.T) { }, } - t.Parallel() + // t.Parallel() for _, tc := range secretCases { t.Run(tc.name, reconcileObjectCase(initObj, tc.obj, tc.err)) } @@ -273,7 +273,7 @@ func TestCreateOrUpdateDaemonSet(t *testing.T) { }, } - t.Parallel() + // t.Parallel() for _, tc := range daemonSetCases { t.Run(tc.name, reconcileObjectCase(initObj, tc.obj, tc.err)) } @@ -327,7 +327,7 @@ func TestCreateOrUpdateStatefulSet(t *testing.T) { }, } - t.Parallel() + // t.Parallel() for _, tc := range statefulSetCases { t.Run(tc.name, reconcileObjectCase(initObj, tc.obj, tc.err)) } @@ -381,7 +381,7 @@ func TestCreateOrUpdateServiceAccount(t *testing.T) { }, } - t.Parallel() + // t.Parallel() for _, tc := range serviceAccountCases { t.Run(tc.name, reconcileObjectCase(initObj, tc.obj, tc.err)) } @@ -435,7 +435,7 @@ func TestCreateOrUpdateClusterRole(t *testing.T) { }, } - t.Parallel() + // t.Parallel() for _, tc := range clusterRoleCases { t.Run(tc.name, reconcileObjectCase(initObj, tc.obj, tc.err)) } @@ -489,7 +489,7 @@ func TestCreateOrUpdateClusterRoleBinding(t *testing.T) { }, } - t.Parallel() + // t.Parallel() for _, tc := range clusterRoleBindingCases { t.Run(tc.name, reconcileObjectCase(initObj, tc.obj, tc.err)) } @@ -497,7 +497,7 @@ func TestCreateOrUpdateClusterRoleBinding(t *testing.T) { func TestCreatePod(t *testing.T) { createPodCase := func(objInit, obj *corev1.Pod, want error) func(t *testing.T) { - t.Parallel() + // t.Parallel() return func(t *testing.T) { t.Helper() @@ -546,7 +546,7 @@ func TestCreatePod(t *testing.T) { }, } - t.Parallel() + // t.Parallel() for _, tc := range podCases { t.Run(tc.name, createPodCase(initObj, tc.obj, tc.err)) } @@ -554,7 +554,7 @@ func TestCreatePod(t *testing.T) { func TestGetPod(t *testing.T) { getPodCase := func(objInit, obj, wantPod *corev1.Pod, want error) func(t *testing.T) { - t.Parallel() + // t.Parallel() return func(t *testing.T) { t.Helper() @@ -615,7 +615,7 @@ func TestGetPod(t *testing.T) { }, } - t.Parallel() + // t.Parallel() for _, tc := range podCases { t.Run(tc.name, getPodCase(initObj, tc.obj, tc.wantObj, tc.err)) } @@ -623,7 +623,7 @@ func TestGetPod(t *testing.T) { func TestUpdateStatus(t *testing.T) { updateStatusCase := func(objInit, obj client.Object, want error) func(t *testing.T) { - t.Parallel() + // t.Parallel() return func(t *testing.T) { t.Helper() @@ -690,7 +690,7 @@ func TestUpdateStatus(t *testing.T) { }, } - t.Parallel() + // t.Parallel() for _, tc := range testCases { t.Run(tc.name, updateStatusCase(tc.initObj, tc.updateObj, tc.err)) } From 6c192b47c59f6b95931b02409299dec471ce1580 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Mon, 31 Oct 2022 13:25:34 +0300 Subject: [PATCH 052/316] Cleanup Signed-off-by: Zemtsov Vladimir --- .../factory/config/configcheck/configcheck.go | 21 ------------------- controllers/factory/utils/hash/hash_test.go | 2 +- 2 files changed, 1 insertion(+), 22 deletions(-) diff --git a/controllers/factory/config/configcheck/configcheck.go b/controllers/factory/config/configcheck/configcheck.go index 7698656a..62e6f9e1 100644 --- a/controllers/factory/config/configcheck/configcheck.go +++ b/controllers/factory/config/configcheck/configcheck.go @@ -54,27 +54,6 @@ func New(ctx context.Context, config []byte, c client.Client, cs *kubernetes.Cli } } -// func (cfg *Config) StartCheck() error { - -// log := log.FromContext(context.TODO()).WithValues("ConfigCheck Vector Pipeline", cfg.vaCtrl.Vector.Name) - -// configCheck := configcheck.New(cfg.Ctx, cfg.ByteConfig, cfg.vaCtrl.Client, cfg.vaCtrl.ClientSet, cfg.Name, cfg.vaCtrl.Vector.Namespace, cfg.vaCtrl.Vector.Spec.Agent.Image) - -// err := configCheck.Run() -// if _, ok := err.(*configcheck.ErrConfigCheck); ok { -// if err := cfg.vaCtrl.SetFailedStatus(cfg.Ctx, err); err != nil { -// return err -// } -// log.Error(err, "Vector Config has error") -// return nil -// } -// if err != nil { -// return err -// } - -// return cfg.vaCtrl.SetSucceesStatus(cfg.Ctx) -// } - func (cc *ConfigCheck) Run() error { log := log.FromContext(context.TODO()).WithValues("Vector ConfigCheck", cc.Name) diff --git a/controllers/factory/utils/hash/hash_test.go b/controllers/factory/utils/hash/hash_test.go index 35ac2f68..d3c8196d 100644 --- a/controllers/factory/utils/hash/hash_test.go +++ b/controllers/factory/utils/hash/hash_test.go @@ -54,7 +54,7 @@ func TestGet(t *testing.T) { }, } - t.Parallel() + // t.Parallel() for _, tc := range testCases { t.Run(tc.name, hashCase(tc.bytes, tc.want)) } From f01b3792f78691538c3c3d048dc6a20e65be2885 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Mon, 31 Oct 2022 13:43:29 +0300 Subject: [PATCH 053/316] gc for checkconfig (#26) --- .../factory/config/configcheck/configcheck.go | 54 +++++++++++++++++++ controllers/vectorpipeline_controller.go | 3 ++ 2 files changed, 57 insertions(+) diff --git a/controllers/factory/config/configcheck/configcheck.go b/controllers/factory/config/configcheck/configcheck.go index 62e6f9e1..633dba4c 100644 --- a/controllers/factory/config/configcheck/configcheck.go +++ b/controllers/factory/config/configcheck/configcheck.go @@ -23,6 +23,7 @@ import ( "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/client-go/kubernetes" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" @@ -107,6 +108,10 @@ func (cc *ConfigCheck) checkVectorConfigCheckPod() error { return err } + err = cc.cleanup() + if err != nil { + return err + } return nil } @@ -162,3 +167,52 @@ func (cc *ConfigCheck) getCheckResult(pod *corev1.Pod) error { } } } + +func (cc *ConfigCheck) cleanup() error { + listOpts, err := cc.gcRListOptions() + if err != nil { + return err + } + + podlist := corev1.PodList{} + secretList := corev1.SecretList{} + err = cc.Client.List(cc.Ctx, &podlist, &listOpts) + if err != nil { + return err + } + err = cc.Client.List(cc.Ctx, &secretList, &listOpts) + if err != nil { + return err + } + for _, pod := range podlist.Items { + if pod.Status.Phase == "Succeeded" { + if err := cc.Client.Delete(cc.Ctx, &pod); err != nil { + return err + } + } + } + for _, secret := range secretList.Items { + if err := cc.Client.Delete(cc.Ctx, &secret); err != nil { + return err + } + } + return nil +} + +func (cc *ConfigCheck) gcRListOptions() (client.ListOptions, error) { + configCheckLabels := labelsForVectorConfigCheck() + var requirements []labels.Requirement + for k, v := range configCheckLabels { + r, err := labels.NewRequirement(k, "==", []string{v}) + if err != nil { + return client.ListOptions{}, err + } + requirements = append(requirements, *r) + } + labelsSelector := labels.NewSelector().Add(requirements...) + + return client.ListOptions{ + LabelSelector: labelsSelector, + Namespace: cc.Namespace, + }, nil +} diff --git a/controllers/vectorpipeline_controller.go b/controllers/vectorpipeline_controller.go index b542c2d0..9338c1a0 100644 --- a/controllers/vectorpipeline_controller.go +++ b/controllers/vectorpipeline_controller.go @@ -106,6 +106,9 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque // Init Controller for Vector Agent vaCtrl := vectoragent.NewController(&vector, r.Client, r.Clientset) + if vaCtrl.Vector.Spec.Agent.DataDir == "" { + vaCtrl.Vector.Spec.Agent.DataDir = "/vector-data-dir" + } // Get Vector Config file config, err := config.New(ctx, vaCtrl) From d2e255961dd826a7123dd87a7394df0674fdd8b9 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Mon, 31 Oct 2022 16:30:02 +0300 Subject: [PATCH 054/316] restruct vector api field && Change name printedColumn to Valid Signed-off-by: Zemtsov Vladimir --- api/v1alpha1/clustervectorpipeline_types.go | 2 +- api/v1alpha1/vector_types.go | 11 ++++++-- api/v1alpha1/vectorpipeline_types.go | 2 +- api/v1alpha1/zz_generated.deepcopy.go | 20 +++++++++++++ ...ity.kaasops.io_clustervectorpipelines.yaml | 2 +- ...ervability.kaasops.io_vectorpipelines.yaml | 2 +- .../observability.kaasops.io_vectors.yaml | 15 ++++++++-- controllers/factory/config/config_build.go | 2 +- controllers/factory/vector/types.go | 20 ++++++------- controllers/factory/vector/vector.go | 9 +++--- .../vector/vectoragent/vectoragent_default.go | 28 +++++++++++++++++++ controllers/vector_controller.go | 6 ++-- 12 files changed, 89 insertions(+), 30 deletions(-) create mode 100644 controllers/factory/vector/vectoragent/vectoragent_default.go diff --git a/api/v1alpha1/clustervectorpipeline_types.go b/api/v1alpha1/clustervectorpipeline_types.go index 2a3c088e..41fe3883 100644 --- a/api/v1alpha1/clustervectorpipeline_types.go +++ b/api/v1alpha1/clustervectorpipeline_types.go @@ -27,7 +27,7 @@ import ( //+kubebuilder:subresource:status //+kubebuilder:resource:shortName=cvp //+kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" -//+kubebuilder:printcolumn:name="Status",type="boolean",JSONPath=".status.configCheckResult" +//+kubebuilder:printcolumn:name="Valid",type="boolean",JSONPath=".status.configCheckResult" // ClusterVectorPipeline is the Schema for the clustervectorpipelines API type ClusterVectorPipeline struct { diff --git a/api/v1alpha1/vector_types.go b/api/v1alpha1/vector_types.go index a2cc0249..60b7b464 100644 --- a/api/v1alpha1/vector_types.go +++ b/api/v1alpha1/vector_types.go @@ -46,11 +46,18 @@ type VectorAgent struct { // +kubebuilder:default:="timberio/vector:0.24.0-distroless-libc" Image string `json:"image,omitempty"` DataDir string `json:"dataDir,omitempty"` - ApiEnabled bool `json:"ApiEnabled,omitempty"` + Api *ApiSpec `json:"api,omitempty"` Service bool `json:"service,omitempty"` Tolerations []v1.Toleration `json:"tolerations,omitempty"` } +// ApiSpec is the Schema for the Vector Agent GraphQL API - https://vector.dev/docs/reference/api/ +type ApiSpec struct { + Address string `json:"address,omitempty"` + Enabled bool `json:"enabled,omitempty"` + Playground bool `json:"playground,omitempty"` +} + // VectorAggregator is the Schema for the Vector Aggregator type VectorAggregator struct { Enable bool `json:"enable,omitempty"` @@ -62,7 +69,7 @@ type VectorAggregator struct { //+kubebuilder:object:root=true //+kubebuilder:subresource:status //+kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" -//+kubebuilder:printcolumn:name="Status",type="boolean",JSONPath=".status.configCheckResult" +//+kubebuilder:printcolumn:name="Valid",type="boolean",JSONPath=".status.configCheckResult" // Vector is the Schema for the vectors API type Vector struct { diff --git a/api/v1alpha1/vectorpipeline_types.go b/api/v1alpha1/vectorpipeline_types.go index b65340f3..8365e525 100644 --- a/api/v1alpha1/vectorpipeline_types.go +++ b/api/v1alpha1/vectorpipeline_types.go @@ -45,7 +45,7 @@ type VectorPipelineStatus struct { //+kubebuilder:subresource:status //+kubebuilder:resource:shortName=vp //+kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" -//+kubebuilder:printcolumn:name="Status",type="boolean",JSONPath=".status.configCheckResult" +//+kubebuilder:printcolumn:name="Valid",type="boolean",JSONPath=".status.configCheckResult" // VectorPipeline is the Schema for the vectorpipelines API type VectorPipeline struct { diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 746cbf3f..cb8f5176 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -26,6 +26,21 @@ import ( "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ApiSpec) DeepCopyInto(out *ApiSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApiSpec. +func (in *ApiSpec) DeepCopy() *ApiSpec { + if in == nil { + return nil + } + out := new(ApiSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterVectorPipeline) DeepCopyInto(out *ClusterVectorPipeline) { *out = *in @@ -115,6 +130,11 @@ func (in *Vector) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VectorAgent) DeepCopyInto(out *VectorAgent) { *out = *in + if in.Api != nil { + in, out := &in.Api, &out.Api + *out = new(ApiSpec) + **out = **in + } if in.Tolerations != nil { in, out := &in.Tolerations, &out.Tolerations *out = make([]v1.Toleration, len(*in)) diff --git a/config/crd/bases/observability.kaasops.io_clustervectorpipelines.yaml b/config/crd/bases/observability.kaasops.io_clustervectorpipelines.yaml index f22f7cbe..715b8145 100644 --- a/config/crd/bases/observability.kaasops.io_clustervectorpipelines.yaml +++ b/config/crd/bases/observability.kaasops.io_clustervectorpipelines.yaml @@ -22,7 +22,7 @@ spec: name: Age type: date - jsonPath: .status.configCheckResult - name: Status + name: Valid type: boolean name: v1alpha1 schema: diff --git a/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml b/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml index 0577b656..e40dd908 100644 --- a/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml +++ b/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml @@ -22,7 +22,7 @@ spec: name: Age type: date - jsonPath: .status.configCheckResult - name: Status + name: Valid type: boolean name: v1alpha1 schema: diff --git a/config/crd/bases/observability.kaasops.io_vectors.yaml b/config/crd/bases/observability.kaasops.io_vectors.yaml index e4f237ff..9783f7d7 100644 --- a/config/crd/bases/observability.kaasops.io_vectors.yaml +++ b/config/crd/bases/observability.kaasops.io_vectors.yaml @@ -20,7 +20,7 @@ spec: name: Age type: date - jsonPath: .status.configCheckResult - name: Status + name: Valid type: boolean name: v1alpha1 schema: @@ -46,8 +46,17 @@ spec: description: DisableAggregation DisableAggregation bool `json:"disableAggregation,omitempty"` Vector Agent properties: - ApiEnabled: - type: boolean + api: + description: ApiSpec is the Schema for the Vector Agent GraphQL + API - https://vector.dev/docs/reference/api/ + properties: + address: + type: string + enabled: + type: boolean + playground: + type: boolean + type: object dataDir: type: string image: diff --git a/controllers/factory/config/config_build.go b/controllers/factory/config/config_build.go index e5a8ce80..8a7bf82d 100644 --- a/controllers/factory/config/config_build.go +++ b/controllers/factory/config/config_build.go @@ -37,7 +37,7 @@ var ( ) func (cfg *Config) GenerateVectorConfig() error { - vectorConfig := vector.New(cfg.vaCtrl.Vector.Spec.Agent.DataDir, cfg.vaCtrl.Vector.Spec.Agent.ApiEnabled) + vectorConfig := vector.New(cfg.vaCtrl.Vector) sources, transforms, sinks, err := cfg.getComponents() if err != nil { diff --git a/controllers/factory/vector/types.go b/controllers/factory/vector/types.go index 3349dd19..52a58681 100644 --- a/controllers/factory/vector/types.go +++ b/controllers/factory/vector/types.go @@ -16,18 +16,16 @@ limitations under the License. package vector -type VectorConfig struct { - DataDir string `mapstructure:"data_dir"` - Api *ApiSpec `mapstructure:"api"` - Sources []Source `mapstructure:"sources"` - Transforms []Transform `mapstructure:"transforms"` - Sinks []Sink `mapstructure:"sinks"` -} +import ( + vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" +) -type ApiSpec struct { - Enabled *bool `json:"enabled,omitempty"` - Address *string `json:"address,omitempty"` - Playground *bool `json:"playground,omitempty"` +type VectorConfig struct { + DataDir string `mapstructure:"data_dir"` + Api *vectorv1alpha1.ApiSpec `mapstructure:"api"` + Sources []Source `mapstructure:"sources"` + Transforms []Transform `mapstructure:"transforms"` + Sinks []Sink `mapstructure:"sinks"` } type Source struct { diff --git a/controllers/factory/vector/vector.go b/controllers/factory/vector/vector.go index fbff5dbb..7002c8a5 100644 --- a/controllers/factory/vector/vector.go +++ b/controllers/factory/vector/vector.go @@ -17,18 +17,17 @@ limitations under the License. package vector import ( + vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" "github.com/mitchellh/mapstructure" ) -func New(dataDir string, apiEnabled bool) *VectorConfig { +func New(vector *vectorv1alpha1.Vector) *VectorConfig { sources := []Source{} sinks := []Sink{} return &VectorConfig{ - DataDir: dataDir, - Api: &ApiSpec{ - Enabled: &apiEnabled, - }, + DataDir: vector.Spec.Agent.DataDir, + Api: vector.Spec.Agent.Api, Sources: sources, Sinks: sinks, } diff --git a/controllers/factory/vector/vectoragent/vectoragent_default.go b/controllers/factory/vector/vectoragent/vectoragent_default.go new file mode 100644 index 00000000..5f760472 --- /dev/null +++ b/controllers/factory/vector/vectoragent/vectoragent_default.go @@ -0,0 +1,28 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package vectoragent + +func (ctrl *Controller) SetDefault() { + if ctrl.Vector.Spec.Agent.Api.Address == "" { + ctrl.Vector.Spec.Agent.Api.Address = "0.0.0.0:8686" + } + + if ctrl.Vector.Spec.Agent.DataDir == "" { + ctrl.Vector.Spec.Agent.DataDir = "/vector-data-dir" + } + +} diff --git a/controllers/vector_controller.go b/controllers/vector_controller.go index 18699f0a..f635b34f 100644 --- a/controllers/vector_controller.go +++ b/controllers/vector_controller.go @@ -70,10 +70,6 @@ func (r *VectorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr return ctrl.Result{}, nil } - if vectorCR.Spec.Agent.DataDir == "" { - vectorCR.Spec.Agent.DataDir = "/vector-data-dir" - } - return r.CreateOrUpdateVector(ctx, vectorCR) } @@ -102,6 +98,8 @@ func (r *VectorReconciler) CreateOrUpdateVector(ctx context.Context, v *vectorv1 // Init Controller for Vector Agent vaCtrl := vectoragent.NewController(v, r.Client, r.Clientset) + vaCtrl.SetDefault() + // Get Vector Config file config, err := config.New(ctx, vaCtrl) if err != nil { From d82838b8ec73b5ad8f55d6b55e8eb4ce1de71cb2 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Mon, 31 Oct 2022 16:30:13 +0300 Subject: [PATCH 055/316] Add docs Signed-off-by: Zemtsov Vladimir --- docs/design.md | 48 ++++++++++++++++++++++++++++++++++ docs/specification.md | 60 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 docs/design.md create mode 100644 docs/specification.md diff --git a/docs/design.md b/docs/design.md new file mode 100644 index 00000000..10dc27e1 --- /dev/null +++ b/docs/design.md @@ -0,0 +1,48 @@ +# Design +This document describes the design and interaction between the custom resource definitions (CRD) that the Vector Operator introduces. + +Operator introduces the following custom resources: +- [Vector](#Vector) +- [VectorPipeline](#vectorpipeline) +- [ClusterVectorPipeline](#clustervectorpipeline) + +# Vector +The `Vector` CRD declaratively defines a Vector installation to run in a Kubernetes cluster. +For each Vector resource, the Operator deploys a properly configured DaemonSet in the same namespace. +For each Vector resource, the Operator adds: +- DaemonSet with Vector +- Secret with Vector Configurtion file +- Service. (For connect to Vector API, or scrape Vector metrics, if enabled) +- ServiceAccount/ClusterRole/RoleBinding for get access to Kubernetes API + + +## Restrictions +- Currently tested only ONE installation Vector on Kubernetes cluster + +## Planned +- Add aggregator role in StatefullSet +- Add features for compress Vector configuration file. (Delete dublicates sources/Transforms/Sinks. Compress to gzip) + +## Specification +Specification access to [this]() page + + +# VectorPipeline +The `VectorPipeline` CRD defines Sources, Transforms and Sinks rules for Vector. +All `VectorPipelines`, with validated configuration file, added to Vector configuration file. + +## Restrictions +- For source available only [kubernetes_logs](https://vector.dev/docs/reference/configuration/sources/kubernetes_logs/) type +- For source field `extra_namespace_label_selector` cannot be installed. The operator control this field and sets the namespace there, where VectorPipeline is defined. + +## Specification +Specification access to [this]() page + +# ClusterVectorPipeline +The `ClusterVectorPipeline` CRD defines Sources, Transforms and Sinks rules for Vector. +All `ClusterVectorPipelines`, with validated configuration file, added to Vector configuration file. + +ClusterVectorPipelines works like VectorPipeline, but without restrictions. + +## Specification +Specification access to [this]() page \ No newline at end of file diff --git a/docs/specification.md b/docs/specification.md new file mode 100644 index 00000000..f4b717fb --- /dev/null +++ b/docs/specification.md @@ -0,0 +1,60 @@ +# Specification + +- [Vector](#vector-spec) +- VectorPipeline +- ClusterVectorPipeline + + + +# Vector Spec + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
agentimageImage for Vector agent. timberio/vector:0.24.0-distroless-libc by default
dataDirDataDir for Vector Agent. `/vector-data-dir` by default
api
addressThe network address to which the API should bind. If you’re running Vector in a Docker container, make sure to bind to 0.0.0.0. Otherwise the API will not be exposed outside the container. By default - 0.0.0.0:8686
enabledWhether the GraphQL API is enabled for this Vector instance. By default - false
playgroundWhether the GraphQL Playground is enabled for the API. The Playground is accessible via the /playground endpoint of the address set using the bind parameter. By default - false
serviceTemporary field for enabling service for Vector DaemonSet. By default - false
tolerationsTolerations for Vector DaemonSet. By default - nil
+ + +# VectorPipelineSpec (ClusterVectorPipelineSpec) + + + + + + + + + + + + + +
sourcesList of Sources
transformsList of Transforms
sinksList of Sinks
\ No newline at end of file From c39945a6a37db4a00e6c53f09ad0cbd6cebcacdf Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Mon, 31 Oct 2022 17:23:44 +0300 Subject: [PATCH 056/316] Add controllet components for Vector Agent DaemonSet Signed-off-by: Zemtsov Vladimir --- api/v1alpha1/vector_types.go | 54 +- api/v1alpha1/zz_generated.deepcopy.go | 32 +- .../observability.kaasops.io_vectors.yaml | 1124 +++++++++++++++++ controllers/factory/vector/vector.go | 2 +- .../vectoragent/vectoragent_daemonset.go | 10 +- .../vector/vectoragent/vectoragent_default.go | 19 + docs/design.md | 6 +- docs/specification.md | 46 +- 8 files changed, 1277 insertions(+), 16 deletions(-) diff --git a/api/v1alpha1/vector_types.go b/api/v1alpha1/vector_types.go index 60b7b464..c7d6cdc1 100644 --- a/api/v1alpha1/vector_types.go +++ b/api/v1alpha1/vector_types.go @@ -44,11 +44,57 @@ type VectorStatus struct { // VectorAgent is the Schema for the Vector Agent type VectorAgent struct { // +kubebuilder:default:="timberio/vector:0.24.0-distroless-libc" - Image string `json:"image,omitempty"` - DataDir string `json:"dataDir,omitempty"` - Api *ApiSpec `json:"api,omitempty"` - Service bool `json:"service,omitempty"` + // Image - docker image settings for Vector Agent + // if no specified operator uses default config version + // +optional + Image string `json:"image,omitempty"` + // ImagePullSecrets An optional list of references to secrets in the same namespace + // to use for pulling images from registries + // see http://kubernetes.io/docs/user-guide/images#specifying-imagepullsecrets-on-a-pod + // +optional + ImagePullSecrets []v1.LocalObjectReference `json:"imagePullSecrets,omitempty"` + // Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + // if not specified - default setting will be used + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Resources",xDescriptors="urn:alm:descriptor:com.tectonic.ui:resourceRequirements" + // +optional + Resources v1.ResourceRequirements `json:"resources,omitempty"` + // Affinity If specified, the pod's scheduling constraints. + // +optional + Affinity *v1.Affinity `json:"affinity,omitempty"` + // Tolerations If specified, the pod's tolerations. + // +optional Tolerations []v1.Toleration `json:"tolerations,omitempty"` + // SecurityContext holds pod-level security attributes and common container settings. + // This defaults to the default PodSecurityContext. + // +optional + // Tolerations If specified, the pod's tolerations. + // +optional + SecurityContext *v1.PodSecurityContext `json:"securityContext,omitempty"` + // SchedulerName - defines kubernetes scheduler name + // +optional + SchedulerName string `json:"schedulerName,omitempty"` + // RuntimeClassName - defines runtime class for kubernetes pod. + // https://kubernetes.io/docs/concepts/containers/runtime-class/ + RuntimeClassName *string `json:"runtimeClassName,omitempty"` + // HostAliases provides mapping between ip and hostnames, + // that would be propagated to pod, + // cannot be used with HostNetwork. + // +optional + HostAliases []v1.HostAlias `json:"host_aliases,omitempty"` + // PodSecurityPolicyName - defines name for podSecurityPolicy + // in case of empty value, prefixedName will be used. + // +optional + PodSecurityPolicyName string `json:"podSecurityPolicyName,omitempty"` + // PriorityClassName assigned to the Pods + // +optional + PriorityClassName string `json:"priorityClassName,omitempty"` + // HostNetwork controls whether the pod may use the node network namespace + // +optional + HostNetwork bool `json:"hostNetwork,omitempty"` + + DataDir string `json:"dataDir,omitempty"` + Api ApiSpec `json:"api,omitempty"` + Service bool `json:"service,omitempty"` } // ApiSpec is the Schema for the Vector Agent GraphQL API - https://vector.dev/docs/reference/api/ diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index cb8f5176..c0fa7efc 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -130,10 +130,16 @@ func (in *Vector) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VectorAgent) DeepCopyInto(out *VectorAgent) { *out = *in - if in.Api != nil { - in, out := &in.Api, &out.Api - *out = new(ApiSpec) - **out = **in + if in.ImagePullSecrets != nil { + in, out := &in.ImagePullSecrets, &out.ImagePullSecrets + *out = make([]v1.LocalObjectReference, len(*in)) + copy(*out, *in) + } + in.Resources.DeepCopyInto(&out.Resources) + if in.Affinity != nil { + in, out := &in.Affinity, &out.Affinity + *out = new(v1.Affinity) + (*in).DeepCopyInto(*out) } if in.Tolerations != nil { in, out := &in.Tolerations, &out.Tolerations @@ -142,6 +148,24 @@ func (in *VectorAgent) DeepCopyInto(out *VectorAgent) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.SecurityContext != nil { + in, out := &in.SecurityContext, &out.SecurityContext + *out = new(v1.PodSecurityContext) + (*in).DeepCopyInto(*out) + } + if in.RuntimeClassName != nil { + in, out := &in.RuntimeClassName, &out.RuntimeClassName + *out = new(string) + **out = **in + } + if in.HostAliases != nil { + in, out := &in.HostAliases, &out.HostAliases + *out = make([]v1.HostAlias, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + out.Api = in.Api } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VectorAgent. diff --git a/config/crd/bases/observability.kaasops.io_vectors.yaml b/config/crd/bases/observability.kaasops.io_vectors.yaml index 9783f7d7..807da448 100644 --- a/config/crd/bases/observability.kaasops.io_vectors.yaml +++ b/config/crd/bases/observability.kaasops.io_vectors.yaml @@ -46,6 +46,874 @@ spec: description: DisableAggregation DisableAggregation bool `json:"disableAggregation,omitempty"` Vector Agent properties: + affinity: + description: Affinity If specified, the pod's scheduling constraints. + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for + the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods + to nodes that satisfy the affinity expressions specified + by this field, but it may choose a node that violates + one or more of the expressions. The node that is most + preferred is the one with the greatest sum of weights, + i.e. for each node that meets all of the scheduling + requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating + through the elements of this field and adding "weight" + to the sum if the node matches the corresponding matchExpressions; + the node(s) with the highest sum are the most preferred. + items: + description: An empty preferred scheduling term matches + all objects with implicit weight 0 (i.e. it's a no-op). + A null preferred scheduling term matches no objects + (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated with + the corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + x-kubernetes-map-type: atomic + weight: + description: Weight associated with matching the + corresponding nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by + this field are not met at scheduling time, the pod will + not be scheduled onto the node. If the affinity requirements + specified by this field cease to be met at some point + during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from + its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. + The terms are ORed. + items: + description: A null or empty node selector term + matches no objects. The requirements of them are + ANDed. The TopologySelectorTerm type implements + a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + x-kubernetes-map-type: atomic + type: array + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. + co-locate this pod in the same node, zone, etc. as some + other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods + to nodes that satisfy the affinity expressions specified + by this field, but it may choose a node that violates + one or more of the expressions. The node that is most + preferred is the one with the greatest sum of weights, + i.e. for each node that meets all of the scheduling + requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating + through the elements of this field and adding "weight" + to the sum if the node has pods which matches the corresponding + podAffinityTerm; the node(s) with the highest sum are + the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by + this field and the ones listed in the namespaces + field. null selector and null or empty namespaces + list means "this pod's namespace". An empty + selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. + The term is applied to the union of the namespaces + listed in this field and the ones selected + by namespaceSelector. null or empty namespaces + list and null namespaceSelector means "this + pod's namespace". + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the + pods matching the labelSelector in the specified + namespaces, where co-located is defined as + running on a node whose value of the label + with key topologyKey matches that of any node + on which any of the selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the + corresponding podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by + this field are not met at scheduling time, the pod will + not be scheduled onto the node. If the affinity requirements + specified by this field cease to be met at some point + during pod execution (e.g. due to a pod label update), + the system may or may not try to eventually evict the + pod from its node. When there are multiple elements, + the lists of nodes corresponding to each podAffinityTerm + are intersected, i.e. all terms must be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not + co-located (anti-affinity) with, where co-located + is defined as running on a node whose value of the + label with key matches that of any node + on which a pod of the set of pods is running + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by this + field and the ones listed in the namespaces field. + null selector and null or empty namespaces list + means "this pod's namespace". An empty selector + ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. The + term is applied to the union of the namespaces + listed in this field and the ones selected by + namespaceSelector. null or empty namespaces list + and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey + matches that of any node on which any of the selected + pods is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules + (e.g. avoid putting this pod in the same node, zone, etc. + as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods + to nodes that satisfy the anti-affinity expressions + specified by this field, but it may choose a node that + violates one or more of the expressions. The node that + is most preferred is the one with the greatest sum of + weights, i.e. for each node that meets all of the scheduling + requirements (resource request, requiredDuringScheduling + anti-affinity expressions, etc.), compute a sum by iterating + through the elements of this field and adding "weight" + to the sum if the node has pods which matches the corresponding + podAffinityTerm; the node(s) with the highest sum are + the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by + this field and the ones listed in the namespaces + field. null selector and null or empty namespaces + list means "this pod's namespace". An empty + selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. + The term is applied to the union of the namespaces + listed in this field and the ones selected + by namespaceSelector. null or empty namespaces + list and null namespaceSelector means "this + pod's namespace". + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the + pods matching the labelSelector in the specified + namespaces, where co-located is defined as + running on a node whose value of the label + with key topologyKey matches that of any node + on which any of the selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the + corresponding podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified + by this field are not met at scheduling time, the pod + will not be scheduled onto the node. If the anti-affinity + requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod + label update), the system may or may not try to eventually + evict the pod from its node. When there are multiple + elements, the lists of nodes corresponding to each podAffinityTerm + are intersected, i.e. all terms must be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not + co-located (anti-affinity) with, where co-located + is defined as running on a node whose value of the + label with key matches that of any node + on which a pod of the set of pods is running + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by this + field and the ones listed in the namespaces field. + null selector and null or empty namespaces list + means "this pod's namespace". An empty selector + ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. The + term is applied to the union of the namespaces + listed in this field and the ones selected by + namespaceSelector. null or empty namespaces list + and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey + matches that of any node on which any of the selected + pods is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object api: description: ApiSpec is the Schema for the Vector Agent GraphQL API - https://vector.dev/docs/reference/api/ @@ -59,12 +927,268 @@ spec: type: object dataDir: type: string + host_aliases: + description: HostAliases provides mapping between ip and hostnames, + that would be propagated to pod, cannot be used with HostNetwork. + items: + description: HostAlias holds the mapping between IP and hostnames + that will be injected as an entry in the pod's hosts file. + properties: + hostnames: + description: Hostnames for the above IP address. + items: + type: string + type: array + ip: + description: IP address of the host file entry. + type: string + type: object + type: array + hostNetwork: + description: HostNetwork controls whether the pod may use the + node network namespace + type: boolean image: default: timberio/vector:0.24.0-distroless-libc + description: Image - docker image settings for Vector Agent if + no specified operator uses default config version type: string + imagePullSecrets: + description: ImagePullSecrets An optional list of references to + secrets in the same namespace to use for pulling images from + registries see http://kubernetes.io/docs/user-guide/images#specifying-imagepullsecrets-on-a-pod + items: + description: LocalObjectReference contains enough information + to let you locate the referenced object inside the same namespace. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic + type: array + podSecurityPolicyName: + description: PodSecurityPolicyName - defines name for podSecurityPolicy + in case of empty value, prefixedName will be used. + type: string + priorityClassName: + description: PriorityClassName assigned to the Pods + type: string + resources: + description: Resources container resource request and limits, + https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + if not specified - default setting will be used + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, otherwise + to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + runtimeClassName: + description: RuntimeClassName - defines runtime class for kubernetes + pod. https://kubernetes.io/docs/concepts/containers/runtime-class/ + type: string + schedulerName: + description: SchedulerName - defines kubernetes scheduler name + type: string + securityContext: + description: PodSecurityContext holds pod-level security attributes + and common container settings. Some fields are also present + in container.securityContext. Field values of container.securityContext + take precedence over field values of PodSecurityContext. + properties: + fsGroup: + description: "A special supplemental group that applies to + all containers in a pod. Some volume types allow the Kubelet + to change the ownership of that volume to be owned by the + pod: \n 1. The owning GID will be the FSGroup 2. The setgid + bit is set (new files created in the volume will be owned + by FSGroup) 3. The permission bits are OR'd with rw-rw---- + \n If unset, the Kubelet will not modify the ownership and + permissions of any volume. Note that this field cannot be + set when spec.os.name is windows." + format: int64 + type: integer + fsGroupChangePolicy: + description: 'fsGroupChangePolicy defines behavior of changing + ownership and permission of the volume before being exposed + inside Pod. This field will only apply to volume types which + support fsGroup based ownership(and permissions). It will + have no effect on ephemeral volume types such as: secret, + configmaps and emptydir. Valid values are "OnRootMismatch" + and "Always". If not specified, "Always" is used. Note that + this field cannot be set when spec.os.name is windows.' + type: string + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be set + in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence for that container. Note that this field + cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a non-root + user. If true, the Kubelet will validate the image at runtime + to ensure that it does not run as UID 0 (root) and fail + to start the container if it does. If unset or false, no + such validation will be performed. May also be set in SecurityContext. If + set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata if + unspecified. May also be set in SecurityContext. If set + in both SecurityContext and PodSecurityContext, the value + specified in SecurityContext takes precedence for that container. + Note that this field cannot be set when spec.os.name is + windows. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to all containers. + If unspecified, the container runtime will allocate a random + SELinux context for each container. May also be set in + SecurityContext. If set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence + for that container. Note that this field cannot be set when + spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by the containers + in this pod. Note that this field cannot be set when spec.os.name + is windows. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile must + be preconfigured on the node to work. Must be a descending + path, relative to the kubelet's configured seccomp profile + location. Must only be set if type is "Localhost". + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - a + profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile + should be used. Unconfined - no profile should be applied." + type: string + required: + - type + type: object + supplementalGroups: + description: A list of groups applied to the first process + run in each container, in addition to the container's primary + GID. If unspecified, no groups will be added to any container. + Note that this field cannot be set when spec.os.name is + windows. + items: + format: int64 + type: integer + type: array + sysctls: + description: Sysctls hold a list of namespaced sysctls used + for the pod. Pods with unsupported sysctls (by the container + runtime) might fail to launch. Note that this field cannot + be set when spec.os.name is windows. + items: + description: Sysctl defines a kernel parameter to be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + description: The Windows specific settings applied to all + containers. If unspecified, the options within a container's + SecurityContext will be used. If set in both SecurityContext + and PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named + by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. This field is + alpha-level and will only be honored by components that + enable the WindowsHostProcessContainers feature flag. + Setting this field without the feature flag will result + in errors when validating the Pod. All of a Pod's containers + must have the same effective HostProcess value (it is + not allowed to have a mix of HostProcess containers + and non-HostProcess containers). In addition, if HostProcess + is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set in + PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. + type: string + type: object + type: object service: type: boolean tolerations: + description: SecurityContext holds pod-level security attributes + and common container settings. This defaults to the default + PodSecurityContext. Tolerations If specified, the pod's tolerations. items: description: The pod this Toleration is attached to tolerates any taint that matches the triple using diff --git a/controllers/factory/vector/vector.go b/controllers/factory/vector/vector.go index 7002c8a5..139d458c 100644 --- a/controllers/factory/vector/vector.go +++ b/controllers/factory/vector/vector.go @@ -27,7 +27,7 @@ func New(vector *vectorv1alpha1.Vector) *VectorConfig { return &VectorConfig{ DataDir: vector.Spec.Agent.DataDir, - Api: vector.Spec.Agent.Api, + Api: &vector.Spec.Agent.Api, Sources: sources, Sinks: sinks, } diff --git a/controllers/factory/vector/vectoragent/vectoragent_daemonset.go b/controllers/factory/vector/vectoragent/vectoragent_daemonset.go index e551b8df..6351b703 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_daemonset.go +++ b/controllers/factory/vector/vectoragent/vectoragent_daemonset.go @@ -34,8 +34,15 @@ func (ctrl *Controller) createVectorAgentDaemonSet() *appsv1.DaemonSet { Spec: corev1.PodSpec{ ServiceAccountName: ctrl.getNameVectorAgent(), Volumes: ctrl.generateVectorAgentVolume(), - SecurityContext: &corev1.PodSecurityContext{}, + SecurityContext: ctrl.Vector.Spec.Agent.SecurityContext, + ImagePullSecrets: ctrl.Vector.Spec.Agent.ImagePullSecrets, + Affinity: ctrl.Vector.Spec.Agent.Affinity, + RuntimeClassName: ctrl.Vector.Spec.Agent.RuntimeClassName, + SchedulerName: ctrl.Vector.Spec.Agent.SchedulerName, Tolerations: ctrl.Vector.Spec.Agent.Tolerations, + PriorityClassName: ctrl.Vector.Spec.Agent.PodSecurityPolicyName, + HostNetwork: ctrl.Vector.Spec.Agent.HostNetwork, + HostAliases: ctrl.Vector.Spec.Agent.HostAliases, Containers: []corev1.Container{ { Name: ctrl.getNameVectorAgent(), @@ -50,6 +57,7 @@ func (ctrl *Controller) createVectorAgentDaemonSet() *appsv1.DaemonSet { }, }, VolumeMounts: ctrl.generateVectorAgentVolumeMounts(), + Resources: ctrl.Vector.Spec.Agent.Resources, SecurityContext: &corev1.SecurityContext{}, }, }, diff --git a/controllers/factory/vector/vectoragent/vectoragent_default.go b/controllers/factory/vector/vectoragent/vectoragent_default.go index 5f760472..fefeddaa 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_default.go +++ b/controllers/factory/vector/vectoragent/vectoragent_default.go @@ -16,7 +16,26 @@ limitations under the License. package vectoragent +import ( + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" +) + func (ctrl *Controller) SetDefault() { + if ctrl.Vector.Spec.Agent.Image == "" { + ctrl.Vector.Spec.Agent.Image = "timberio/vector:0.24.0-distroless-libc" + } + + if ctrl.Vector.Spec.Agent.Resources.Requests == nil { + ctrl.Vector.Spec.Agent.Resources.Requests[corev1.ResourceMemory] = resource.MustParse("200Mi") + ctrl.Vector.Spec.Agent.Resources.Requests[corev1.ResourceCPU] = resource.MustParse("100m") + } + + if ctrl.Vector.Spec.Agent.Resources.Limits == nil { + ctrl.Vector.Spec.Agent.Resources.Limits[corev1.ResourceMemory] = resource.MustParse("1000Mi") + ctrl.Vector.Spec.Agent.Resources.Limits[corev1.ResourceCPU] = resource.MustParse("1000m") + } + if ctrl.Vector.Spec.Agent.Api.Address == "" { ctrl.Vector.Spec.Agent.Api.Address = "0.0.0.0:8686" } diff --git a/docs/design.md b/docs/design.md index 10dc27e1..1c75b8e5 100644 --- a/docs/design.md +++ b/docs/design.md @@ -24,7 +24,7 @@ For each Vector resource, the Operator adds: - Add features for compress Vector configuration file. (Delete dublicates sources/Transforms/Sinks. Compress to gzip) ## Specification -Specification access to [this]() page +Specification access to [this](https://github.com/kaasops/vector-operator/blob/main/docs/specification.md#vector-spec) page # VectorPipeline @@ -36,7 +36,7 @@ All `VectorPipelines`, with validated configuration file, added to Vector config - For source field `extra_namespace_label_selector` cannot be installed. The operator control this field and sets the namespace there, where VectorPipeline is defined. ## Specification -Specification access to [this]() page +Specification access to [this](https://github.com/kaasops/vector-operator/blob/main/docs/specification.md#vectorpipelinespec-clustervectorpipelinespec) page # ClusterVectorPipeline The `ClusterVectorPipeline` CRD defines Sources, Transforms and Sinks rules for Vector. @@ -45,4 +45,4 @@ All `ClusterVectorPipelines`, with validated configuration file, added to Vector ClusterVectorPipelines works like VectorPipeline, but without restrictions. ## Specification -Specification access to [this]() page \ No newline at end of file +Specification access to [this](https://github.com/kaasops/vector-operator/blob/main/docs/specification.md#vectorpipelinespec-clustervectorpipelinespec) page \ No newline at end of file diff --git a/docs/specification.md b/docs/specification.md index f4b717fb..49776e5f 100644 --- a/docs/specification.md +++ b/docs/specification.md @@ -1,15 +1,15 @@ # Specification - [Vector](#vector-spec) -- VectorPipeline -- ClusterVectorPipeline +- [VectorPipeline](#vectorpipelinespec-clustervectorpipelinespec) +- [ClusterVectorPipeline](#vectorpipelinespec-clustervectorpipelinespec) # Vector Spec - + @@ -36,10 +36,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
agentagent image Image for Vector agent. timberio/vector:0.24.0-distroless-libc by default
service Temporary field for enabling service for Vector DaemonSet. By default - false
imagePullSecretsImagePullSecrets An optional list of references to secrets in the same namespace to use for pulling images from registries. By default not set
resourcesResources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/. If not specified - default setting will be used
affinityAffinity If specified, the pod's scheduling constraints. By default not set
tolerations Tolerations for Vector DaemonSet. By default - nil
securityContextSecurityContext holds pod-level security attributes and common container settings. By default - not set
schedulerNameSchedulerName - defines kubernetes scheduler name. By default - not set
runtimeClassNameRuntimeClassName - defines runtime class for kubernetes pod.. By default - not set
hostAliasesHostAliases provides mapping between ip and hostnames, that would be propagated to pod.
podSecurityPolicyNamePodSecurityPolicyName - defines name for podSecurityPolicy in case of empty value, prefixedName will be used.
priorityClassNamePriorityClassName assigned to the Pods.
hostNetworkHostNetwork controls whether the pod may use the node network namespace.
From c4a673355f3b08187842750d03cbc541709989bd Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Mon, 31 Oct 2022 17:40:47 +0300 Subject: [PATCH 057/316] Add field env to Vector spec Signed-off-by: Zemtsov Vladimir --- api/v1alpha1/vector_types.go | 2 + api/v1alpha1/zz_generated.deepcopy.go | 7 + .../observability.kaasops.io_vectors.yaml | 125 +++++++++++++++++- docs/specification.md | 42 +++--- 4 files changed, 154 insertions(+), 22 deletions(-) diff --git a/api/v1alpha1/vector_types.go b/api/v1alpha1/vector_types.go index c7d6cdc1..9992dab2 100644 --- a/api/v1alpha1/vector_types.go +++ b/api/v1alpha1/vector_types.go @@ -91,6 +91,8 @@ type VectorAgent struct { // HostNetwork controls whether the pod may use the node network namespace // +optional HostNetwork bool `json:"hostNetwork,omitempty"` + // Env that will be added to Vector pod + Env []v1.EnvVar `json:"env,omitempty"` DataDir string `json:"dataDir,omitempty"` Api ApiSpec `json:"api,omitempty"` diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index c0fa7efc..52a62c40 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -165,6 +165,13 @@ func (in *VectorAgent) DeepCopyInto(out *VectorAgent) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.Env != nil { + in, out := &in.Env, &out.Env + *out = make([]v1.EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } out.Api = in.Api } diff --git a/config/crd/bases/observability.kaasops.io_vectors.yaml b/config/crd/bases/observability.kaasops.io_vectors.yaml index 807da448..1bc130b3 100644 --- a/config/crd/bases/observability.kaasops.io_vectors.yaml +++ b/config/crd/bases/observability.kaasops.io_vectors.yaml @@ -927,6 +927,120 @@ spec: type: object dataDir: type: string + env: + description: Env that will be added to Vector pod + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. If + a variable cannot be resolved, the reference in the input + string will be unchanged. Double $$ are reduced to a single + $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless + of whether the variable exists or not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.podIP, + status.podIPs.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.ephemeral-storage) are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array host_aliases: description: HostAliases provides mapping between ip and hostnames, that would be propagated to pod, cannot be used with HostNetwork. @@ -1011,10 +1125,9 @@ spec: description: SchedulerName - defines kubernetes scheduler name type: string securityContext: - description: PodSecurityContext holds pod-level security attributes - and common container settings. Some fields are also present - in container.securityContext. Field values of container.securityContext - take precedence over field values of PodSecurityContext. + description: SecurityContext holds pod-level security attributes + and common container settings. This defaults to the default + PodSecurityContext. Tolerations If specified, the pod's tolerations. properties: fsGroup: description: "A special supplemental group that applies to @@ -1186,9 +1299,7 @@ spec: service: type: boolean tolerations: - description: SecurityContext holds pod-level security attributes - and common container settings. This defaults to the default - PodSecurityContext. Tolerations If specified, the pod's tolerations. + description: Tolerations If specified, the pod's tolerations. items: description: The pod this Toleration is attached to tolerates any taint that matches the triple using diff --git a/docs/specification.md b/docs/specification.md index 49776e5f..aa67c9b5 100644 --- a/docs/specification.md +++ b/docs/specification.md @@ -9,7 +9,7 @@ # Vector Spec - + @@ -18,19 +18,8 @@ - - - - - - - - - - - - - + + @@ -62,7 +51,7 @@ - + @@ -80,6 +69,29 @@ + + + + +
agentagent image Image for Vector agent. timberio/vector:0.24.0-distroless-libc by default
DataDir for Vector Agent. `/vector-data-dir` by default
api
addressThe network address to which the API should bind. If you’re running Vector in a Docker container, make sure to bind to 0.0.0.0. Otherwise the API will not be exposed outside the container. By default - 0.0.0.0:8686
enabledWhether the GraphQL API is enabled for this Vector instance. By default - false
playgroundWhether the GraphQL Playground is enabled for the API. The Playground is accessible via the /playground endpoint of the address set using the bind parameter. By default - falseapiApiSpec
service
runtimeClassNameRuntimeClassName - defines runtime class for kubernetes pod.. By default - not setRuntimeClassName - defines runtime class for kubernetes pod. By default - not set
hostAliaseshostNetwork HostNetwork controls whether the pod may use the node network namespace.
envEnv that will be added to Vector pod. By default - not set
+ +## ApiSpec + + + + + + + + + + + + + + + +
api
addressThe network address to which the API should bind. If you’re running Vector in a Docker container, make sure to bind to 0.0.0.0. Otherwise the API will not be exposed outside the container. By default - 0.0.0.0:8686
enabledWhether the GraphQL API is enabled for this Vector instance. By default - false
playgroundWhether the GraphQL Playground is enabled for the API. The Playground is accessible via the /playground endpoint of the address set using the bind parameter. By default - false
From 1786d69fa87adc47cd3a66689e65df890bf08202 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Mon, 31 Oct 2022 17:41:32 +0300 Subject: [PATCH 058/316] docs: Cleanup specification Signed-off-by: Zemtsov Vladimir --- docs/specification.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/specification.md b/docs/specification.md index aa67c9b5..0556d071 100644 --- a/docs/specification.md +++ b/docs/specification.md @@ -75,7 +75,7 @@ -## ApiSpec +## Api Spec From ab5b24c2848fb1e23bc4809f834b0240a4915a04 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Mon, 31 Oct 2022 17:51:19 +0300 Subject: [PATCH 059/316] fix configcheck to delete pod-secret pairs (#27) --- .../factory/config/configcheck/configcheck.go | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/controllers/factory/config/configcheck/configcheck.go b/controllers/factory/config/configcheck/configcheck.go index 633dba4c..98565ae3 100644 --- a/controllers/factory/config/configcheck/configcheck.go +++ b/controllers/factory/config/configcheck/configcheck.go @@ -24,6 +24,7 @@ import ( "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" @@ -175,27 +176,29 @@ func (cc *ConfigCheck) cleanup() error { } podlist := corev1.PodList{} - secretList := corev1.SecretList{} err = cc.Client.List(cc.Ctx, &podlist, &listOpts) if err != nil { return err } - err = cc.Client.List(cc.Ctx, &secretList, &listOpts) - if err != nil { - return err - } for _, pod := range podlist.Items { if pod.Status.Phase == "Succeeded" { + for _, v := range pod.Spec.Volumes { + if v.Name == "config" { + secret := &corev1.Secret{} + secretName := v.Secret.SecretName + if err := cc.Client.Get(cc.Ctx, types.NamespacedName{Name: secretName, Namespace: pod.Namespace}, secret); err != nil { + return err + } + if err := cc.Client.Delete(cc.Ctx, secret); err != nil { + return err + } + } + } if err := cc.Client.Delete(cc.Ctx, &pod); err != nil { return err } } } - for _, secret := range secretList.Items { - if err := cc.Client.Delete(cc.Ctx, &secret); err != nil { - return err - } - } return nil } From d6f32dd64dde1729146cfce1e930438c0a9eccfa Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Mon, 31 Oct 2022 17:58:19 +0300 Subject: [PATCH 060/316] Vector: Fix error if field resource not set Signed-off-by: Zemtsov Vladimir --- .../vector/vectoragent/vectoragent_default.go | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/controllers/factory/vector/vectoragent/vectoragent_default.go b/controllers/factory/vector/vectoragent/vectoragent_default.go index fefeddaa..4d0c80e5 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_default.go +++ b/controllers/factory/vector/vectoragent/vectoragent_default.go @@ -18,7 +18,7 @@ package vectoragent import ( corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/resource" + resourcev1 "k8s.io/apimachinery/pkg/api/resource" ) func (ctrl *Controller) SetDefault() { @@ -27,13 +27,16 @@ func (ctrl *Controller) SetDefault() { } if ctrl.Vector.Spec.Agent.Resources.Requests == nil { - ctrl.Vector.Spec.Agent.Resources.Requests[corev1.ResourceMemory] = resource.MustParse("200Mi") - ctrl.Vector.Spec.Agent.Resources.Requests[corev1.ResourceCPU] = resource.MustParse("100m") + ctrl.Vector.Spec.Agent.Resources.Requests = corev1.ResourceList{ + corev1.ResourceMemory: resourcev1.MustParse("200Mi"), + corev1.ResourceCPU: resourcev1.MustParse("100m"), + } } - if ctrl.Vector.Spec.Agent.Resources.Limits == nil { - ctrl.Vector.Spec.Agent.Resources.Limits[corev1.ResourceMemory] = resource.MustParse("1000Mi") - ctrl.Vector.Spec.Agent.Resources.Limits[corev1.ResourceCPU] = resource.MustParse("1000m") + ctrl.Vector.Spec.Agent.Resources.Limits = corev1.ResourceList{ + corev1.ResourceMemory: resourcev1.MustParse("1024Mi"), + corev1.ResourceCPU: resourcev1.MustParse("1000m"), + } } if ctrl.Vector.Spec.Agent.Api.Address == "" { From 45fa2e2e689313fe10bf10ba3e1cec072d5386ab Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Mon, 31 Oct 2022 22:35:22 +0200 Subject: [PATCH 061/316] refactor gc --- .../factory/config/configcheck/configcheck.go | 13 ++++++---- controllers/factory/utils/k8s/k8s.go | 24 +++++++++++++++++++ 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/controllers/factory/config/configcheck/configcheck.go b/controllers/factory/config/configcheck/configcheck.go index 98565ae3..c7343771 100644 --- a/controllers/factory/config/configcheck/configcheck.go +++ b/controllers/factory/config/configcheck/configcheck.go @@ -184,17 +184,20 @@ func (cc *ConfigCheck) cleanup() error { if pod.Status.Phase == "Succeeded" { for _, v := range pod.Spec.Volumes { if v.Name == "config" { - secret := &corev1.Secret{} - secretName := v.Secret.SecretName - if err := cc.Client.Get(cc.Ctx, types.NamespacedName{Name: secretName, Namespace: pod.Namespace}, secret); err != nil { + nn := types.NamespacedName{ + Name: v.Secret.SecretName, + Namespace: pod.Namespace, + } + secret, err := k8s.GetSecret(nn, cc.Client) + if err != nil { return err } - if err := cc.Client.Delete(cc.Ctx, secret); err != nil { + if err := k8s.DeleteSecret(secret, cc.Client); err != nil { return err } } } - if err := cc.Client.Delete(cc.Ctx, &pod); err != nil { + if err := k8s.DeletePod(&pod, cc.Client); err != nil { return err } } diff --git a/controllers/factory/utils/k8s/k8s.go b/controllers/factory/utils/k8s/k8s.go index 5ee3c494..8f3863da 100644 --- a/controllers/factory/utils/k8s/k8s.go +++ b/controllers/factory/utils/k8s/k8s.go @@ -27,6 +27,7 @@ import ( "k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -76,6 +77,29 @@ func GetPod(pod *corev1.Pod, c client.Client) (*corev1.Pod, error) { return result, nil } +func DeletePod(pod *corev1.Pod, c client.Client) error { + if err := c.Delete(context.TODO(), pod); err != nil { + return err + } + return nil +} + +func GetSecret(namespacedName types.NamespacedName, c client.Client) (*corev1.Secret, error) { + result := &corev1.Secret{} + err := c.Get(context.TODO(), namespacedName, result) + if err != nil { + return nil, err + } + return result, nil +} + +func DeleteSecret(secret *corev1.Secret, c client.Client) error { + if err := c.Delete(context.TODO(), secret); err != nil { + return err + } + return nil +} + func GetPodLogs(pod *corev1.Pod, cs *kubernetes.Clientset) (string, error) { count := int64(100) podLogOptions := corev1.PodLogOptions{ From e76ff4d713c95fdd865aebee74eecefa8bb3bbcb Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Tue, 1 Nov 2022 13:17:42 +0300 Subject: [PATCH 062/316] Clusterpipeline (#28) * added clusterpipeline feature --- .../clustervectorpipeline_controller.go | 174 ++++++++++++------ controllers/factory/config/config.go | 36 +++- controllers/factory/config/config_build.go | 15 +- controllers/factory/pipeline/pipeline.go | 18 +- controllers/factory/utils/k8s/k8s.go | 4 + controllers/factory/vector/types.go | 8 +- controllers/factory/vector/vector.go | 4 +- 7 files changed, 177 insertions(+), 82 deletions(-) diff --git a/controllers/clustervectorpipeline_controller.go b/controllers/clustervectorpipeline_controller.go index f785e859..8cc8c017 100644 --- a/controllers/clustervectorpipeline_controller.go +++ b/controllers/clustervectorpipeline_controller.go @@ -19,6 +19,7 @@ package controllers import ( "context" + "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes" ctrl "sigs.k8s.io/controller-runtime" @@ -26,6 +27,11 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" + "github.com/kaasops/vector-operator/controllers/factory/config" + "github.com/kaasops/vector-operator/controllers/factory/config/configcheck" + "github.com/kaasops/vector-operator/controllers/factory/pipeline" + "github.com/kaasops/vector-operator/controllers/factory/pipeline/clustervectorpipeline" + "github.com/kaasops/vector-operator/controllers/factory/vector/vectoragent" ) // ClusterVectorPipelineReconciler reconciles a ClusterVectorPipeline object @@ -51,68 +57,118 @@ type ClusterVectorPipelineReconciler struct { // For more details, check Reconcile and its Result here: // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.12.2/pkg/reconcile func (r *ClusterVectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - log := log.FromContext(ctx).WithValues("VectorPipeline", req.Name) - - log.Info("start Reconcile ClusterVectorPipeline") - - // cvp, err := r.findClusterVectorPipelineCustomResourceInstance(ctx, req) - // if err != nil { - // log.Error(err, "Failed to get Cluster Vector Pipeline") - // return ctrl.Result{}, err - // } - // if cvp == nil { - // log.Info("Cluster Vector Pipeline CR not found. Ignoring since object must be deleted") - // return ctrl.Result{}, nil - // } - // hash, err := vectorpipeline.GetSpecHash(cvp.Spec) - // if err != nil { - // return ctrl.Result{}, err - // } - // if cvp.Status.LastAppliedPipelineHash != nil && *hash == *cvp.Status.LastAppliedPipelineHash { - // log.Info("ClusterVectorPipeline has no changes. Finish Reconcile ClusterVectorPipeline") - // return ctrl.Result{}, nil - // } - - // vectorInstances := &vectorv1alpha1.VectorList{} - // err = r.List(ctx, vectorInstances) - // if err != nil { - // return ctrl.Result{}, err - // } - - // if len(vectorInstances.Items) == 0 { - // log.Info("Vertors not found") - // return ctrl.Result{}, nil - // } - - // for _, v := range vectorInstances.Items { - // if v.DeletionTimestamp != nil { - // continue - // } - // if err = checkConfig(ctx, &v, vp, r.Client, r.Clientset); err != nil { - // return ctrl.Result{}, err - // } - // if err = vectorpipeline.SetLastAppliedPipelineStatus(ctx, vp, r.Client); err != nil { - // return ctrl.Result{}, err - // } - - // } - - log.Info("finish Reconcile ClusterVectorPipeline") + log := log.FromContext(ctx).WithValues("ClusterVectorPipeline", req.Name) + + log.Info("start Reconcile VectorPipeline") + + // Get CR VectorPipeline + vectorPipelineCR, err := r.findClusterVectorPipelineCustomResourceInstance(ctx, req) + if err != nil { + log.Error(err, "Failed to get Vector Pipeline") + return ctrl.Result{}, err + } + if vectorPipelineCR == nil { + log.Info("VectorPIpeline CR not found. Ignoring since object must be deleted") + return ctrl.Result{}, nil + } + + // Generate VectorPipeline Controller + vpCtrl := clustervectorpipeline.NewController(vectorPipelineCR) + // Generate Pipeline Controller + pCtrl := pipeline.NewController(ctx, r.Client, vpCtrl) + + // Check Pipeline hash + checkResult, err := pCtrl.CheckHash() + if err != nil { + return ctrl.Result{}, err + } + if checkResult { + log.Info("VectorPipeline has no changes. Finish Reconcile VectorPipeline") + return ctrl.Result{}, nil + } + + // Generate Pipeline ConfigCheck for all Vectors + vectorInstances := &vectorv1alpha1.VectorList{} + err = r.List(ctx, vectorInstances) + if err != nil { + return ctrl.Result{}, err + } + + if len(vectorInstances.Items) == 0 { + log.Info("Vertors not found") + return ctrl.Result{}, nil + } + + for _, vector := range vectorInstances.Items { + if vector.DeletionTimestamp != nil { + continue + } + + // Init Controller for Vector Agent + vaCtrl := vectoragent.NewController(&vector, r.Client, r.Clientset) + if vaCtrl.Vector.Spec.Agent.DataDir == "" { + vaCtrl.Vector.Spec.Agent.DataDir = "/vector-data-dir" + } + + // Get Vector Config file + config, err := config.New(ctx, vaCtrl) + if err != nil { + return ctrl.Result{}, err + } + if err := config.FillForVectorPipeline(pCtrl); err != nil { + return ctrl.Result{}, err + } + + // Get Config in Json ([]byte) + byteConfig, err := config.GetByteConfig() + if err != nil { + return ctrl.Result{}, err + } + + // Init CheckConfig + configCheck := configcheck.New(ctx, byteConfig, vaCtrl.Client, vaCtrl.ClientSet, vaCtrl.Vector.Name, vaCtrl.Vector.Namespace, vaCtrl.Vector.Spec.Agent.Image) + + // Start ConfigCheck + err = configCheck.Run() + if _, ok := err.(*configcheck.ConfigCheckError); ok { + if err := pCtrl.SetFailedStatus(err); err != nil { + return ctrl.Result{}, err + } + if err = pCtrl.SetLastAppliedPipelineStatus(); err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{}, nil + } + if err != nil { + return ctrl.Result{}, err + } + + if err = pCtrl.SetSucceesStatus(); err != nil { + return ctrl.Result{}, err + } + + if err = pCtrl.SetLastAppliedPipelineStatus(); err != nil { + return ctrl.Result{}, err + } + + } + + log.Info("finish Reconcile VectorPipeline") return ctrl.Result{}, nil } -// func (r *ClusterVectorPipelineReconciler) findClusterVectorPipelineCustomResourceInstance(ctx context.Context, req ctrl.Request) (*vectorv1alpha1.ClusterVectorPipeline, error) { -// // fetch the master instance -// cvp := &vectorv1alpha1.ClusterVectorPipeline{} -// err := r.Get(ctx, req.NamespacedName, cvp) -// if err != nil { -// if errors.IsNotFound(err) { -// return nil, nil -// } -// return nil, err -// } -// return cvp, nil -// } +func (r *ClusterVectorPipelineReconciler) findClusterVectorPipelineCustomResourceInstance(ctx context.Context, req ctrl.Request) (*vectorv1alpha1.ClusterVectorPipeline, error) { + // fetch the master instance + cvp := &vectorv1alpha1.ClusterVectorPipeline{} + err := r.Get(ctx, req.NamespacedName, cvp) + if err != nil { + if errors.IsNotFound(err) { + return nil, nil + } + return nil, err + } + return cvp, nil +} // SetupWithManager sets up the controller with the Manager. func (r *ClusterVectorPipelineReconciler) SetupWithManager(mgr ctrl.Manager) error { diff --git a/controllers/factory/config/config.go b/controllers/factory/config/config.go index da4f8159..6c713a57 100644 --- a/controllers/factory/config/config.go +++ b/controllers/factory/config/config.go @@ -19,8 +19,11 @@ package config import ( "context" "encoding/json" + "errors" "github.com/kaasops/vector-operator/controllers/factory/pipeline" + "github.com/kaasops/vector-operator/controllers/factory/pipeline/clustervectorpipeline" + "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" "github.com/kaasops/vector-operator/controllers/factory/vector" "github.com/kaasops/vector-operator/controllers/factory/vector/vectoragent" "github.com/mitchellh/mapstructure" @@ -62,11 +65,11 @@ func (cfg *Config) FillForVectorAgent() error { return nil } -func (cfg *Config) FillForVectorPipeline(vCtrl *pipeline.Controller) error { - cfg.Name = vCtrl.Pipeline.Name() +func (cfg *Config) FillForVectorPipeline(pCtrl *pipeline.Controller) error { + cfg.Name = pCtrl.Pipeline.Name() pCtrls := make([]pipeline.Controller, 1) - pCtrls[0] = *vCtrl + pCtrls[0] = *pCtrl cfg.pCtrls = pCtrls @@ -74,6 +77,17 @@ func (cfg *Config) FillForVectorPipeline(vCtrl *pipeline.Controller) error { return err } + err := cfg.Validate() + if err != nil { + if err := pCtrl.SetFailedStatus(err); err != nil { + return err + } + if err := pCtrl.SetLastAppliedPipelineStatus(); err != nil { + return err + } + return err + } + return nil } @@ -127,3 +141,19 @@ func CfgToMap(cfg vector.VectorConfig) (cfgMap map[string]interface{}, err error return cfgMap, nil } + +func (cfg *Config) Validate() error { + err := errors.New("type kubernetes_logs only allowed") + vp := cfg.pCtrls[0] + for _, source := range cfg.VectorConfig.Sources { + if vp.Pipeline.Type() != clustervectorpipeline.Type { + if source.Type != "kubernetes_logs" { + return err + } + if source.ExtraNamespaceLabelSelector != "" && source.ExtraNamespaceLabelSelector != k8s.NamespaceNameToLabel(vp.Pipeline.Namespace()) { + return err + } + } + } + return nil +} diff --git a/controllers/factory/config/config_build.go b/controllers/factory/config/config_build.go index 8a7bf82d..cabc7fd8 100644 --- a/controllers/factory/config/config_build.go +++ b/controllers/factory/config/config_build.go @@ -17,15 +17,17 @@ limitations under the License. package config import ( + "github.com/kaasops/vector-operator/controllers/factory/pipeline/clustervectorpipeline" + "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" "github.com/kaasops/vector-operator/controllers/factory/vector" ) var ( - sourceDefault = vector.Source{ + sourceDefault = &vector.Source{ Name: "defaultSource", Type: "kubernetes_logs", } - sinkDefault = vector.Sink{ + sinkDefault = &vector.Sink{ Name: "defaultSink", Type: "blackhole", Inputs: []string{"defaultSource"}, @@ -45,10 +47,10 @@ func (cfg *Config) GenerateVectorConfig() error { } if len(sources) == 0 { - sources = []vector.Source{sourceDefault} + sources = []*vector.Source{sourceDefault} } if len(sinks) == 0 { - sinks = []vector.Sink{sinkDefault} + sinks = []*vector.Sink{sinkDefault} } vectorConfig.Sinks = sinks @@ -60,7 +62,7 @@ func (cfg *Config) GenerateVectorConfig() error { return nil } -func (cfg *Config) getComponents() (sources []vector.Source, transforms []vector.Transform, sinks []vector.Sink, err error) { +func (cfg *Config) getComponents() (sources []*vector.Source, transforms []*vector.Transform, sinks []*vector.Sink, err error) { for _, vCtrl := range cfg.pCtrls { pipelineSources, err := vCtrl.GetSources(nil) @@ -71,6 +73,9 @@ func (cfg *Config) getComponents() (sources []vector.Source, transforms []vector if err != nil { return nil, nil, nil, err } + if vCtrl.Pipeline.Type() != clustervectorpipeline.Type && source.Type == "kubernetes_logs" { + source.ExtraNamespaceLabelSelector = k8s.NamespaceNameToLabel(vCtrl.Pipeline.Namespace()) + } sources = append(sources, source) } pipelineTransforms, err := vCtrl.GetTransforms() diff --git a/controllers/factory/pipeline/pipeline.go b/controllers/factory/pipeline/pipeline.go index f6108ddf..da21ce8c 100644 --- a/controllers/factory/pipeline/pipeline.go +++ b/controllers/factory/pipeline/pipeline.go @@ -132,8 +132,8 @@ func (ctrl *Controller) SelectClusterVectorPipelineSucceesed() ([]*clustervector return cvpCombined, nil } -func (ctrl *Controller) GetSources(filter []string) ([]vector.Source, error) { - var sources []vector.Source +func (ctrl *Controller) GetSources(filter []string) ([]*vector.Source, error) { + var sources []*vector.Source sourcesMap, err := decodeRaw(ctrl.Pipeline.Spec().Sources.Raw) if err != nil { return nil, err @@ -144,7 +144,7 @@ func (ctrl *Controller) GetSources(filter []string) ([]vector.Source, error) { continue } } - var source vector.Source + var source *vector.Source if err := mapstructure.Decode(v, &source); err != nil { return nil, err } @@ -154,7 +154,7 @@ func (ctrl *Controller) GetSources(filter []string) ([]vector.Source, error) { return sources, nil } -func (ctrl *Controller) GetTransforms() ([]vector.Transform, error) { +func (ctrl *Controller) GetTransforms() ([]*vector.Transform, error) { if ctrl.Pipeline.Spec().Transforms == nil { return nil, nil } @@ -162,12 +162,12 @@ func (ctrl *Controller) GetTransforms() ([]vector.Transform, error) { if err != nil { return nil, err } - var transforms []vector.Transform + var transforms []*vector.Transform if err := json.Unmarshal(ctrl.Pipeline.Spec().Transforms.Raw, &transformsMap); err != nil { return nil, err } for k, v := range transformsMap { - var transform vector.Transform + var transform *vector.Transform if err := mapstructure.Decode(v, &transform); err != nil { return nil, err } @@ -180,14 +180,14 @@ func (ctrl *Controller) GetTransforms() ([]vector.Transform, error) { return transforms, nil } -func (ctrl *Controller) GetSinks() ([]vector.Sink, error) { +func (ctrl *Controller) GetSinks() ([]*vector.Sink, error) { sinksMap, err := decodeRaw(ctrl.Pipeline.Spec().Sinks.Raw) if err != nil { return nil, err } - var sinks []vector.Sink + var sinks []*vector.Sink for k, v := range sinksMap { - var sink vector.Sink + var sink *vector.Sink if err := mapstructure.Decode(v, &sink); err != nil { return nil, err } diff --git a/controllers/factory/utils/k8s/k8s.go b/controllers/factory/utils/k8s/k8s.go index 8f3863da..8247a4b1 100644 --- a/controllers/factory/utils/k8s/k8s.go +++ b/controllers/factory/utils/k8s/k8s.go @@ -289,3 +289,7 @@ func reconcileClusterRoleBinding(obj runtime.Object, c client.Client) error { return nil } + +func NamespaceNameToLabel(namespace string) string { + return "kubernetes.io/metadata.name=" + namespace +} diff --git a/controllers/factory/vector/types.go b/controllers/factory/vector/types.go index 52a58681..929e69ab 100644 --- a/controllers/factory/vector/types.go +++ b/controllers/factory/vector/types.go @@ -23,15 +23,15 @@ import ( type VectorConfig struct { DataDir string `mapstructure:"data_dir"` Api *vectorv1alpha1.ApiSpec `mapstructure:"api"` - Sources []Source `mapstructure:"sources"` - Transforms []Transform `mapstructure:"transforms"` - Sinks []Sink `mapstructure:"sinks"` + Sources []*Source `mapstructure:"sources"` + Transforms []*Transform `mapstructure:"transforms"` + Sinks []*Sink `mapstructure:"sinks"` } type Source struct { Name string Type string `mapper:"type"` - ExtraNamespaceLabelSelector string `mapper:"extra_namespace_label_selector"` + ExtraNamespaceLabelSelector string `mapper:"extra_namespace_label_selector,omitempty"` Options map[string]interface{} `mapstructure:",remain"` } diff --git a/controllers/factory/vector/vector.go b/controllers/factory/vector/vector.go index 139d458c..d96bd773 100644 --- a/controllers/factory/vector/vector.go +++ b/controllers/factory/vector/vector.go @@ -22,8 +22,8 @@ import ( ) func New(vector *vectorv1alpha1.Vector) *VectorConfig { - sources := []Source{} - sinks := []Sink{} + sources := []*Source{} + sinks := []*Sink{} return &VectorConfig{ DataDir: vector.Spec.Agent.DataDir, From eada0c7afd13d86d9aa3bea811a16cbdb16ac466 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Tue, 1 Nov 2022 15:13:52 +0300 Subject: [PATCH 063/316] Add quick-start doc Signed-off-by: Zemtsov Vladimir --- docs/quick-start.md | 300 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 300 insertions(+) create mode 100644 docs/quick-start.md diff --git a/docs/quick-start.md b/docs/quick-start.md new file mode 100644 index 00000000..fd0dfc6a --- /dev/null +++ b/docs/quick-start.md @@ -0,0 +1,300 @@ +# Quick start +Operator serves to make running Vector applications on top of Kubernetes as easy as possible while preserving Kubernetes-native configuration options. + +# Installing by Manifest +## Install CRDs +```bash +k apply -f config/crd/bases/observability.kaasops.io_vectors.yaml +k apply -f config/crd/bases/observability.kaasops.io_vectorpipelines.yaml +k apply -f config/crd/bases/observability.kaasops.io_clustervectorpipelines.yaml +``` + +## Start Vector Operator +### Create namespace for Vector Operator +```bash +kubectl create namespace vector-operator-system +``` + +### Create RBAC +```bash +# Create ServiceAccount for Vector Operator +kubectl create serviceaccount -n vector-operator-system vector-operator + +# Create Secret for Vector Operator Service Account (if Kubernetes version > 1.23) +cat < Date: Tue, 1 Nov 2022 15:39:24 +0300 Subject: [PATCH 064/316] Cleanup doc quick-start Signed-off-by: Zemtsov Vladimir --- docs/quick-start.md | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/docs/quick-start.md b/docs/quick-start.md index fd0dfc6a..399fd33a 100644 --- a/docs/quick-start.md +++ b/docs/quick-start.md @@ -4,9 +4,11 @@ Operator serves to make running Vector applications on top of Kubernetes as easy # Installing by Manifest ## Install CRDs ```bash -k apply -f config/crd/bases/observability.kaasops.io_vectors.yaml -k apply -f config/crd/bases/observability.kaasops.io_vectorpipelines.yaml -k apply -f config/crd/bases/observability.kaasops.io_clustervectorpipelines.yaml +git clone https://github.com/kaasops/vector-operator.git +cd vector-operator +kubectl apply -f config/crd/bases/observability.kaasops.io_vectors.yaml +kubectl apply -f config/crd/bases/observability.kaasops.io_vectorpipelines.yaml +kubectl apply -f config/crd/bases/observability.kaasops.io_clustervectorpipelines.yaml ``` ## Start Vector Operator @@ -17,9 +19,6 @@ kubectl create namespace vector-operator-system ### Create RBAC ```bash -# Create ServiceAccount for Vector Operator -kubectl create serviceaccount -n vector-operator-system vector-operator - # Create Secret for Vector Operator Service Account (if Kubernetes version > 1.23) cat < Date: Tue, 1 Nov 2022 15:43:03 +0300 Subject: [PATCH 065/316] build docker only on master tag (#29) --- .github/workflows/main.yaml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index c6cec786..bdeb0ff4 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -4,10 +4,7 @@ name: Build and Test on: push: tags: - - '*' - branches: - - main - pull_request: + - 'v*' branches: - main jobs: @@ -20,7 +17,7 @@ jobs: - name: Get tag id: tag - run: echo "TAG=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + run: echo "TAG=$(git describe --tags HEAD)" >> $GITHUB_OUTPUT - name: Set up QEMU uses: docker/setup-qemu-action@v1 @@ -40,4 +37,4 @@ jobs: context: . platforms: linux/amd64,linux/arm64 push: true - tags: ${{ github.repository }}:${{ steps.tag.outputs.TAG }} \ No newline at end of file + tags: ${{ github.repository }}:${{ steps.tag.outputs.TAG }},${{ github.repository }}:latest \ No newline at end of file From 1d575f84e5b10787af03769d97117fe472d6da8f Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Tue, 1 Nov 2022 15:21:52 +0200 Subject: [PATCH 066/316] disable build image after push to main --- .github/workflows/{main.yaml => build-image.yaml} | 2 -- 1 file changed, 2 deletions(-) rename .github/workflows/{main.yaml => build-image.yaml} (97%) diff --git a/.github/workflows/main.yaml b/.github/workflows/build-image.yaml similarity index 97% rename from .github/workflows/main.yaml rename to .github/workflows/build-image.yaml index bdeb0ff4..13a9cca8 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/build-image.yaml @@ -5,8 +5,6 @@ on: push: tags: - 'v*' - branches: - - main jobs: build-and-push-docker-image: name: Build and push Docker image From cd91ca897283b30a7c363386ceebf55e3ddb6af5 Mon Sep 17 00:00:00 2001 From: Vladimir <31961982+zvlb@users.noreply.github.com> Date: Tue, 1 Nov 2022 15:41:00 +0200 Subject: [PATCH 067/316] Update ReadMe (#30) Signed-off-by: Zemtsov Vladimir --- README.md | 46 +++++----------------------------------------- 1 file changed, 5 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index a80a0a6f..9f5e01ca 100644 --- a/README.md +++ b/README.md @@ -15,48 +15,14 @@ The operator deploys and configures a vector agent daemonset on every node to co - [x] Building vector config from namespaced custom resources (kind: VectorPipeline) - [x] Configuration validation - [x] Full support of vector config options -- [ ] Namespace isolation -- [ ] Garbage collection +- [x] Namespace isolation - [ ] Vector config optimization - [ ] Vector aggregator support - -## Getting Started -You’ll need a Kubernetes cluster to run against. You can use [KIND](https://sigs.k8s.io/kind) to get a local cluster for testing, or run against a remote cluster. -**Note:** Your controller will automatically use the current context in your kubeconfig file (i.e. whatever cluster `kubectl cluster-info` shows). - -### Running on the cluster -1. Install Instances of Custom Resources: - -```sh -kubectl apply -f config/samples/ -``` - -2. Build and push your image to the location specified by `IMG`: - -```sh -make docker-build docker-push IMG=docker pull kaasops/vector-operator:latest -``` - -3. Deploy the controller to the cluster with the image specified by `IMG`: - -```sh -make deploy IMG=docker pull kaasops/vector-operator:latest -``` - -### Uninstall CRDs -To delete the CRDs from the cluster: - -```sh -make uninstall -``` - -### Undeploy controller -UnDeploy the controller to the cluster: - -```sh -make undeploy -``` +## Documentation +- Quick start [doc](https://github.com/kaasops/vector-operator/blob/main/docs/quick-start.md) +- Design [doc](https://github.com/kaasops/vector-operator/blob/main/docs/design.md) +- Specification [doc](https://github.com/kaasops/vector-operator/blob/main/docs/specification.md) ## Configuration Examples Configuration for CR Vector: @@ -113,8 +79,6 @@ spec: ``` - - ## Contributing ### How it works From de212eff71474a7c6244ffc62a8baede1908f187 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Fri, 4 Nov 2022 14:14:38 +0200 Subject: [PATCH 068/316] config build refactoring --- api/v1alpha1/clustervectorpipeline.go | 59 +++++ api/v1alpha1/vectorpipeline.go | 59 +++++ .../clustervectorpipeline_controller.go | 32 ++- controllers/factory/config/config.go | 146 ++---------- controllers/factory/config/config_build.go | 221 +++++++++++++++-- .../factory/{vector => config}/types.go | 2 +- controllers/factory/config/utils.go | 28 +++ .../clustervectorpipeline.go | 76 ------ controllers/factory/pipeline/hash.go | 10 +- controllers/factory/pipeline/pipeline.go | 223 +++--------------- .../pipeline/vectorpipeline/vectorpipeline.go | 76 ------ controllers/factory/vector/vector.go | 65 ----- controllers/vector_controller.go | 8 +- controllers/vectorpipeline_controller.go | 32 ++- 14 files changed, 450 insertions(+), 587 deletions(-) create mode 100644 api/v1alpha1/clustervectorpipeline.go create mode 100644 api/v1alpha1/vectorpipeline.go rename controllers/factory/{vector => config}/types.go (99%) create mode 100644 controllers/factory/config/utils.go delete mode 100644 controllers/factory/pipeline/clustervectorpipeline/clustervectorpipeline.go delete mode 100644 controllers/factory/pipeline/vectorpipeline/vectorpipeline.go delete mode 100644 controllers/factory/vector/vector.go diff --git a/api/v1alpha1/clustervectorpipeline.go b/api/v1alpha1/clustervectorpipeline.go new file mode 100644 index 00000000..b27b437a --- /dev/null +++ b/api/v1alpha1/clustervectorpipeline.go @@ -0,0 +1,59 @@ +package v1alpha1 + +import ( + "context" + + "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +var ( + ClusterPipelineKind = "ClusterVectorPipeline" +) + +func (vp *ClusterVectorPipeline) GetSpec() VectorPipelineSpec { + return vp.Spec +} + +func (vp *ClusterVectorPipeline) GetName() string { + return vp.Name +} + +func (vp *ClusterVectorPipeline) GetNamespace() string { + return vp.Namespace +} + +func (vp *ClusterVectorPipeline) Type() string { + return vp.Kind +} + +func (vp *ClusterVectorPipeline) IsValid() bool { + if vp.Status.ConfigCheckResult != nil { + return *vp.Status.ConfigCheckResult + } + return false +} + +func (vp *ClusterVectorPipeline) IsDeleted() bool { + return !vp.DeletionTimestamp.IsZero() +} + +func (vp *ClusterVectorPipeline) SetConfigCheck(value bool) { + vp.Status.ConfigCheckResult = &value +} + +func (vp *ClusterVectorPipeline) SetReason(reason *string) { + vp.Status.Reason = reason +} + +func (vp *ClusterVectorPipeline) GetLastAppliedPipeline() *uint32 { + return vp.Status.LastAppliedPipelineHash +} + +func (vp *ClusterVectorPipeline) SetLastAppliedPipeline(hash *uint32) { + vp.Status.LastAppliedPipelineHash = hash +} + +func (vp *ClusterVectorPipeline) UpdateStatus(ctx context.Context, c client.Client) error { + return k8s.UpdateStatus(ctx, vp, c) +} diff --git a/api/v1alpha1/vectorpipeline.go b/api/v1alpha1/vectorpipeline.go new file mode 100644 index 00000000..c99d3a5c --- /dev/null +++ b/api/v1alpha1/vectorpipeline.go @@ -0,0 +1,59 @@ +package v1alpha1 + +import ( + "context" + + "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +var ( + LocalPipelineKind = "VectorPipeline" +) + +func (vp *VectorPipeline) GetSpec() VectorPipelineSpec { + return vp.Spec +} + +func (vp *VectorPipeline) GetName() string { + return vp.Name +} + +func (vp *VectorPipeline) GetNamespace() string { + return vp.Namespace +} + +func (vp *VectorPipeline) Type() string { + return vp.Kind +} + +func (vp *VectorPipeline) IsValid() bool { + if vp.Status.ConfigCheckResult != nil { + return *vp.Status.ConfigCheckResult + } + return false +} + +func (vp *VectorPipeline) IsDeleted() bool { + return !vp.DeletionTimestamp.IsZero() +} + +func (vp *VectorPipeline) SetConfigCheck(value bool) { + vp.Status.ConfigCheckResult = &value +} + +func (vp *VectorPipeline) SetReason(reason *string) { + vp.Status.Reason = reason +} + +func (vp *VectorPipeline) GetLastAppliedPipeline() *uint32 { + return vp.Status.LastAppliedPipelineHash +} + +func (vp *VectorPipeline) SetLastAppliedPipeline(hash *uint32) { + vp.Status.LastAppliedPipelineHash = hash +} + +func (vp *VectorPipeline) UpdateStatus(ctx context.Context, c client.Client) error { + return k8s.UpdateStatus(ctx, vp, c) +} diff --git a/controllers/clustervectorpipeline_controller.go b/controllers/clustervectorpipeline_controller.go index 8cc8c017..7bbdabcd 100644 --- a/controllers/clustervectorpipeline_controller.go +++ b/controllers/clustervectorpipeline_controller.go @@ -30,7 +30,6 @@ import ( "github.com/kaasops/vector-operator/controllers/factory/config" "github.com/kaasops/vector-operator/controllers/factory/config/configcheck" "github.com/kaasops/vector-operator/controllers/factory/pipeline" - "github.com/kaasops/vector-operator/controllers/factory/pipeline/clustervectorpipeline" "github.com/kaasops/vector-operator/controllers/factory/vector/vectoragent" ) @@ -72,13 +71,8 @@ func (r *ClusterVectorPipelineReconciler) Reconcile(ctx context.Context, req ctr return ctrl.Result{}, nil } - // Generate VectorPipeline Controller - vpCtrl := clustervectorpipeline.NewController(vectorPipelineCR) - // Generate Pipeline Controller - pCtrl := pipeline.NewController(ctx, r.Client, vpCtrl) - // Check Pipeline hash - checkResult, err := pCtrl.CheckHash() + checkResult, err := pipeline.CheckHash(vectorPipelineCR) if err != nil { return ctrl.Result{}, err } @@ -111,18 +105,20 @@ func (r *ClusterVectorPipelineReconciler) Reconcile(ctx context.Context, req ctr } // Get Vector Config file - config, err := config.New(ctx, vaCtrl) + configBuilder, err := config.NewConfigBuilder(ctx, vaCtrl, vectorPipelineCR) if err != nil { return ctrl.Result{}, err } - if err := config.FillForVectorPipeline(pCtrl); err != nil { - return ctrl.Result{}, err - } - // Get Config in Json ([]byte) - byteConfig, err := config.GetByteConfig() + byteConfig, err := configBuilder.GetByteConfigWithValidate() if err != nil { - return ctrl.Result{}, err + if err := pipeline.SetFailedStatus(ctx, r.Client, vectorPipelineCR, err); err != nil { + return ctrl.Result{}, err + } + if err = pipeline.SetLastAppliedPipelineStatus(ctx, r.Client, vectorPipelineCR); err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{}, nil } // Init CheckConfig @@ -131,10 +127,10 @@ func (r *ClusterVectorPipelineReconciler) Reconcile(ctx context.Context, req ctr // Start ConfigCheck err = configCheck.Run() if _, ok := err.(*configcheck.ConfigCheckError); ok { - if err := pCtrl.SetFailedStatus(err); err != nil { + if err = pipeline.SetFailedStatus(ctx, r.Client, vectorPipelineCR, err); err != nil { return ctrl.Result{}, err } - if err = pCtrl.SetLastAppliedPipelineStatus(); err != nil { + if err = pipeline.SetLastAppliedPipelineStatus(ctx, r.Client, vectorPipelineCR); err != nil { return ctrl.Result{}, err } return ctrl.Result{}, nil @@ -143,11 +139,11 @@ func (r *ClusterVectorPipelineReconciler) Reconcile(ctx context.Context, req ctr return ctrl.Result{}, err } - if err = pCtrl.SetSucceesStatus(); err != nil { + if err = pipeline.SetSucceesStatus(ctx, r.Client, vectorPipelineCR); err != nil { return ctrl.Result{}, err } - if err = pCtrl.SetLastAppliedPipelineStatus(); err != nil { + if err = pipeline.SetLastAppliedPipelineStatus(ctx, r.Client, vectorPipelineCR); err != nil { return ctrl.Result{}, err } diff --git a/controllers/factory/config/config.go b/controllers/factory/config/config.go index 6c713a57..fe12b1a4 100644 --- a/controllers/factory/config/config.go +++ b/controllers/factory/config/config.go @@ -17,143 +17,49 @@ limitations under the License. package config import ( - "context" - "encoding/json" - "errors" - - "github.com/kaasops/vector-operator/controllers/factory/pipeline" - "github.com/kaasops/vector-operator/controllers/factory/pipeline/clustervectorpipeline" - "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" - "github.com/kaasops/vector-operator/controllers/factory/vector" - "github.com/kaasops/vector-operator/controllers/factory/vector/vectoragent" + vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" "github.com/mitchellh/mapstructure" ) -type Config struct { - Name string - - Ctx context.Context - vaCtrl *vectoragent.Controller - pCtrls []pipeline.Controller - - VectorConfig *vector.VectorConfig -} - -func New(ctx context.Context, vaCtrl *vectoragent.Controller) (*Config, error) { - cfg := &Config{ - Ctx: ctx, - vaCtrl: vaCtrl, - } - - return cfg, nil -} - -func (cfg *Config) FillForVectorAgent() error { - cfg.Name = cfg.vaCtrl.Vector.Name - - pCtrl := pipeline.NewController(cfg.Ctx, cfg.vaCtrl.Client, nil) - pCtrls, err := pCtrl.SelectSucceesed() - if err != nil { - return err - } - cfg.pCtrls = pCtrls +func New(vector *vectorv1alpha1.Vector) *VectorConfig { + sources := []*Source{} + sinks := []*Sink{} - if err := cfg.GenerateVectorConfig(); err != nil { - return err + return &VectorConfig{ + DataDir: vector.Spec.Agent.DataDir, + Api: &vector.Spec.Agent.Api, + Sources: sources, + Sinks: sinks, } - - return nil } -func (cfg *Config) FillForVectorPipeline(pCtrl *pipeline.Controller) error { - cfg.Name = pCtrl.Pipeline.Name() - - pCtrls := make([]pipeline.Controller, 1) - pCtrls[0] = *pCtrl - - cfg.pCtrls = pCtrls - - if err := cfg.GenerateVectorConfig(); err != nil { - return err +func Mapper(c ConfigComponent) (map[string]interface{}, error) { + spec := c.GetOptions() + config := &mapstructure.DecoderConfig{ + Result: &spec, + ZeroFields: false, + TagName: "mapper", + IgnoreUntaggedFields: true, } - - err := cfg.Validate() - if err != nil { - if err := pCtrl.SetFailedStatus(err); err != nil { - return err - } - if err := pCtrl.SetLastAppliedPipelineStatus(); err != nil { - return err - } - return err - } - - return nil -} - -func (cfg *Config) GetByteConfig() ([]byte, error) { - cfgMap, err := CfgToMap(*cfg.VectorConfig) + decoder, err := mapstructure.NewDecoder(config) if err != nil { return nil, err } - data, err := json.Marshal(cfgMap) + err = decoder.Decode(c) if err != nil { return nil, err } - - return data, nil + return spec, nil } -func CfgToMap(cfg vector.VectorConfig) (cfgMap map[string]interface{}, err error) { - sources := make(map[string]interface{}) - transforms := make(map[string]interface{}) - sinks := make(map[string]interface{}) - for _, source := range cfg.Sources { - spec, err := vector.Mapper(source) - if err != nil { - return nil, err - } - sources[source.Name] = spec - } - for _, transform := range cfg.Transforms { - spec, err := vector.Mapper(transform) - if err != nil { - return nil, err - } - transforms[transform.Name] = spec - } - for _, sink := range cfg.Sinks { - spec, err := vector.Mapper(sink) - if err != nil { - return nil, err - } - sinks[sink.Name] = spec - } - - err = mapstructure.Decode(cfg, &cfgMap) - if err != nil { - return nil, err - } - // TODO: remove hardcoded map keys - cfgMap["sources"] = sources - cfgMap["transforms"] = transforms - cfgMap["sinks"] = sinks +func (t Source) GetOptions() map[string]interface{} { + return t.Options +} - return cfgMap, nil +func (t Transform) GetOptions() map[string]interface{} { + return t.Options } -func (cfg *Config) Validate() error { - err := errors.New("type kubernetes_logs only allowed") - vp := cfg.pCtrls[0] - for _, source := range cfg.VectorConfig.Sources { - if vp.Pipeline.Type() != clustervectorpipeline.Type { - if source.Type != "kubernetes_logs" { - return err - } - if source.ExtraNamespaceLabelSelector != "" && source.ExtraNamespaceLabelSelector != k8s.NamespaceNameToLabel(vp.Pipeline.Namespace()) { - return err - } - } - } - return nil +func (t Sink) GetOptions() map[string]interface{} { + return t.Options } diff --git a/controllers/factory/config/config_build.go b/controllers/factory/config/config_build.go index cabc7fd8..077127d6 100644 --- a/controllers/factory/config/config_build.go +++ b/controllers/factory/config/config_build.go @@ -17,17 +17,24 @@ limitations under the License. package config import ( - "github.com/kaasops/vector-operator/controllers/factory/pipeline/clustervectorpipeline" + "context" + "encoding/json" + "errors" + + vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" + "github.com/kaasops/vector-operator/controllers/factory/pipeline" "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" - "github.com/kaasops/vector-operator/controllers/factory/vector" + "github.com/kaasops/vector-operator/controllers/factory/vector/vectoragent" + "github.com/mitchellh/mapstructure" ) var ( - sourceDefault = &vector.Source{ + KubernetesSourceType = "kubernetes_logs" + sourceDefault = &Source{ Name: "defaultSource", - Type: "kubernetes_logs", + Type: KubernetesSourceType, } - sinkDefault = &vector.Sink{ + sinkDefault = &Sink{ Name: "defaultSink", Type: "blackhole", Inputs: []string{"defaultSource"}, @@ -38,34 +45,88 @@ var ( } ) -func (cfg *Config) GenerateVectorConfig() error { - vectorConfig := vector.New(cfg.vaCtrl.Vector) +type ConfigBuilder struct { + Name string + Ctx context.Context + vaCtrl *vectoragent.Controller + Pipelines []pipeline.Pipeline +} + +func NewConfigBuilder(ctx context.Context, vaCtrl *vectoragent.Controller, pipelines ...pipeline.Pipeline) (*ConfigBuilder, error) { + return &ConfigBuilder{ + Ctx: ctx, + vaCtrl: vaCtrl, + Pipelines: pipelines, + }, nil +} + +func (b *ConfigBuilder) GetByteConfig() ([]byte, error) { + config, err := b.generateVectorConfig() + if err != nil { + return nil, err + } + + data, err := vectorConfigToByte(config) + if err != nil { + return nil, err + } + + return data, nil +} + +func (b *ConfigBuilder) GetByteConfigWithValidate() ([]byte, error) { + validateError := errors.New("type kubernetes_logs only allowed") + config, err := b.generateVectorConfig() + if err != nil { + return nil, err + } + if len(b.Pipelines) != 0 { + for _, pipeline := range b.Pipelines { + for _, source := range config.Sources { + if pipeline.Type() != vectorv1alpha1.ClusterPipelineKind { + if source.Type != KubernetesSourceType { + return nil, validateError + } + if source.ExtraNamespaceLabelSelector != "" && source.ExtraNamespaceLabelSelector != k8s.NamespaceNameToLabel(pipeline.GetNamespace()) { + return nil, validateError + } + } + } + } + } + data, err := vectorConfigToByte(config) + if err != nil { + return nil, err + } + return data, nil +} + +func (b *ConfigBuilder) generateVectorConfig() (*VectorConfig, error) { + vectorConfig := New(b.vaCtrl.Vector) - sources, transforms, sinks, err := cfg.getComponents() + sources, transforms, sinks, err := b.getComponents() if err != nil { - return err + return nil, err } if len(sources) == 0 { - sources = []*vector.Source{sourceDefault} + sources = []*Source{sourceDefault} } if len(sinks) == 0 { - sinks = []*vector.Sink{sinkDefault} + sinks = []*Sink{sinkDefault} } vectorConfig.Sinks = sinks vectorConfig.Sources = sources vectorConfig.Transforms = transforms - cfg.VectorConfig = vectorConfig - - return nil + return vectorConfig, nil } -func (cfg *Config) getComponents() (sources []*vector.Source, transforms []*vector.Transform, sinks []*vector.Sink, err error) { +func (b *ConfigBuilder) getComponents() (sources []*Source, transforms []*Transform, sinks []*Sink, err error) { - for _, vCtrl := range cfg.pCtrls { - pipelineSources, err := vCtrl.GetSources(nil) + for _, pipeline := range b.Pipelines { + pipelineSources, err := getSources(pipeline, nil) if err != nil { return nil, nil, nil, err } @@ -73,12 +134,12 @@ func (cfg *Config) getComponents() (sources []*vector.Source, transforms []*vect if err != nil { return nil, nil, nil, err } - if vCtrl.Pipeline.Type() != clustervectorpipeline.Type && source.Type == "kubernetes_logs" { - source.ExtraNamespaceLabelSelector = k8s.NamespaceNameToLabel(vCtrl.Pipeline.Namespace()) + if pipeline.Type() != vectorv1alpha1.ClusterPipelineKind && source.Type == KubernetesSourceType { + source.ExtraNamespaceLabelSelector = k8s.NamespaceNameToLabel(pipeline.GetNamespace()) } sources = append(sources, source) } - pipelineTransforms, err := vCtrl.GetTransforms() + pipelineTransforms, err := getTransforms(pipeline) if err != nil { return nil, nil, nil, err } @@ -88,7 +149,7 @@ func (cfg *Config) getComponents() (sources []*vector.Source, transforms []*vect } transforms = append(transforms, transform) } - pipelineSinks, err := vCtrl.GetSinks() + pipelineSinks, err := getSinks(pipeline) if err != nil { return nil, nil, nil, err } @@ -101,3 +162,121 @@ func (cfg *Config) getComponents() (sources []*vector.Source, transforms []*vect } return sources, transforms, sinks, nil } + +func vectorConfigToByte(config *VectorConfig) ([]byte, error) { + cfgMap, err := cfgToMap(config) + if err != nil { + return nil, err + } + data, err := json.Marshal(cfgMap) + if err != nil { + return nil, err + } + return data, nil +} + +func getSources(pipeline pipeline.Pipeline, filter []string) ([]*Source, error) { + var sources []*Source + sourcesMap, err := decodeRaw(pipeline.GetSpec().Sources.Raw) + if err != nil { + return nil, err + } + for k, v := range sourcesMap { + if len(filter) != 0 { + if !contains(filter, k) { + continue + } + } + var source *Source + if err := mapstructure.Decode(v, &source); err != nil { + return nil, err + } + source.Name = addPrefix(pipeline.GetNamespace(), pipeline.GetName(), k) + sources = append(sources, source) + } + return sources, nil +} + +func getTransforms(pipeline pipeline.Pipeline) ([]*Transform, error) { + if pipeline.GetSpec().Transforms == nil { + return nil, nil + } + transformsMap, err := decodeRaw(pipeline.GetSpec().Transforms.Raw) + if err != nil { + return nil, err + } + var transforms []*Transform + if err := json.Unmarshal(pipeline.GetSpec().Transforms.Raw, &transformsMap); err != nil { + return nil, err + } + for k, v := range transformsMap { + var transform *Transform + if err := mapstructure.Decode(v, &transform); err != nil { + return nil, err + } + transform.Name = addPrefix(pipeline.GetNamespace(), pipeline.GetName(), k) + for i, inputName := range transform.Inputs { + transform.Inputs[i] = addPrefix(pipeline.GetNamespace(), pipeline.GetName(), inputName) + } + transforms = append(transforms, transform) + } + return transforms, nil +} + +func getSinks(pipeline pipeline.Pipeline) ([]*Sink, error) { + sinksMap, err := decodeRaw(pipeline.GetSpec().Sinks.Raw) + if err != nil { + return nil, err + } + var sinks []*Sink + for k, v := range sinksMap { + var sink *Sink + if err := mapstructure.Decode(v, &sink); err != nil { + return nil, err + } + sink.Name = addPrefix(pipeline.GetNamespace(), pipeline.GetName(), k) + for i, inputName := range sink.Inputs { + sink.Inputs[i] = addPrefix(pipeline.GetNamespace(), pipeline.GetName(), inputName) + } + sinks = append(sinks, sink) + } + return sinks, nil +} + +func cfgToMap(config *VectorConfig) (cfgMap map[string]interface{}, err error) { + sources := make(map[string]interface{}) + transforms := make(map[string]interface{}) + sinks := make(map[string]interface{}) + for _, source := range config.Sources { + spec, err := Mapper(source) + if err != nil { + return nil, err + } + sources[source.Name] = spec + } + for _, transform := range config.Transforms { + spec, err := Mapper(transform) + if err != nil { + return nil, err + } + transforms[transform.Name] = spec + } + for _, sink := range config.Sinks { + spec, err := Mapper(sink) + if err != nil { + return nil, err + } + sinks[sink.Name] = spec + } + + err = mapstructure.Decode(config, &cfgMap) + if err != nil { + return nil, err + } + // TODO: remove hardcoded map keys + cfgMap["sources"] = sources + cfgMap["transforms"] = transforms + cfgMap["sinks"] = sinks + + return cfgMap, nil +} diff --git a/controllers/factory/vector/types.go b/controllers/factory/config/types.go similarity index 99% rename from controllers/factory/vector/types.go rename to controllers/factory/config/types.go index 929e69ab..d62ca2a5 100644 --- a/controllers/factory/vector/types.go +++ b/controllers/factory/config/types.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package vector +package config import ( vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" diff --git a/controllers/factory/config/utils.go b/controllers/factory/config/utils.go new file mode 100644 index 00000000..fc37bf91 --- /dev/null +++ b/controllers/factory/config/utils.go @@ -0,0 +1,28 @@ +package config + +import "encoding/json" + +func decodeRaw(raw []byte) (map[string]interface{}, error) { + result := make(map[string]interface{}) + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return result, nil +} + +func contains(elems []string, v string) bool { + for _, s := range elems { + if v == s { + return true + } + } + return false +} + +func addPrefix(Namespace, Name, componentName string) string { + return generateName(Namespace, Name) + "-" + componentName +} + +func generateName(Namespace, Name string) string { + return Namespace + "-" + Name +} diff --git a/controllers/factory/pipeline/clustervectorpipeline/clustervectorpipeline.go b/controllers/factory/pipeline/clustervectorpipeline/clustervectorpipeline.go deleted file mode 100644 index 40b5e97c..00000000 --- a/controllers/factory/pipeline/clustervectorpipeline/clustervectorpipeline.go +++ /dev/null @@ -1,76 +0,0 @@ -/* -Copyright 2022. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package clustervectorpipeline - -import ( - "context" - - vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" - "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -var ( - Type = "cluster" -) - -type Controller struct { - ClusterVectorPipeline *vectorv1alpha1.ClusterVectorPipeline - Config []byte -} - -func (ctrl *Controller) Spec() vectorv1alpha1.VectorPipelineSpec { - return ctrl.ClusterVectorPipeline.Spec -} - -func (ctrl *Controller) Name() string { - return ctrl.ClusterVectorPipeline.Name -} - -func (ctrl *Controller) Namespace() string { - return ctrl.ClusterVectorPipeline.Namespace -} - -func (ctrl *Controller) Type() string { - return Type -} - -func (ctrl *Controller) SetConfigCheck(value bool) { - ctrl.ClusterVectorPipeline.Status.ConfigCheckResult = &value -} - -func (ctrl *Controller) SetReason(reason *string) { - ctrl.ClusterVectorPipeline.Status.Reason = reason -} - -func (ctrl *Controller) GetLastAppliedPipeline() *uint32 { - return ctrl.ClusterVectorPipeline.Status.LastAppliedPipelineHash -} - -func (ctrl *Controller) SetLastAppliedPipeline(hash *uint32) { - ctrl.ClusterVectorPipeline.Status.LastAppliedPipelineHash = hash -} - -func (ctrl *Controller) UpdateStatus(ctx context.Context, c client.Client) error { - return k8s.UpdateStatus(ctx, ctrl.ClusterVectorPipeline, c) -} - -func NewController(cvp *vectorv1alpha1.ClusterVectorPipeline) *Controller { - return &Controller{ - ClusterVectorPipeline: cvp, - } -} diff --git a/controllers/factory/pipeline/hash.go b/controllers/factory/pipeline/hash.go index 56ae2669..2be62ee8 100644 --- a/controllers/factory/pipeline/hash.go +++ b/controllers/factory/pipeline/hash.go @@ -22,8 +22,8 @@ import ( "github.com/kaasops/vector-operator/controllers/factory/utils/hash" ) -func (ctrl *Controller) GetSpecHash() (*uint32, error) { - a, err := json.Marshal(ctrl.Pipeline.Spec()) +func GetSpecHash(pipeline Pipeline) (*uint32, error) { + a, err := json.Marshal(pipeline.GetSpec()) if err != nil { return nil, err } @@ -32,13 +32,13 @@ func (ctrl *Controller) GetSpecHash() (*uint32, error) { } // CheckHash returns true, if hash in .status.lastAppliedPipelineHash matches with spec Hash -func (ctrl *Controller) CheckHash() (bool, error) { - hash, err := ctrl.GetSpecHash() +func CheckHash(pipeline Pipeline) (bool, error) { + hash, err := GetSpecHash(pipeline) if err != nil { return false, err } - if ctrl.Pipeline.GetLastAppliedPipeline() != nil && *hash == *ctrl.Pipeline.GetLastAppliedPipeline() { + if pipeline.GetLastAppliedPipeline() != nil && *hash == *pipeline.GetLastAppliedPipeline() { return true, nil } diff --git a/controllers/factory/pipeline/pipeline.go b/controllers/factory/pipeline/pipeline.go index da21ce8c..8edb9edf 100644 --- a/controllers/factory/pipeline/pipeline.go +++ b/controllers/factory/pipeline/pipeline.go @@ -18,238 +18,93 @@ package pipeline import ( "context" - "encoding/json" vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" - "github.com/kaasops/vector-operator/controllers/factory/pipeline/clustervectorpipeline" - "github.com/kaasops/vector-operator/controllers/factory/pipeline/vectorpipeline" - "github.com/kaasops/vector-operator/controllers/factory/vector" - "github.com/mitchellh/mapstructure" "sigs.k8s.io/controller-runtime/pkg/client" ) type Pipeline interface { - Spec() vectorv1alpha1.VectorPipelineSpec - Name() string - Namespace() string + GetSpec() vectorv1alpha1.VectorPipelineSpec + GetName() string + GetNamespace() string Type() string SetConfigCheck(bool) SetReason(*string) GetLastAppliedPipeline() *uint32 SetLastAppliedPipeline(*uint32) + IsValid() bool + IsDeleted() bool UpdateStatus(context.Context, client.Client) error } -type Controller struct { - Pipeline Pipeline - Ctx context.Context - Client client.Client -} - -func NewController(ctx context.Context, c client.Client, p Pipeline) *Controller { - return &Controller{ - Pipeline: p, - Ctx: ctx, - Client: c, - } -} - -func (ctrl *Controller) SelectSucceesed() ([]Controller, error) { - var combined []Controller - - vpCombined, err := ctrl.SelectVectorPipelineSucceesed() - if err != nil { - return nil, err - } - - cvpCombined, err := ctrl.SelectClusterVectorPipelineSucceesed() - if err != nil { - return nil, err - } - - for _, vp := range vpCombined { - combined = append(combined, Controller{ - Pipeline: vp, - }) - } - for _, cvp := range cvpCombined { - combined = append(combined, Controller{ - Pipeline: cvp, - }) - } - - return combined, nil - -} - -func (ctrl *Controller) SelectVectorPipelineSucceesed() ([]*vectorpipeline.Controller, error) { - var vpCombined []*vectorpipeline.Controller - - vps := vectorv1alpha1.VectorPipelineList{} - err := ctrl.Client.List(ctrl.Ctx, &vps) +func GetValidPipelines(ctx context.Context, client client.Client) ([]Pipeline, error) { + var validPipelines []Pipeline + vps, err := GetVectorPipelines(ctx, client) if err != nil { return nil, err } - - for _, vp := range vps.Items { - if !vp.DeletionTimestamp.IsZero() { - continue - } - if vp.Status.ConfigCheckResult != nil { - if *vp.Status.ConfigCheckResult { - vpCombined = append(vpCombined, &vectorpipeline.Controller{ - VectorPipeline: vp.DeepCopy(), - }) - } - } - - } - return vpCombined, nil -} - -func (ctrl *Controller) SelectClusterVectorPipelineSucceesed() ([]*clustervectorpipeline.Controller, error) { - var cvpCombined []*clustervectorpipeline.Controller - - cvps := vectorv1alpha1.ClusterVectorPipelineList{} - err := ctrl.Client.List(ctrl.Ctx, &cvps) + cvps, err := GetClusterVectorPipelines(ctx, client) if err != nil { return nil, err } - - for _, cvp := range cvps.Items { - if !cvp.DeletionTimestamp.IsZero() { - continue - } - if cvp.Status.ConfigCheckResult != nil { - if *cvp.Status.ConfigCheckResult { - cvpCombined = append(cvpCombined, &clustervectorpipeline.Controller{ - ClusterVectorPipeline: cvp.DeepCopy(), - }) + if len(vps) != 0 { + for _, vp := range vps { + if !vp.IsDeleted() && vp.IsValid() { + validPipelines = append(validPipelines, vp.DeepCopy()) } } - } - return cvpCombined, nil -} - -func (ctrl *Controller) GetSources(filter []string) ([]*vector.Source, error) { - var sources []*vector.Source - sourcesMap, err := decodeRaw(ctrl.Pipeline.Spec().Sources.Raw) - if err != nil { - return nil, err - } - for k, v := range sourcesMap { - if len(filter) != 0 { - if !contains(filter, k) { - continue + if len(cvps) != 0 { + for _, cvp := range cvps { + if !cvp.IsDeleted() && cvp.IsValid() { + validPipelines = append(validPipelines, cvp.DeepCopy()) } } - var source *vector.Source - if err := mapstructure.Decode(v, &source); err != nil { - return nil, err - } - source.Name = addPrefix(ctrl.Pipeline.Namespace(), ctrl.Pipeline.Name(), k) - sources = append(sources, source) - } - return sources, nil -} - -func (ctrl *Controller) GetTransforms() ([]*vector.Transform, error) { - if ctrl.Pipeline.Spec().Transforms == nil { - return nil, nil - } - transformsMap, err := decodeRaw(ctrl.Pipeline.Spec().Transforms.Raw) - if err != nil { - return nil, err - } - var transforms []*vector.Transform - if err := json.Unmarshal(ctrl.Pipeline.Spec().Transforms.Raw, &transformsMap); err != nil { - return nil, err - } - for k, v := range transformsMap { - var transform *vector.Transform - if err := mapstructure.Decode(v, &transform); err != nil { - return nil, err - } - transform.Name = addPrefix(ctrl.Pipeline.Namespace(), ctrl.Pipeline.Name(), k) - for i, inputName := range transform.Inputs { - transform.Inputs[i] = addPrefix(ctrl.Pipeline.Namespace(), ctrl.Pipeline.Name(), inputName) - } - transforms = append(transforms, transform) - } - return transforms, nil -} - -func (ctrl *Controller) GetSinks() ([]*vector.Sink, error) { - sinksMap, err := decodeRaw(ctrl.Pipeline.Spec().Sinks.Raw) - if err != nil { - return nil, err - } - var sinks []*vector.Sink - for k, v := range sinksMap { - var sink *vector.Sink - if err := mapstructure.Decode(v, &sink); err != nil { - return nil, err - } - sink.Name = addPrefix(ctrl.Pipeline.Namespace(), ctrl.Pipeline.Name(), k) - for i, inputName := range sink.Inputs { - sink.Inputs[i] = addPrefix(ctrl.Pipeline.Namespace(), ctrl.Pipeline.Name(), inputName) - } - sinks = append(sinks, sink) } - return sinks, nil + return validPipelines, nil } -func (ctrl *Controller) SetSucceesStatus() error { +func SetSucceesStatus(ctx context.Context, client client.Client, p Pipeline) error { var status = true - ctrl.Pipeline.SetConfigCheck(status) - ctrl.Pipeline.SetReason(nil) + p.SetConfigCheck(status) + p.SetReason(nil) - return ctrl.Pipeline.UpdateStatus(ctrl.Ctx, ctrl.Client) + return p.UpdateStatus(ctx, client) } -func (ctrl *Controller) SetFailedStatus(err error) error { +func SetFailedStatus(ctx context.Context, client client.Client, p Pipeline, err error) error { var status = false var reason = err.Error() - ctrl.Pipeline.SetConfigCheck(status) - ctrl.Pipeline.SetReason(&reason) + p.SetConfigCheck(status) + p.SetReason(&reason) - return ctrl.Pipeline.UpdateStatus(ctrl.Ctx, ctrl.Client) + return p.UpdateStatus(ctx, client) } -func (ctrl *Controller) SetLastAppliedPipelineStatus() error { - hash, err := ctrl.GetSpecHash() +func SetLastAppliedPipelineStatus(ctx context.Context, client client.Client, p Pipeline) error { + hash, err := GetSpecHash(p) if err != nil { return err } - ctrl.Pipeline.SetLastAppliedPipeline(hash) + p.SetLastAppliedPipeline(hash) - return ctrl.Pipeline.UpdateStatus(ctrl.Ctx, ctrl.Client) + return p.UpdateStatus(ctx, client) } -func decodeRaw(raw []byte) (map[string]interface{}, error) { - result := make(map[string]interface{}) - if err := json.Unmarshal(raw, &result); err != nil { +func GetVectorPipelines(ctx context.Context, client client.Client) ([]vectorv1alpha1.VectorPipeline, error) { + vps := vectorv1alpha1.VectorPipelineList{} + if err := client.List(ctx, &vps); err != nil { return nil, err } - return result, nil + return vps.Items, nil } -func contains(elems []string, v string) bool { - for _, s := range elems { - if v == s { - return true - } +func GetClusterVectorPipelines(ctx context.Context, client client.Client) ([]vectorv1alpha1.ClusterVectorPipeline, error) { + cvps := vectorv1alpha1.ClusterVectorPipelineList{} + if err := client.List(ctx, &cvps); err != nil { + return nil, err } - return false -} - -func addPrefix(Namespace, Name, componentName string) string { - return generateName(Namespace, Name) + "-" + componentName -} - -func generateName(Namespace, Name string) string { - return Namespace + "-" + Name + return cvps.Items, nil } diff --git a/controllers/factory/pipeline/vectorpipeline/vectorpipeline.go b/controllers/factory/pipeline/vectorpipeline/vectorpipeline.go deleted file mode 100644 index 8c9d9922..00000000 --- a/controllers/factory/pipeline/vectorpipeline/vectorpipeline.go +++ /dev/null @@ -1,76 +0,0 @@ -/* -Copyright 2022. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package vectorpipeline - -import ( - "context" - - vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" - "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -var ( - Type = "noCluster" -) - -type Controller struct { - VectorPipeline *vectorv1alpha1.VectorPipeline - Config []byte -} - -func (ctrl *Controller) Spec() vectorv1alpha1.VectorPipelineSpec { - return ctrl.VectorPipeline.Spec -} - -func (ctrl *Controller) Name() string { - return ctrl.VectorPipeline.Name -} - -func (ctrl *Controller) Namespace() string { - return ctrl.VectorPipeline.Namespace -} - -func (ctrl *Controller) Type() string { - return Type -} - -func (ctrl *Controller) SetConfigCheck(value bool) { - ctrl.VectorPipeline.Status.ConfigCheckResult = &value -} - -func (ctrl *Controller) SetReason(reason *string) { - ctrl.VectorPipeline.Status.Reason = reason -} - -func (ctrl *Controller) GetLastAppliedPipeline() *uint32 { - return ctrl.VectorPipeline.Status.LastAppliedPipelineHash -} - -func (ctrl *Controller) SetLastAppliedPipeline(hash *uint32) { - ctrl.VectorPipeline.Status.LastAppliedPipelineHash = hash -} - -func (ctrl *Controller) UpdateStatus(ctx context.Context, c client.Client) error { - return k8s.UpdateStatus(ctx, ctrl.VectorPipeline, c) -} - -func NewController(vp *vectorv1alpha1.VectorPipeline) *Controller { - return &Controller{ - VectorPipeline: vp, - } -} diff --git a/controllers/factory/vector/vector.go b/controllers/factory/vector/vector.go deleted file mode 100644 index d96bd773..00000000 --- a/controllers/factory/vector/vector.go +++ /dev/null @@ -1,65 +0,0 @@ -/* -Copyright 2022. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package vector - -import ( - vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" - "github.com/mitchellh/mapstructure" -) - -func New(vector *vectorv1alpha1.Vector) *VectorConfig { - sources := []*Source{} - sinks := []*Sink{} - - return &VectorConfig{ - DataDir: vector.Spec.Agent.DataDir, - Api: &vector.Spec.Agent.Api, - Sources: sources, - Sinks: sinks, - } -} - -func Mapper(c ConfigComponent) (map[string]interface{}, error) { - spec := c.GetOptions() - config := &mapstructure.DecoderConfig{ - Result: &spec, - ZeroFields: false, - TagName: "mapper", - IgnoreUntaggedFields: true, - } - decoder, err := mapstructure.NewDecoder(config) - if err != nil { - return nil, err - } - err = decoder.Decode(c) - if err != nil { - return nil, err - } - return spec, nil -} - -func (t Source) GetOptions() map[string]interface{} { - return t.Options -} - -func (t Transform) GetOptions() map[string]interface{} { - return t.Options -} - -func (t Sink) GetOptions() map[string]interface{} { - return t.Options -} diff --git a/controllers/vector_controller.go b/controllers/vector_controller.go index f635b34f..35d4bd6e 100644 --- a/controllers/vector_controller.go +++ b/controllers/vector_controller.go @@ -21,6 +21,7 @@ import ( "time" "github.com/kaasops/vector-operator/controllers/factory/config" + "github.com/kaasops/vector-operator/controllers/factory/pipeline" "github.com/kaasops/vector-operator/controllers/factory/vector/vectoragent" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" @@ -101,16 +102,17 @@ func (r *VectorReconciler) CreateOrUpdateVector(ctx context.Context, v *vectorv1 vaCtrl.SetDefault() // Get Vector Config file - config, err := config.New(ctx, vaCtrl) + pipelines, err := pipeline.GetValidPipelines(ctx, vaCtrl.Client) if err != nil { return ctrl.Result{}, err } - if err := config.FillForVectorAgent(); err != nil { + configBuilder, err := config.NewConfigBuilder(ctx, vaCtrl, pipelines...) + if err != nil { return ctrl.Result{}, err } // Get Config in Json ([]byte) - byteConfig, err := config.GetByteConfig() + byteConfig, err := configBuilder.GetByteConfig() if err != nil { return ctrl.Result{}, err } diff --git a/controllers/vectorpipeline_controller.go b/controllers/vectorpipeline_controller.go index 9338c1a0..fbe22007 100644 --- a/controllers/vectorpipeline_controller.go +++ b/controllers/vectorpipeline_controller.go @@ -30,7 +30,6 @@ import ( "github.com/kaasops/vector-operator/controllers/factory/config" "github.com/kaasops/vector-operator/controllers/factory/config/configcheck" "github.com/kaasops/vector-operator/controllers/factory/pipeline" - "github.com/kaasops/vector-operator/controllers/factory/pipeline/vectorpipeline" "github.com/kaasops/vector-operator/controllers/factory/vector/vectoragent" ) @@ -72,13 +71,8 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque return ctrl.Result{}, nil } - // Generate VectorPipeline Controller - vpCtrl := vectorpipeline.NewController(vectorPipelineCR) - // Generate Pipeline Controller - pCtrl := pipeline.NewController(ctx, r.Client, vpCtrl) - // Check Pipeline hash - checkResult, err := pCtrl.CheckHash() + checkResult, err := pipeline.CheckHash(vectorPipelineCR) if err != nil { return ctrl.Result{}, err } @@ -111,18 +105,20 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque } // Get Vector Config file - config, err := config.New(ctx, vaCtrl) + configBuilder, err := config.NewConfigBuilder(ctx, vaCtrl, vectorPipelineCR) if err != nil { return ctrl.Result{}, err } - if err := config.FillForVectorPipeline(pCtrl); err != nil { - return ctrl.Result{}, err - } - // Get Config in Json ([]byte) - byteConfig, err := config.GetByteConfig() + byteConfig, err := configBuilder.GetByteConfigWithValidate() if err != nil { - return ctrl.Result{}, err + if err := pipeline.SetFailedStatus(ctx, r.Client, vectorPipelineCR, err); err != nil { + return ctrl.Result{}, err + } + if err = pipeline.SetLastAppliedPipelineStatus(ctx, r.Client, vectorPipelineCR); err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{}, nil } // Init CheckConfig @@ -131,10 +127,10 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque // Start ConfigCheck err = configCheck.Run() if _, ok := err.(*configcheck.ConfigCheckError); ok { - if err := pCtrl.SetFailedStatus(err); err != nil { + if err := pipeline.SetFailedStatus(ctx, r.Client, vectorPipelineCR, err); err != nil { return ctrl.Result{}, err } - if err = pCtrl.SetLastAppliedPipelineStatus(); err != nil { + if err := pipeline.SetLastAppliedPipelineStatus(ctx, r.Client, vectorPipelineCR); err != nil { return ctrl.Result{}, err } return ctrl.Result{}, nil @@ -143,11 +139,11 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque return ctrl.Result{}, err } - if err = pCtrl.SetSucceesStatus(); err != nil { + if err = pipeline.SetSucceesStatus(ctx, r.Client, vectorPipelineCR); err != nil { return ctrl.Result{}, err } - if err = pCtrl.SetLastAppliedPipelineStatus(); err != nil { + if err = pipeline.SetLastAppliedPipelineStatus(ctx, r.Client, vectorPipelineCR); err != nil { return ctrl.Result{}, err } From e8c2ebf4805fa1b6dcfdfb7b8645ea30bc949a8c Mon Sep 17 00:00:00 2001 From: Nikita Strelkov Date: Sat, 5 Nov 2022 23:54:01 +0400 Subject: [PATCH 069/316] Rename builder - simplify name --- controllers/clustervectorpipeline_controller.go | 2 +- controllers/factory/config/config_build.go | 14 +++++++------- controllers/vector_controller.go | 2 +- controllers/vectorpipeline_controller.go | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/controllers/clustervectorpipeline_controller.go b/controllers/clustervectorpipeline_controller.go index 7bbdabcd..57adeb16 100644 --- a/controllers/clustervectorpipeline_controller.go +++ b/controllers/clustervectorpipeline_controller.go @@ -105,7 +105,7 @@ func (r *ClusterVectorPipelineReconciler) Reconcile(ctx context.Context, req ctr } // Get Vector Config file - configBuilder, err := config.NewConfigBuilder(ctx, vaCtrl, vectorPipelineCR) + configBuilder, err := config.NewBuilder(ctx, vaCtrl, vectorPipelineCR) if err != nil { return ctrl.Result{}, err } diff --git a/controllers/factory/config/config_build.go b/controllers/factory/config/config_build.go index 077127d6..222ba51d 100644 --- a/controllers/factory/config/config_build.go +++ b/controllers/factory/config/config_build.go @@ -45,22 +45,22 @@ var ( } ) -type ConfigBuilder struct { +type Builder struct { Name string Ctx context.Context vaCtrl *vectoragent.Controller Pipelines []pipeline.Pipeline } -func NewConfigBuilder(ctx context.Context, vaCtrl *vectoragent.Controller, pipelines ...pipeline.Pipeline) (*ConfigBuilder, error) { - return &ConfigBuilder{ +func NewBuilder(ctx context.Context, vaCtrl *vectoragent.Controller, pipelines ...pipeline.Pipeline) (*Builder, error) { + return &Builder{ Ctx: ctx, vaCtrl: vaCtrl, Pipelines: pipelines, }, nil } -func (b *ConfigBuilder) GetByteConfig() ([]byte, error) { +func (b *Builder) GetByteConfig() ([]byte, error) { config, err := b.generateVectorConfig() if err != nil { return nil, err @@ -74,7 +74,7 @@ func (b *ConfigBuilder) GetByteConfig() ([]byte, error) { return data, nil } -func (b *ConfigBuilder) GetByteConfigWithValidate() ([]byte, error) { +func (b *Builder) GetByteConfigWithValidate() ([]byte, error) { validateError := errors.New("type kubernetes_logs only allowed") config, err := b.generateVectorConfig() if err != nil { @@ -101,7 +101,7 @@ func (b *ConfigBuilder) GetByteConfigWithValidate() ([]byte, error) { return data, nil } -func (b *ConfigBuilder) generateVectorConfig() (*VectorConfig, error) { +func (b *Builder) generateVectorConfig() (*VectorConfig, error) { vectorConfig := New(b.vaCtrl.Vector) sources, transforms, sinks, err := b.getComponents() @@ -123,7 +123,7 @@ func (b *ConfigBuilder) generateVectorConfig() (*VectorConfig, error) { return vectorConfig, nil } -func (b *ConfigBuilder) getComponents() (sources []*Source, transforms []*Transform, sinks []*Sink, err error) { +func (b *Builder) getComponents() (sources []*Source, transforms []*Transform, sinks []*Sink, err error) { for _, pipeline := range b.Pipelines { pipelineSources, err := getSources(pipeline, nil) diff --git a/controllers/vector_controller.go b/controllers/vector_controller.go index 35d4bd6e..46d688f1 100644 --- a/controllers/vector_controller.go +++ b/controllers/vector_controller.go @@ -106,7 +106,7 @@ func (r *VectorReconciler) CreateOrUpdateVector(ctx context.Context, v *vectorv1 if err != nil { return ctrl.Result{}, err } - configBuilder, err := config.NewConfigBuilder(ctx, vaCtrl, pipelines...) + configBuilder, err := config.NewBuilder(ctx, vaCtrl, pipelines...) if err != nil { return ctrl.Result{}, err } diff --git a/controllers/vectorpipeline_controller.go b/controllers/vectorpipeline_controller.go index fbe22007..2fdecd09 100644 --- a/controllers/vectorpipeline_controller.go +++ b/controllers/vectorpipeline_controller.go @@ -105,7 +105,7 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque } // Get Vector Config file - configBuilder, err := config.NewConfigBuilder(ctx, vaCtrl, vectorPipelineCR) + configBuilder, err := config.NewBuilder(ctx, vaCtrl, vectorPipelineCR) if err != nil { return ctrl.Result{}, err } From 63b191558e93cad9ce0b8814d37f127b44b8a80d Mon Sep 17 00:00:00 2001 From: Nikita Strelkov Date: Sun, 6 Nov 2022 00:07:59 +0400 Subject: [PATCH 070/316] Make config error more ideomatic --- api/v1alpha1/groupversion_info.go | 4 ++-- controllers/clustervectorpipeline_controller.go | 7 ++++--- .../factory/config/configcheck/configcheck.go | 4 +--- .../config/configcheck/configcheck_error.go | 16 +++++++--------- .../vector/vectoragent/vectoragent_config.go | 3 ++- controllers/vectorpipeline_controller.go | 7 ++++--- 6 files changed, 20 insertions(+), 21 deletions(-) diff --git a/api/v1alpha1/groupversion_info.go b/api/v1alpha1/groupversion_info.go index ee33ae74..11f08d85 100644 --- a/api/v1alpha1/groupversion_info.go +++ b/api/v1alpha1/groupversion_info.go @@ -15,8 +15,8 @@ limitations under the License. */ // Package v1alpha1 contains API Schema definitions for the observability v1alpha1 API group -//+kubebuilder:object:generate=true -//+groupName=observability.kaasops.io +// +kubebuilder:object:generate=true +// +groupName=observability.kaasops.io package v1alpha1 import ( diff --git a/controllers/clustervectorpipeline_controller.go b/controllers/clustervectorpipeline_controller.go index 57adeb16..a2dc5bb9 100644 --- a/controllers/clustervectorpipeline_controller.go +++ b/controllers/clustervectorpipeline_controller.go @@ -18,8 +18,9 @@ package controllers import ( "context" + "errors" - "k8s.io/apimachinery/pkg/api/errors" + api_errors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes" ctrl "sigs.k8s.io/controller-runtime" @@ -126,7 +127,7 @@ func (r *ClusterVectorPipelineReconciler) Reconcile(ctx context.Context, req ctr // Start ConfigCheck err = configCheck.Run() - if _, ok := err.(*configcheck.ConfigCheckError); ok { + if errors.Is(err, configcheck.ValidationError) { if err = pipeline.SetFailedStatus(ctx, r.Client, vectorPipelineCR, err); err != nil { return ctrl.Result{}, err } @@ -158,7 +159,7 @@ func (r *ClusterVectorPipelineReconciler) findClusterVectorPipelineCustomResourc cvp := &vectorv1alpha1.ClusterVectorPipeline{} err := r.Get(ctx, req.NamespacedName, cvp) if err != nil { - if errors.IsNotFound(err) { + if api_errors.IsNotFound(err) { return nil, nil } return nil, err diff --git a/controllers/factory/config/configcheck/configcheck.go b/controllers/factory/config/configcheck/configcheck.go index c7343771..85b2dffc 100644 --- a/controllers/factory/config/configcheck/configcheck.go +++ b/controllers/factory/config/configcheck/configcheck.go @@ -159,9 +159,7 @@ func (cc *ConfigCheck) getCheckResult(pod *corev1.Pod) error { if err != nil { return err } - return &ConfigCheckError{ - Reason: reason, - } + return newValidationError(reason) case "Succeeded": log.Info("Config Check completed successfully") return nil diff --git a/controllers/factory/config/configcheck/configcheck_error.go b/controllers/factory/config/configcheck/configcheck_error.go index 4ecf5e85..3a457466 100644 --- a/controllers/factory/config/configcheck/configcheck_error.go +++ b/controllers/factory/config/configcheck/configcheck_error.go @@ -16,15 +16,13 @@ limitations under the License. package configcheck -type error interface { - Error() string -} +import ( + "errors" + "fmt" +) -type ConfigCheckError struct { - Reason string - Err error -} +var ValidationError = errors.New("config validation error") -func (e *ConfigCheckError) Error() string { - return e.Reason +func newValidationError(reason string) error { + return fmt.Errorf("%w: %s", ValidationError, reason) } diff --git a/controllers/factory/vector/vectoragent/vectoragent_config.go b/controllers/factory/vector/vectoragent/vectoragent_config.go index b17bfdd8..a59e2ac3 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_config.go +++ b/controllers/factory/vector/vectoragent/vectoragent_config.go @@ -18,6 +18,7 @@ package vectoragent import ( "context" + "errors" corev1 "k8s.io/api/core/v1" @@ -32,7 +33,7 @@ func (ctrl *Controller) createVectorAgentConfig(ctx context.Context) (*corev1.Se if ctrl.Vector.Status.LastAppliedConfigHash == nil || *ctrl.Vector.Status.LastAppliedConfigHash != cfgHash { err := configCheck.Run() - if _, ok := err.(*configcheck.ConfigCheckError); ok { + if errors.Is(err, configcheck.ValidationError) { if err := ctrl.SetFailedStatus(ctx, err); err != nil { return nil, err } diff --git a/controllers/vectorpipeline_controller.go b/controllers/vectorpipeline_controller.go index 2fdecd09..3b0481a4 100644 --- a/controllers/vectorpipeline_controller.go +++ b/controllers/vectorpipeline_controller.go @@ -18,8 +18,9 @@ package controllers import ( "context" + "errors" - "k8s.io/apimachinery/pkg/api/errors" + api_errors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes" ctrl "sigs.k8s.io/controller-runtime" @@ -126,7 +127,7 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque // Start ConfigCheck err = configCheck.Run() - if _, ok := err.(*configcheck.ConfigCheckError); ok { + if errors.Is(err, configcheck.ValidationError) { if err := pipeline.SetFailedStatus(ctx, r.Client, vectorPipelineCR, err); err != nil { return ctrl.Result{}, err } @@ -158,7 +159,7 @@ func (r *VectorPipelineReconciler) findVectorPipelineCustomResourceInstance(ctx vectorPipelineCR := &vectorv1alpha1.VectorPipeline{} err := r.Get(ctx, req.NamespacedName, vectorPipelineCR) if err != nil { - if errors.IsNotFound(err) { + if api_errors.IsNotFound(err) { return nil, nil } return nil, err From 96e62c40fcb75daaa7bfae79f3e4e560ff30bfb2 Mon Sep 17 00:00:00 2001 From: Nikita Strelkov Date: Sun, 6 Nov 2022 00:42:12 +0400 Subject: [PATCH 071/316] Fix typo --- controllers/clustervectorpipeline_controller.go | 2 +- controllers/factory/pipeline/pipeline.go | 2 +- controllers/vectorpipeline_controller.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/controllers/clustervectorpipeline_controller.go b/controllers/clustervectorpipeline_controller.go index a2dc5bb9..680c6830 100644 --- a/controllers/clustervectorpipeline_controller.go +++ b/controllers/clustervectorpipeline_controller.go @@ -140,7 +140,7 @@ func (r *ClusterVectorPipelineReconciler) Reconcile(ctx context.Context, req ctr return ctrl.Result{}, err } - if err = pipeline.SetSucceesStatus(ctx, r.Client, vectorPipelineCR); err != nil { + if err = pipeline.SetSuccessStatus(ctx, r.Client, vectorPipelineCR); err != nil { return ctrl.Result{}, err } diff --git a/controllers/factory/pipeline/pipeline.go b/controllers/factory/pipeline/pipeline.go index 8edb9edf..13af5bec 100644 --- a/controllers/factory/pipeline/pipeline.go +++ b/controllers/factory/pipeline/pipeline.go @@ -64,7 +64,7 @@ func GetValidPipelines(ctx context.Context, client client.Client) ([]Pipeline, e return validPipelines, nil } -func SetSucceesStatus(ctx context.Context, client client.Client, p Pipeline) error { +func SetSuccessStatus(ctx context.Context, client client.Client, p Pipeline) error { var status = true p.SetConfigCheck(status) diff --git a/controllers/vectorpipeline_controller.go b/controllers/vectorpipeline_controller.go index 3b0481a4..c6d89918 100644 --- a/controllers/vectorpipeline_controller.go +++ b/controllers/vectorpipeline_controller.go @@ -140,7 +140,7 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque return ctrl.Result{}, err } - if err = pipeline.SetSucceesStatus(ctx, r.Client, vectorPipelineCR); err != nil { + if err = pipeline.SetSuccessStatus(ctx, r.Client, vectorPipelineCR); err != nil { return ctrl.Result{}, err } From 7ba643b36ee9543d96e16129987c382744e5a8f1 Mon Sep 17 00:00:00 2001 From: Nikita Strelkov Date: Sun, 6 Nov 2022 00:43:48 +0400 Subject: [PATCH 072/316] Simplify code --- controllers/factory/pipeline/pipeline.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/controllers/factory/pipeline/pipeline.go b/controllers/factory/pipeline/pipeline.go index 13af5bec..f6d59e6d 100644 --- a/controllers/factory/pipeline/pipeline.go +++ b/controllers/factory/pipeline/pipeline.go @@ -65,19 +65,16 @@ func GetValidPipelines(ctx context.Context, client client.Client) ([]Pipeline, e } func SetSuccessStatus(ctx context.Context, client client.Client, p Pipeline) error { - var status = true - - p.SetConfigCheck(status) + p.SetConfigCheck(true) p.SetReason(nil) return p.UpdateStatus(ctx, client) } func SetFailedStatus(ctx context.Context, client client.Client, p Pipeline, err error) error { - var status = false var reason = err.Error() - p.SetConfigCheck(status) + p.SetConfigCheck(true) p.SetReason(&reason) return p.UpdateStatus(ctx, client) From 6544220b0d340237c1a2673a632f6736af2f42a5 Mon Sep 17 00:00:00 2001 From: Nikita Strelkov Date: Sun, 6 Nov 2022 00:49:11 +0400 Subject: [PATCH 073/316] Simplify errors checking No need in comparing err != nil two times, because errors.IsAlreadyExists works fine with nil error, and after checking `errors.IsAlreadyExists(err)` we just need to return `err` whether it is nil or not --- controllers/factory/utils/k8s/k8s.go | 56 +++++++--------------------- 1 file changed, 14 insertions(+), 42 deletions(-) diff --git a/controllers/factory/utils/k8s/k8s.go b/controllers/factory/utils/k8s/k8s.go index 8247a4b1..1c09eed0 100644 --- a/controllers/factory/utils/k8s/k8s.go +++ b/controllers/factory/utils/k8s/k8s.go @@ -133,7 +133,7 @@ func reconcileService(obj runtime.Object, c client.Client) error { desired := obj.(*corev1.Service) err := c.Create(context.TODO(), desired) - if err != nil && errors.IsAlreadyExists(err) { + if errors.IsAlreadyExists(err) { err := c.Get(context.TODO(), client.ObjectKeyFromObject(desired), existing) if err != nil { return err @@ -145,11 +145,7 @@ func reconcileService(obj runtime.Object, c client.Client) error { return err } } - if err != nil && !errors.IsAlreadyExists(err) { - return err - } - - return nil + return err } func reconcileSecret(obj runtime.Object, c client.Client) error { @@ -158,7 +154,7 @@ func reconcileSecret(obj runtime.Object, c client.Client) error { desired := obj.(*corev1.Secret) err := c.Create(context.TODO(), desired) - if err != nil && errors.IsAlreadyExists(err) { + if errors.IsAlreadyExists(err) { err := c.Get(context.TODO(), client.ObjectKeyFromObject(desired), existing) if err != nil { return err @@ -169,11 +165,7 @@ func reconcileSecret(obj runtime.Object, c client.Client) error { return c.Update(context.TODO(), existing) } } - if err != nil && !errors.IsAlreadyExists(err) { - return err - } - - return nil + return err } func reconcileDaemonSet(obj runtime.Object, c client.Client) error { @@ -182,7 +174,7 @@ func reconcileDaemonSet(obj runtime.Object, c client.Client) error { desired := obj.(*appsv1.DaemonSet) err := c.Create(context.TODO(), desired) - if err != nil && errors.IsAlreadyExists(err) { + if errors.IsAlreadyExists(err) { err := c.Get(context.TODO(), client.ObjectKeyFromObject(desired), existing) if err != nil { return err @@ -193,11 +185,7 @@ func reconcileDaemonSet(obj runtime.Object, c client.Client) error { return c.Update(context.TODO(), existing) } } - if err != nil && !errors.IsAlreadyExists(err) { - return err - } - - return nil + return err } func reconcileStatefulSet(obj runtime.Object, c client.Client) error { @@ -206,7 +194,7 @@ func reconcileStatefulSet(obj runtime.Object, c client.Client) error { desired := obj.(*appsv1.StatefulSet) err := c.Create(context.TODO(), desired) - if err != nil && errors.IsAlreadyExists(err) { + if errors.IsAlreadyExists(err) { err := c.Get(context.TODO(), client.ObjectKeyFromObject(desired), existing) if err != nil { return err @@ -217,11 +205,7 @@ func reconcileStatefulSet(obj runtime.Object, c client.Client) error { return c.Update(context.TODO(), existing) } } - if err != nil && !errors.IsAlreadyExists(err) { - return err - } - - return nil + return err } func reconcileServiceAccount(obj runtime.Object, c client.Client) error { @@ -230,17 +214,13 @@ func reconcileServiceAccount(obj runtime.Object, c client.Client) error { desired := obj.(*corev1.ServiceAccount) err := c.Create(context.TODO(), desired) - if err != nil && errors.IsAlreadyExists(err) { + if errors.IsAlreadyExists(err) { err := c.Get(context.TODO(), client.ObjectKeyFromObject(desired), existing) if err != nil { return err } } - if err != nil && !errors.IsAlreadyExists(err) { - return err - } - - return nil + return err } func reconcileClusterRole(obj runtime.Object, c client.Client) error { @@ -249,7 +229,7 @@ func reconcileClusterRole(obj runtime.Object, c client.Client) error { desired := obj.(*rbacv1.ClusterRole) err := c.Create(context.TODO(), desired) - if err != nil && errors.IsAlreadyExists(err) { + if errors.IsAlreadyExists(err) { err := c.Get(context.TODO(), client.ObjectKeyFromObject(desired), existing) if err != nil { return err @@ -259,11 +239,7 @@ func reconcileClusterRole(obj runtime.Object, c client.Client) error { return c.Update(context.TODO(), existing) } } - if err != nil && !errors.IsAlreadyExists(err) { - return err - } - - return nil + return err } func reconcileClusterRoleBinding(obj runtime.Object, c client.Client) error { @@ -272,7 +248,7 @@ func reconcileClusterRoleBinding(obj runtime.Object, c client.Client) error { desired := obj.(*rbacv1.ClusterRoleBinding) err := c.Create(context.TODO(), desired) - if err != nil && errors.IsAlreadyExists(err) { + if errors.IsAlreadyExists(err) { err := c.Get(context.TODO(), client.ObjectKeyFromObject(desired), existing) if err != nil { return err @@ -283,11 +259,7 @@ func reconcileClusterRoleBinding(obj runtime.Object, c client.Client) error { return c.Update(context.TODO(), existing) } } - if err != nil && !errors.IsAlreadyExists(err) { - return err - } - - return nil + return err } func NamespaceNameToLabel(namespace string) string { From 06da0da039a169c1d4ed6caa6dded522d91838a4 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Mon, 7 Nov 2022 10:10:43 +0200 Subject: [PATCH 074/316] return nil on succesfull reconcle --- controllers/factory/utils/k8s/k8s.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/controllers/factory/utils/k8s/k8s.go b/controllers/factory/utils/k8s/k8s.go index 1c09eed0..05d8b1ae 100644 --- a/controllers/factory/utils/k8s/k8s.go +++ b/controllers/factory/utils/k8s/k8s.go @@ -141,9 +141,9 @@ func reconcileService(obj runtime.Object, c client.Client) error { if !equality.Semantic.DeepEqual(existing, desired) { existing.Spec = desired.Spec existing.Labels = desired.Labels - err := c.Update(context.TODO(), existing) - return err + return c.Update(context.TODO(), existing) } + return nil } return err } @@ -164,6 +164,7 @@ func reconcileSecret(obj runtime.Object, c client.Client) error { existing.Labels = desired.Labels return c.Update(context.TODO(), existing) } + return nil } return err } @@ -184,6 +185,7 @@ func reconcileDaemonSet(obj runtime.Object, c client.Client) error { existing.Labels = desired.Labels return c.Update(context.TODO(), existing) } + return nil } return err } @@ -204,6 +206,7 @@ func reconcileStatefulSet(obj runtime.Object, c client.Client) error { existing.Labels = desired.Labels return c.Update(context.TODO(), existing) } + return nil } return err } @@ -219,6 +222,7 @@ func reconcileServiceAccount(obj runtime.Object, c client.Client) error { if err != nil { return err } + return nil } return err } @@ -238,6 +242,7 @@ func reconcileClusterRole(obj runtime.Object, c client.Client) error { existing.Rules = desired.Rules return c.Update(context.TODO(), existing) } + return nil } return err } @@ -258,6 +263,7 @@ func reconcileClusterRoleBinding(obj runtime.Object, c client.Client) error { existing.Subjects = desired.Subjects return c.Update(context.TODO(), existing) } + return nil } return err } From 67b81e5d56d053b42a0aa65c49ca75a330a7b7ec Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Mon, 7 Nov 2022 17:21:17 +0200 Subject: [PATCH 075/316] added reconcile config func --- .../clustervectorpipeline_controller.go | 45 +--------------- controllers/factory/config/config.go | 53 +++++++++++++++++++ controllers/vectorpipeline_controller.go | 43 +-------------- 3 files changed, 55 insertions(+), 86 deletions(-) diff --git a/controllers/clustervectorpipeline_controller.go b/controllers/clustervectorpipeline_controller.go index 680c6830..77965e1d 100644 --- a/controllers/clustervectorpipeline_controller.go +++ b/controllers/clustervectorpipeline_controller.go @@ -18,7 +18,6 @@ package controllers import ( "context" - "errors" api_errors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" @@ -29,7 +28,6 @@ import ( vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" "github.com/kaasops/vector-operator/controllers/factory/config" - "github.com/kaasops/vector-operator/controllers/factory/config/configcheck" "github.com/kaasops/vector-operator/controllers/factory/pipeline" "github.com/kaasops/vector-operator/controllers/factory/vector/vectoragent" ) @@ -104,50 +102,9 @@ func (r *ClusterVectorPipelineReconciler) Reconcile(ctx context.Context, req ctr if vaCtrl.Vector.Spec.Agent.DataDir == "" { vaCtrl.Vector.Spec.Agent.DataDir = "/vector-data-dir" } - - // Get Vector Config file - configBuilder, err := config.NewBuilder(ctx, vaCtrl, vectorPipelineCR) - if err != nil { - return ctrl.Result{}, err - } - - byteConfig, err := configBuilder.GetByteConfigWithValidate() - if err != nil { - if err := pipeline.SetFailedStatus(ctx, r.Client, vectorPipelineCR, err); err != nil { - return ctrl.Result{}, err - } - if err = pipeline.SetLastAppliedPipelineStatus(ctx, r.Client, vectorPipelineCR); err != nil { - return ctrl.Result{}, err - } - return ctrl.Result{}, nil - } - - // Init CheckConfig - configCheck := configcheck.New(ctx, byteConfig, vaCtrl.Client, vaCtrl.ClientSet, vaCtrl.Vector.Name, vaCtrl.Vector.Namespace, vaCtrl.Vector.Spec.Agent.Image) - - // Start ConfigCheck - err = configCheck.Run() - if errors.Is(err, configcheck.ValidationError) { - if err = pipeline.SetFailedStatus(ctx, r.Client, vectorPipelineCR, err); err != nil { - return ctrl.Result{}, err - } - if err = pipeline.SetLastAppliedPipelineStatus(ctx, r.Client, vectorPipelineCR); err != nil { - return ctrl.Result{}, err - } - return ctrl.Result{}, nil - } - if err != nil { + if err := config.ReconcileConfig(ctx, r.Client, vectorPipelineCR, vaCtrl); err != nil { return ctrl.Result{}, err } - - if err = pipeline.SetSuccessStatus(ctx, r.Client, vectorPipelineCR); err != nil { - return ctrl.Result{}, err - } - - if err = pipeline.SetLastAppliedPipelineStatus(ctx, r.Client, vectorPipelineCR); err != nil { - return ctrl.Result{}, err - } - } log.Info("finish Reconcile VectorPipeline") diff --git a/controllers/factory/config/config.go b/controllers/factory/config/config.go index fe12b1a4..f4967007 100644 --- a/controllers/factory/config/config.go +++ b/controllers/factory/config/config.go @@ -17,10 +17,63 @@ limitations under the License. package config import ( + "context" + "errors" + vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" + "github.com/kaasops/vector-operator/controllers/factory/config/configcheck" + "github.com/kaasops/vector-operator/controllers/factory/pipeline" + "github.com/kaasops/vector-operator/controllers/factory/vector/vectoragent" "github.com/mitchellh/mapstructure" + "sigs.k8s.io/controller-runtime/pkg/client" ) +func ReconcileConfig(ctx context.Context, client client.Client, p pipeline.Pipeline, vaCtrl *vectoragent.Controller) error { + // Get Vector Config file + configBuilder, err := NewBuilder(ctx, vaCtrl, p) + if err != nil { + return err + } + + byteConfig, err := configBuilder.GetByteConfigWithValidate() + if err != nil { + if err := pipeline.SetFailedStatus(ctx, client, p, err); err != nil { + return err + } + if err = pipeline.SetLastAppliedPipelineStatus(ctx, client, p); err != nil { + return err + } + return nil + } + + // Init CheckConfig + configCheck := configcheck.New(ctx, byteConfig, vaCtrl.Client, vaCtrl.ClientSet, vaCtrl.Vector.Name, vaCtrl.Vector.Namespace, vaCtrl.Vector.Spec.Agent.Image) + + // Start ConfigCheck + err = configCheck.Run() + if errors.Is(err, configcheck.ValidationError) { + if err = pipeline.SetFailedStatus(ctx, client, p, err); err != nil { + return err + } + if err = pipeline.SetLastAppliedPipelineStatus(ctx, client, p); err != nil { + return err + } + return nil + } + if err != nil { + return err + } + + if err = pipeline.SetSuccessStatus(ctx, client, p); err != nil { + return err + } + + if err = pipeline.SetLastAppliedPipelineStatus(ctx, client, p); err != nil { + return err + } + return nil +} + func New(vector *vectorv1alpha1.Vector) *VectorConfig { sources := []*Source{} sinks := []*Sink{} diff --git a/controllers/vectorpipeline_controller.go b/controllers/vectorpipeline_controller.go index c6d89918..a2f3e0f5 100644 --- a/controllers/vectorpipeline_controller.go +++ b/controllers/vectorpipeline_controller.go @@ -18,7 +18,6 @@ package controllers import ( "context" - "errors" api_errors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" @@ -29,7 +28,6 @@ import ( vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" "github.com/kaasops/vector-operator/controllers/factory/config" - "github.com/kaasops/vector-operator/controllers/factory/config/configcheck" "github.com/kaasops/vector-operator/controllers/factory/pipeline" "github.com/kaasops/vector-operator/controllers/factory/vector/vectoragent" ) @@ -105,46 +103,7 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque vaCtrl.Vector.Spec.Agent.DataDir = "/vector-data-dir" } - // Get Vector Config file - configBuilder, err := config.NewBuilder(ctx, vaCtrl, vectorPipelineCR) - if err != nil { - return ctrl.Result{}, err - } - - byteConfig, err := configBuilder.GetByteConfigWithValidate() - if err != nil { - if err := pipeline.SetFailedStatus(ctx, r.Client, vectorPipelineCR, err); err != nil { - return ctrl.Result{}, err - } - if err = pipeline.SetLastAppliedPipelineStatus(ctx, r.Client, vectorPipelineCR); err != nil { - return ctrl.Result{}, err - } - return ctrl.Result{}, nil - } - - // Init CheckConfig - configCheck := configcheck.New(ctx, byteConfig, vaCtrl.Client, vaCtrl.ClientSet, vaCtrl.Vector.Name, vaCtrl.Vector.Namespace, vaCtrl.Vector.Spec.Agent.Image) - - // Start ConfigCheck - err = configCheck.Run() - if errors.Is(err, configcheck.ValidationError) { - if err := pipeline.SetFailedStatus(ctx, r.Client, vectorPipelineCR, err); err != nil { - return ctrl.Result{}, err - } - if err := pipeline.SetLastAppliedPipelineStatus(ctx, r.Client, vectorPipelineCR); err != nil { - return ctrl.Result{}, err - } - return ctrl.Result{}, nil - } - if err != nil { - return ctrl.Result{}, err - } - - if err = pipeline.SetSuccessStatus(ctx, r.Client, vectorPipelineCR); err != nil { - return ctrl.Result{}, err - } - - if err = pipeline.SetLastAppliedPipelineStatus(ctx, r.Client, vectorPipelineCR); err != nil { + if err := config.ReconcileConfig(ctx, r.Client, vectorPipelineCR, vaCtrl); err != nil { return ctrl.Result{}, err } From 821a5414ed307ab01546929ffbce1369ab218c01 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Mon, 7 Nov 2022 17:33:05 +0200 Subject: [PATCH 076/316] fix set failed status --- controllers/factory/pipeline/pipeline.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controllers/factory/pipeline/pipeline.go b/controllers/factory/pipeline/pipeline.go index f6d59e6d..699dc894 100644 --- a/controllers/factory/pipeline/pipeline.go +++ b/controllers/factory/pipeline/pipeline.go @@ -74,7 +74,7 @@ func SetSuccessStatus(ctx context.Context, client client.Client, p Pipeline) err func SetFailedStatus(ctx context.Context, client client.Client, p Pipeline, err error) error { var reason = err.Error() - p.SetConfigCheck(true) + p.SetConfigCheck(false) p.SetReason(&reason) return p.UpdateStatus(ctx, client) From fc9f72dac5f83f244fb9e458460a6992ec26de5a Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Wed, 9 Nov 2022 14:41:02 +0200 Subject: [PATCH 077/316] Cleadup quick start doc Signed-off-by: Zemtsov Vladimir --- docs/quick-start.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/quick-start.md b/docs/quick-start.md index 399fd33a..15d3ecf7 100644 --- a/docs/quick-start.md +++ b/docs/quick-start.md @@ -14,7 +14,7 @@ kubectl apply -f config/crd/bases/observability.kaasops.io_clustervectorpipeline ## Start Vector Operator ### Create namespace for Vector Operator ```bash -kubectl create namespace vector-operator-system +kubectl create namespace vector ``` ### Create RBAC @@ -25,7 +25,7 @@ apiVersion: v1 kind: Secret metadata: name: vector-operator-sa-token - namespace: vector-operator-system + namespace: vector annotations: kubernetes.io/service-account.name: vector-operator type: kubernetes.io/service-account-token @@ -34,7 +34,7 @@ EOF ```bash # Create ServiceAccount for Vector Operator -kubectl create serviceaccount -n vector-operator-system vector-operator +kubectl create serviceaccount -n vector vector-operator # Create ClusterRole for Vector Operator cat < Date: Wed, 9 Nov 2022 14:41:22 +0200 Subject: [PATCH 078/316] Add RoadMap to Readme Signed-off-by: Zemtsov Vladimir --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 9f5e01ca..ac393f96 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,8 @@ The operator deploys and configures a vector agent daemonset on every node to co - [ ] Vector config optimization - [ ] Vector aggregator support +[RoadMap](https://github.com/orgs/kaasops/projects/1) + ## Documentation - Quick start [doc](https://github.com/kaasops/vector-operator/blob/main/docs/quick-start.md) - Design [doc](https://github.com/kaasops/vector-operator/blob/main/docs/design.md) From 01c18e9ce318051213a0ae4fa6e77de0fd40cbeb Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Wed, 9 Nov 2022 16:07:28 +0200 Subject: [PATCH 079/316] Fix context-in-struct warning Signed-off-by: Zemtsov Vladimir --- controllers/factory/config/config.go | 9 +++------ controllers/factory/config/config_build.go | 7 ++----- .../factory/config/configcheck/configcheck.go | 17 +++++++---------- .../vector/vectoragent/vectoragent_config.go | 4 ++-- controllers/vector_controller.go | 5 +---- 5 files changed, 15 insertions(+), 27 deletions(-) diff --git a/controllers/factory/config/config.go b/controllers/factory/config/config.go index f4967007..70fc3595 100644 --- a/controllers/factory/config/config.go +++ b/controllers/factory/config/config.go @@ -30,10 +30,7 @@ import ( func ReconcileConfig(ctx context.Context, client client.Client, p pipeline.Pipeline, vaCtrl *vectoragent.Controller) error { // Get Vector Config file - configBuilder, err := NewBuilder(ctx, vaCtrl, p) - if err != nil { - return err - } + configBuilder := NewBuilder(vaCtrl, p) byteConfig, err := configBuilder.GetByteConfigWithValidate() if err != nil { @@ -47,10 +44,10 @@ func ReconcileConfig(ctx context.Context, client client.Client, p pipeline.Pipel } // Init CheckConfig - configCheck := configcheck.New(ctx, byteConfig, vaCtrl.Client, vaCtrl.ClientSet, vaCtrl.Vector.Name, vaCtrl.Vector.Namespace, vaCtrl.Vector.Spec.Agent.Image) + configCheck := configcheck.New(byteConfig, vaCtrl.Client, vaCtrl.ClientSet, vaCtrl.Vector.Name, vaCtrl.Vector.Namespace, vaCtrl.Vector.Spec.Agent.Image) // Start ConfigCheck - err = configCheck.Run() + err = configCheck.Run(ctx) if errors.Is(err, configcheck.ValidationError) { if err = pipeline.SetFailedStatus(ctx, client, p, err); err != nil { return err diff --git a/controllers/factory/config/config_build.go b/controllers/factory/config/config_build.go index 222ba51d..8512d3ea 100644 --- a/controllers/factory/config/config_build.go +++ b/controllers/factory/config/config_build.go @@ -17,7 +17,6 @@ limitations under the License. package config import ( - "context" "encoding/json" "errors" @@ -47,17 +46,15 @@ var ( type Builder struct { Name string - Ctx context.Context vaCtrl *vectoragent.Controller Pipelines []pipeline.Pipeline } -func NewBuilder(ctx context.Context, vaCtrl *vectoragent.Controller, pipelines ...pipeline.Pipeline) (*Builder, error) { +func NewBuilder(vaCtrl *vectoragent.Controller, pipelines ...pipeline.Pipeline) *Builder { return &Builder{ - Ctx: ctx, vaCtrl: vaCtrl, Pipelines: pipelines, - }, nil + } } func (b *Builder) GetByteConfig() ([]byte, error) { diff --git a/controllers/factory/config/configcheck/configcheck.go b/controllers/factory/config/configcheck/configcheck.go index 85b2dffc..c2d7f666 100644 --- a/controllers/factory/config/configcheck/configcheck.go +++ b/controllers/factory/config/configcheck/configcheck.go @@ -31,8 +31,6 @@ import ( ) type ConfigCheck struct { - Ctx context.Context - Config []byte Client client.Client @@ -44,9 +42,8 @@ type ConfigCheck struct { Hash string } -func New(ctx context.Context, config []byte, c client.Client, cs *kubernetes.Clientset, name, namespace, image string) *ConfigCheck { +func New(config []byte, c client.Client, cs *kubernetes.Clientset, name, namespace, image string) *ConfigCheck { return &ConfigCheck{ - Ctx: ctx, Config: config, Client: c, ClientSet: cs, @@ -56,7 +53,7 @@ func New(ctx context.Context, config []byte, c client.Client, cs *kubernetes.Cli } } -func (cc *ConfigCheck) Run() error { +func (cc *ConfigCheck) Run(ctx context.Context) error { log := log.FromContext(context.TODO()).WithValues("Vector ConfigCheck", cc.Name) log.Info("start ConfigCheck") @@ -71,7 +68,7 @@ func (cc *ConfigCheck) Run() error { return err } - if err := cc.checkVectorConfigCheckPod(); err != nil { + if err := cc.checkVectorConfigCheckPod(ctx); err != nil { return err } @@ -96,7 +93,7 @@ func (cc *ConfigCheck) ensureVectorConfigCheckConfig() error { return k8s.CreateOrUpdateSecret(vectorConfigCheckSecret, cc.Client) } -func (cc *ConfigCheck) checkVectorConfigCheckPod() error { +func (cc *ConfigCheck) checkVectorConfigCheckPod(ctx context.Context) error { vectorConfigCheckPod := cc.createVectorConfigCheckPod() err := k8s.CreatePod(vectorConfigCheckPod, cc.Client) @@ -109,7 +106,7 @@ func (cc *ConfigCheck) checkVectorConfigCheckPod() error { return err } - err = cc.cleanup() + err = cc.cleanup(ctx) if err != nil { return err } @@ -167,14 +164,14 @@ func (cc *ConfigCheck) getCheckResult(pod *corev1.Pod) error { } } -func (cc *ConfigCheck) cleanup() error { +func (cc *ConfigCheck) cleanup(ctx context.Context) error { listOpts, err := cc.gcRListOptions() if err != nil { return err } podlist := corev1.PodList{} - err = cc.Client.List(cc.Ctx, &podlist, &listOpts) + err = cc.Client.List(ctx, &podlist, &listOpts) if err != nil { return err } diff --git a/controllers/factory/vector/vectoragent/vectoragent_config.go b/controllers/factory/vector/vectoragent/vectoragent_config.go index a59e2ac3..a32d418d 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_config.go +++ b/controllers/factory/vector/vectoragent/vectoragent_config.go @@ -29,10 +29,10 @@ import ( func (ctrl *Controller) createVectorAgentConfig(ctx context.Context) (*corev1.Secret, error) { cfgHash := hash.Get(ctrl.Config) - configCheck := configcheck.New(ctx, ctrl.Config, ctrl.Client, ctrl.ClientSet, ctrl.Vector.Name, ctrl.Vector.Namespace, ctrl.Vector.Spec.Agent.Image) + configCheck := configcheck.New(ctrl.Config, ctrl.Client, ctrl.ClientSet, ctrl.Vector.Name, ctrl.Vector.Namespace, ctrl.Vector.Spec.Agent.Image) if ctrl.Vector.Status.LastAppliedConfigHash == nil || *ctrl.Vector.Status.LastAppliedConfigHash != cfgHash { - err := configCheck.Run() + err := configCheck.Run(ctx) if errors.Is(err, configcheck.ValidationError) { if err := ctrl.SetFailedStatus(ctx, err); err != nil { return nil, err diff --git a/controllers/vector_controller.go b/controllers/vector_controller.go index 46d688f1..ce86e071 100644 --- a/controllers/vector_controller.go +++ b/controllers/vector_controller.go @@ -106,10 +106,7 @@ func (r *VectorReconciler) CreateOrUpdateVector(ctx context.Context, v *vectorv1 if err != nil { return ctrl.Result{}, err } - configBuilder, err := config.NewBuilder(ctx, vaCtrl, pipelines...) - if err != nil { - return ctrl.Result{}, err - } + configBuilder := config.NewBuilder(vaCtrl, pipelines...) // Get Config in Json ([]byte) byteConfig, err := configBuilder.GetByteConfig() From d6b6898a02d0d48e002794fa9364486266502a20 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Thu, 10 Nov 2022 09:06:20 +0200 Subject: [PATCH 080/316] Disable reconcile by time (#38) * improve reconcile kubernetes objects --- controllers/factory/utils/k8s/k8s.go | 47 ++++++++++++++++++++++------ controllers/vector_controller.go | 11 +++++-- 2 files changed, 45 insertions(+), 13 deletions(-) diff --git a/controllers/factory/utils/k8s/k8s.go b/controllers/factory/utils/k8s/k8s.go index 05d8b1ae..a19fdbc4 100644 --- a/controllers/factory/utils/k8s/k8s.go +++ b/controllers/factory/utils/k8s/k8s.go @@ -138,9 +138,12 @@ func reconcileService(obj runtime.Object, c client.Client) error { if err != nil { return err } - if !equality.Semantic.DeepEqual(existing, desired) { - existing.Spec = desired.Spec + if !equality.Semantic.DeepDerivative(desired.ObjectMeta.Labels, existing.ObjectMeta.Labels) || + !equality.Semantic.DeepDerivative(desired.Annotations, existing.Annotations) || + !equality.Semantic.DeepDerivative(desired.Spec, existing.Spec) { existing.Labels = desired.Labels + existing.Annotations = desired.Annotations + existing.Spec = desired.Spec return c.Update(context.TODO(), existing) } return nil @@ -159,9 +162,12 @@ func reconcileSecret(obj runtime.Object, c client.Client) error { if err != nil { return err } - if !equality.Semantic.DeepEqual(existing, desired) { - existing.Data = desired.Data + if !equality.Semantic.DeepDerivative(desired.ObjectMeta.Labels, existing.ObjectMeta.Labels) || + !equality.Semantic.DeepDerivative(desired.Annotations, existing.Annotations) || + !equality.Semantic.DeepDerivative(desired.Data, existing.Data) { existing.Labels = desired.Labels + existing.Annotations = desired.Annotations + existing.Data = desired.Data return c.Update(context.TODO(), existing) } return nil @@ -180,9 +186,12 @@ func reconcileDaemonSet(obj runtime.Object, c client.Client) error { if err != nil { return err } - if !equality.Semantic.DeepEqual(existing, desired) { - existing.Spec = desired.Spec + if !equality.Semantic.DeepDerivative(desired.ObjectMeta.Labels, existing.ObjectMeta.Labels) || + !equality.Semantic.DeepDerivative(desired.Annotations, existing.Annotations) || + !equality.Semantic.DeepDerivative(desired.Spec, existing.Spec) { existing.Labels = desired.Labels + existing.Annotations = desired.Annotations + existing.Spec = desired.Spec return c.Update(context.TODO(), existing) } return nil @@ -201,9 +210,12 @@ func reconcileStatefulSet(obj runtime.Object, c client.Client) error { if err != nil { return err } - if !equality.Semantic.DeepEqual(existing, desired) { - existing.Spec = desired.Spec + if !equality.Semantic.DeepDerivative(desired.ObjectMeta.Labels, existing.ObjectMeta.Labels) || + !equality.Semantic.DeepDerivative(desired.Annotations, existing.Annotations) || + !equality.Semantic.DeepDerivative(desired.Spec, existing.Spec) { existing.Labels = desired.Labels + existing.Annotations = desired.Annotations + existing.Spec = desired.Spec return c.Update(context.TODO(), existing) } return nil @@ -222,6 +234,12 @@ func reconcileServiceAccount(obj runtime.Object, c client.Client) error { if err != nil { return err } + if !equality.Semantic.DeepDerivative(desired.ObjectMeta.Labels, existing.ObjectMeta.Labels) || + !equality.Semantic.DeepDerivative(desired.Annotations, existing.Annotations) { + existing.Labels = desired.Labels + existing.Annotations = desired.Annotations + return c.Update(context.TODO(), existing) + } return nil } return err @@ -238,7 +256,11 @@ func reconcileClusterRole(obj runtime.Object, c client.Client) error { if err != nil { return err } - if !equality.Semantic.DeepEqual(existing, desired) { + if !equality.Semantic.DeepDerivative(desired.ObjectMeta.Labels, existing.ObjectMeta.Labels) || + !equality.Semantic.DeepDerivative(desired.Annotations, existing.Annotations) || + !equality.Semantic.DeepDerivative(desired.Rules, existing.Rules) { + existing.Labels = desired.Labels + existing.Annotations = desired.Annotations existing.Rules = desired.Rules return c.Update(context.TODO(), existing) } @@ -258,7 +280,12 @@ func reconcileClusterRoleBinding(obj runtime.Object, c client.Client) error { if err != nil { return err } - if !equality.Semantic.DeepEqual(existing, desired) { + if !equality.Semantic.DeepDerivative(desired.ObjectMeta.Labels, existing.ObjectMeta.Labels) || + !equality.Semantic.DeepDerivative(desired.Annotations, existing.Annotations) || + !equality.Semantic.DeepDerivative(desired.RoleRef, existing.RoleRef) || + !equality.Semantic.DeepDerivative(desired.Subjects, existing.Subjects) { + existing.Labels = desired.Labels + existing.Annotations = desired.Annotations existing.RoleRef = desired.RoleRef existing.Subjects = desired.Subjects return c.Update(context.TODO(), existing) diff --git a/controllers/vector_controller.go b/controllers/vector_controller.go index ce86e071..16508455 100644 --- a/controllers/vector_controller.go +++ b/controllers/vector_controller.go @@ -18,11 +18,13 @@ package controllers import ( "context" - "time" "github.com/kaasops/vector-operator/controllers/factory/config" "github.com/kaasops/vector-operator/controllers/factory/pipeline" "github.com/kaasops/vector-operator/controllers/factory/vector/vectoragent" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes" @@ -92,6 +94,10 @@ func (r *VectorReconciler) findVectorCustomResourceInstance(ctx context.Context, func (r *VectorReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&vectorv1alpha1.Vector{}). + Owns(&appsv1.DaemonSet{}). + Owns(&corev1.Service{}). + Owns(&corev1.Secret{}). + Owns(&corev1.ServiceAccount{}). Complete(r) } @@ -119,6 +125,5 @@ func (r *VectorReconciler) CreateOrUpdateVector(ctx context.Context, v *vectorv1 if err := vaCtrl.EnsureVectorAgent(); err != nil { return ctrl.Result{}, err } - - return ctrl.Result{RequeueAfter: 15 * time.Second}, nil + return ctrl.Result{}, nil } From ba1ce1dcf3447e54d606683c918c96f3c141f961 Mon Sep 17 00:00:00 2001 From: Ilya Batyukov Date: Thu, 10 Nov 2022 10:17:04 +0300 Subject: [PATCH 081/316] add helm chart --- helm/.helmignore | 23 + helm/Chart.yaml | 28 + helm/templates/_helpers.tpl | 62 ++ helm/templates/crds.yaml | 1506 ++++++++++++++++++++++++++++ helm/templates/deployment.yaml | 40 + helm/templates/monitor.yaml | 18 + helm/templates/rbac.yaml | 125 +++ helm/templates/serviceaccount.yaml | 13 + helm/values.yaml | 31 + 9 files changed, 1846 insertions(+) create mode 100644 helm/.helmignore create mode 100644 helm/Chart.yaml create mode 100644 helm/templates/_helpers.tpl create mode 100644 helm/templates/crds.yaml create mode 100644 helm/templates/deployment.yaml create mode 100644 helm/templates/monitor.yaml create mode 100644 helm/templates/rbac.yaml create mode 100644 helm/templates/serviceaccount.yaml create mode 100644 helm/values.yaml diff --git a/helm/.helmignore b/helm/.helmignore new file mode 100644 index 00000000..0e8a0eb3 --- /dev/null +++ b/helm/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/helm/Chart.yaml b/helm/Chart.yaml new file mode 100644 index 00000000..39087628 --- /dev/null +++ b/helm/Chart.yaml @@ -0,0 +1,28 @@ +apiVersion: v2 +name: vector-operator +description: A Helm chart A Helm chart to install vector-operator + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.0.1 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "0.0.1" + +home: https://github.com/kaasops/vector-operator +sources: + - https://github.com/kaasops/vector-operator \ No newline at end of file diff --git a/helm/templates/_helpers.tpl b/helm/templates/_helpers.tpl new file mode 100644 index 00000000..dc13106d --- /dev/null +++ b/helm/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "vector-operator.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "vector-operator.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "vector-operator.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "vector-operator.labels" -}} +helm.sh/chart: {{ include "vector-operator.chart" . }} +{{ include "vector-operator.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "vector-operator.selectorLabels" -}} +app.kubernetes.io/name: {{ include "vector-operator.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "vector-operator.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "vector-operator.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/helm/templates/crds.yaml b/helm/templates/crds.yaml new file mode 100644 index 00000000..2f16cfeb --- /dev/null +++ b/helm/templates/crds.yaml @@ -0,0 +1,1506 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.2 + creationTimestamp: null + name: clustervectorpipelines.observability.kaasops.io +spec: + group: observability.kaasops.io + names: + kind: ClusterVectorPipeline + listKind: ClusterVectorPipelineList + plural: clustervectorpipelines + shortNames: + - cvp + singular: clustervectorpipeline + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.configCheckResult + name: Valid + type: boolean + name: v1alpha1 + schema: + openAPIV3Schema: + description: ClusterVectorPipeline is the Schema for the clustervectorpipelines + API + 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: + type: object + spec: + description: VectorPipelineSpec defines the desired state of VectorPipeline + properties: + sinks: + type: object + x-kubernetes-preserve-unknown-fields: true + sources: + type: object + x-kubernetes-preserve-unknown-fields: true + transforms: + type: object + x-kubernetes-preserve-unknown-fields: true + type: object + status: + description: VectorPipelineStatus defines the observed state of VectorPipeline + properties: + LastAppliedPipelineHash: + format: int32 + type: integer + configCheckResult: + type: boolean + reason: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.2 + creationTimestamp: null + name: vectorpipelines.observability.kaasops.io +spec: + group: observability.kaasops.io + names: + kind: VectorPipeline + listKind: VectorPipelineList + plural: vectorpipelines + shortNames: + - vp + singular: vectorpipeline + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.configCheckResult + name: Valid + type: boolean + name: v1alpha1 + schema: + openAPIV3Schema: + description: VectorPipeline is the Schema for the vectorpipelines API + 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: + type: object + spec: + description: VectorPipelineSpec defines the desired state of VectorPipeline + properties: + sinks: + type: object + x-kubernetes-preserve-unknown-fields: true + sources: + type: object + x-kubernetes-preserve-unknown-fields: true + transforms: + type: object + x-kubernetes-preserve-unknown-fields: true + type: object + status: + description: VectorPipelineStatus defines the observed state of VectorPipeline + properties: + LastAppliedPipelineHash: + format: int32 + type: integer + configCheckResult: + type: boolean + reason: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.2 + creationTimestamp: null + name: vectors.observability.kaasops.io +spec: + group: observability.kaasops.io + names: + kind: Vector + listKind: VectorList + plural: vectors + singular: vector + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.configCheckResult + name: Valid + type: boolean + name: v1alpha1 + schema: + openAPIV3Schema: + description: Vector is the Schema for the vectors API + 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: + type: object + spec: + description: VectorSpec defines the desired state of Vector + properties: + agent: + description: DisableAggregation DisableAggregation bool `json:"disableAggregation,omitempty"` + Vector Agent + properties: + affinity: + description: Affinity If specified, the pod's scheduling constraints. + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for + the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods + to nodes that satisfy the affinity expressions specified + by this field, but it may choose a node that violates + one or more of the expressions. The node that is most + preferred is the one with the greatest sum of weights, + i.e. for each node that meets all of the scheduling + requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating + through the elements of this field and adding "weight" + to the sum if the node matches the corresponding matchExpressions; + the node(s) with the highest sum are the most preferred. + items: + description: An empty preferred scheduling term matches + all objects with implicit weight 0 (i.e. it's a no-op). + A null preferred scheduling term matches no objects + (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated with + the corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + x-kubernetes-map-type: atomic + weight: + description: Weight associated with matching the + corresponding nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by + this field are not met at scheduling time, the pod will + not be scheduled onto the node. If the affinity requirements + specified by this field cease to be met at some point + during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from + its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. + The terms are ORed. + items: + description: A null or empty node selector term + matches no objects. The requirements of them are + ANDed. The TopologySelectorTerm type implements + a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + x-kubernetes-map-type: atomic + type: array + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. + co-locate this pod in the same node, zone, etc. as some + other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods + to nodes that satisfy the affinity expressions specified + by this field, but it may choose a node that violates + one or more of the expressions. The node that is most + preferred is the one with the greatest sum of weights, + i.e. for each node that meets all of the scheduling + requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating + through the elements of this field and adding "weight" + to the sum if the node has pods which matches the corresponding + podAffinityTerm; the node(s) with the highest sum are + the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by + this field and the ones listed in the namespaces + field. null selector and null or empty namespaces + list means "this pod's namespace". An empty + selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. + The term is applied to the union of the namespaces + listed in this field and the ones selected + by namespaceSelector. null or empty namespaces + list and null namespaceSelector means "this + pod's namespace". + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the + pods matching the labelSelector in the specified + namespaces, where co-located is defined as + running on a node whose value of the label + with key topologyKey matches that of any node + on which any of the selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the + corresponding podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by + this field are not met at scheduling time, the pod will + not be scheduled onto the node. If the affinity requirements + specified by this field cease to be met at some point + during pod execution (e.g. due to a pod label update), + the system may or may not try to eventually evict the + pod from its node. When there are multiple elements, + the lists of nodes corresponding to each podAffinityTerm + are intersected, i.e. all terms must be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not + co-located (anti-affinity) with, where co-located + is defined as running on a node whose value of the + label with key matches that of any node + on which a pod of the set of pods is running + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by this + field and the ones listed in the namespaces field. + null selector and null or empty namespaces list + means "this pod's namespace". An empty selector + ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. The + term is applied to the union of the namespaces + listed in this field and the ones selected by + namespaceSelector. null or empty namespaces list + and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey + matches that of any node on which any of the selected + pods is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules + (e.g. avoid putting this pod in the same node, zone, etc. + as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods + to nodes that satisfy the anti-affinity expressions + specified by this field, but it may choose a node that + violates one or more of the expressions. The node that + is most preferred is the one with the greatest sum of + weights, i.e. for each node that meets all of the scheduling + requirements (resource request, requiredDuringScheduling + anti-affinity expressions, etc.), compute a sum by iterating + through the elements of this field and adding "weight" + to the sum if the node has pods which matches the corresponding + podAffinityTerm; the node(s) with the highest sum are + the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by + this field and the ones listed in the namespaces + field. null selector and null or empty namespaces + list means "this pod's namespace". An empty + selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. + The term is applied to the union of the namespaces + listed in this field and the ones selected + by namespaceSelector. null or empty namespaces + list and null namespaceSelector means "this + pod's namespace". + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the + pods matching the labelSelector in the specified + namespaces, where co-located is defined as + running on a node whose value of the label + with key topologyKey matches that of any node + on which any of the selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the + corresponding podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified + by this field are not met at scheduling time, the pod + will not be scheduled onto the node. If the anti-affinity + requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod + label update), the system may or may not try to eventually + evict the pod from its node. When there are multiple + elements, the lists of nodes corresponding to each podAffinityTerm + are intersected, i.e. all terms must be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not + co-located (anti-affinity) with, where co-located + is defined as running on a node whose value of the + label with key matches that of any node + on which a pod of the set of pods is running + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by this + field and the ones listed in the namespaces field. + null selector and null or empty namespaces list + means "this pod's namespace". An empty selector + ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. The + term is applied to the union of the namespaces + listed in this field and the ones selected by + namespaceSelector. null or empty namespaces list + and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey + matches that of any node on which any of the selected + pods is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + api: + description: ApiSpec is the Schema for the Vector Agent GraphQL + API - https://vector.dev/docs/reference/api/ + properties: + address: + type: string + enabled: + type: boolean + playground: + type: boolean + type: object + dataDir: + type: string + env: + description: Env that will be added to Vector pod + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. If + a variable cannot be resolved, the reference in the input + string will be unchanged. Double $$ are reduced to a single + $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless + of whether the variable exists or not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.podIP, + status.podIPs.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.ephemeral-storage) are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + host_aliases: + description: HostAliases provides mapping between ip and hostnames, + that would be propagated to pod, cannot be used with HostNetwork. + items: + description: HostAlias holds the mapping between IP and hostnames + that will be injected as an entry in the pod's hosts file. + properties: + hostnames: + description: Hostnames for the above IP address. + items: + type: string + type: array + ip: + description: IP address of the host file entry. + type: string + type: object + type: array + hostNetwork: + description: HostNetwork controls whether the pod may use the + node network namespace + type: boolean + image: + default: timberio/vector:0.24.0-distroless-libc + description: Image - docker image settings for Vector Agent if + no specified operator uses default config version + type: string + imagePullSecrets: + description: ImagePullSecrets An optional list of references to + secrets in the same namespace to use for pulling images from + registries see http://kubernetes.io/docs/user-guide/images#specifying-imagepullsecrets-on-a-pod + items: + description: LocalObjectReference contains enough information + to let you locate the referenced object inside the same namespace. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic + type: array + podSecurityPolicyName: + description: PodSecurityPolicyName - defines name for podSecurityPolicy + in case of empty value, prefixedName will be used. + type: string + priorityClassName: + description: PriorityClassName assigned to the Pods + type: string + resources: + description: Resources container resource request and limits, + https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + if not specified - default setting will be used + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, otherwise + to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + runtimeClassName: + description: RuntimeClassName - defines runtime class for kubernetes + pod. https://kubernetes.io/docs/concepts/containers/runtime-class/ + type: string + schedulerName: + description: SchedulerName - defines kubernetes scheduler name + type: string + securityContext: + description: SecurityContext holds pod-level security attributes + and common container settings. This defaults to the default + PodSecurityContext. Tolerations If specified, the pod's tolerations. + properties: + fsGroup: + description: "A special supplemental group that applies to + all containers in a pod. Some volume types allow the Kubelet + to change the ownership of that volume to be owned by the + pod: \n 1. The owning GID will be the FSGroup 2. The setgid + bit is set (new files created in the volume will be owned + by FSGroup) 3. The permission bits are OR'd with rw-rw---- + \n If unset, the Kubelet will not modify the ownership and + permissions of any volume. Note that this field cannot be + set when spec.os.name is windows." + format: int64 + type: integer + fsGroupChangePolicy: + description: 'fsGroupChangePolicy defines behavior of changing + ownership and permission of the volume before being exposed + inside Pod. This field will only apply to volume types which + support fsGroup based ownership(and permissions). It will + have no effect on ephemeral volume types such as: secret, + configmaps and emptydir. Valid values are "OnRootMismatch" + and "Always". If not specified, "Always" is used. Note that + this field cannot be set when spec.os.name is windows.' + type: string + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be set + in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence for that container. Note that this field + cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a non-root + user. If true, the Kubelet will validate the image at runtime + to ensure that it does not run as UID 0 (root) and fail + to start the container if it does. If unset or false, no + such validation will be performed. May also be set in SecurityContext. If + set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata if + unspecified. May also be set in SecurityContext. If set + in both SecurityContext and PodSecurityContext, the value + specified in SecurityContext takes precedence for that container. + Note that this field cannot be set when spec.os.name is + windows. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to all containers. + If unspecified, the container runtime will allocate a random + SELinux context for each container. May also be set in + SecurityContext. If set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence + for that container. Note that this field cannot be set when + spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by the containers + in this pod. Note that this field cannot be set when spec.os.name + is windows. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile must + be preconfigured on the node to work. Must be a descending + path, relative to the kubelet's configured seccomp profile + location. Must only be set if type is "Localhost". + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - a + profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile + should be used. Unconfined - no profile should be applied." + type: string + required: + - type + type: object + supplementalGroups: + description: A list of groups applied to the first process + run in each container, in addition to the container's primary + GID. If unspecified, no groups will be added to any container. + Note that this field cannot be set when spec.os.name is + windows. + items: + format: int64 + type: integer + type: array + sysctls: + description: Sysctls hold a list of namespaced sysctls used + for the pod. Pods with unsupported sysctls (by the container + runtime) might fail to launch. Note that this field cannot + be set when spec.os.name is windows. + items: + description: Sysctl defines a kernel parameter to be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + description: The Windows specific settings applied to all + containers. If unspecified, the options within a container's + SecurityContext will be used. If set in both SecurityContext + and PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named + by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. This field is + alpha-level and will only be honored by components that + enable the WindowsHostProcessContainers feature flag. + Setting this field without the feature flag will result + in errors when validating the Pod. All of a Pod's containers + must have the same effective HostProcess value (it is + not allowed to have a mix of HostProcess containers + and non-HostProcess containers). In addition, if HostProcess + is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set in + PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. + type: string + type: object + type: object + service: + type: boolean + tolerations: + description: Tolerations If specified, the pod's tolerations. + items: + description: The pod this Toleration is attached to tolerates + any taint that matches the triple using + the matching operator . + properties: + effect: + description: Effect indicates the taint effect to match. + Empty means match all taint effects. When specified, allowed + values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies + to. Empty means match all taint keys. If the key is empty, + operator must be Exists; this combination means to match + all values and all keys. + type: string + operator: + description: Operator represents a key's relationship to + the value. Valid operators are Exists and Equal. Defaults + to Equal. Exists is equivalent to wildcard for value, + so that a pod can tolerate all taints of a particular + category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of + time the toleration (which must be of effect NoExecute, + otherwise this field is ignored) tolerates the taint. + By default, it is not set, which means tolerate the taint + forever (do not evict). Zero and negative values will + be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches + to. If the operator is Exists, the value should be empty, + otherwise just a regular string. + type: string + type: object + type: array + type: object + type: object + status: + description: VectorStatus defines the observed state of Vector + properties: + LastAppliedConfigHash: + format: int32 + type: integer + configCheckResult: + type: boolean + reason: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/helm/templates/deployment.yaml b/helm/templates/deployment.yaml new file mode 100644 index 00000000..413f679f --- /dev/null +++ b/helm/templates/deployment.yaml @@ -0,0 +1,40 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: vector-operator + namespace: vector-operator-system + labels: + app.kubernetes.io/name: vector-operator +spec: + selector: + matchLabels: + app.kubernetes.io/name: vector-operator + template: + metadata: + labels: + app.kubernetes.io/name: vector-operator + app.kubernetes.io/managed-by: "helm" + meta.helm.sh/release-name: vector-operator + spec: + volumes: + - name: kube-api-access + secret: + secretName: vector-operator-sa-token + defaultMode: 420 + automountServiceAccountToken: false + serviceAccount: vector-operator + serviceAccountName: vector-operator + containers: + - image: {{ .Values.image.repository }}:{{ .Values.image.tag }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + name: vector-operator + volumeMounts: + - name: kube-api-access + readOnly: true + mountPath: /var/run/secrets/kubernetes.io/serviceaccount + resources: +{{ toYaml .Values.resources | indent 12 }} + securityContext: +{{- toYaml .Values.image.securityContext | nindent 10 }} + + \ No newline at end of file diff --git a/helm/templates/monitor.yaml b/helm/templates/monitor.yaml new file mode 100644 index 00000000..0b3952c3 --- /dev/null +++ b/helm/templates/monitor.yaml @@ -0,0 +1,18 @@ +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + labels: + control-plane: controller-manager + name: controller-manager-metrics-monitor + namespace: vector-operator-system +spec: + endpoints: + - path: /metrics + port: https + scheme: https + bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token + tlsConfig: + insecureSkipVerify: true + selector: + matchLabels: + control-plane: controller-manager \ No newline at end of file diff --git a/helm/templates/rbac.yaml b/helm/templates/rbac.yaml new file mode 100644 index 00000000..80e0dd23 --- /dev/null +++ b/helm/templates/rbac.yaml @@ -0,0 +1,125 @@ +{{- if .Values.rbac.create -}} + +--- +{{- if semverCompare ">= 1.24" .Capabilities.KubeVersion.Version }} +apiVersion: v1 +kind: Secret +metadata: + name: vector-operator-sa-token + namespace: vector-operator-system + annotations: + kubernetes.io/service-account.name: vector-operator +type: kubernetes.io/service-account-token +{{- end }} + +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: vector-operator + namespace: vector-operator-system +secrets: + - name: vector-operator-sa-token + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: vector-operator +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: vector-operator +subjects: +- kind: ServiceAccount + name: vector-operator + namespace: vector-operator-system + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: vector-operator +rules: +- apiGroups: + - "" + resources: + - secrets + - pods + - serviceaccounts + - services + - namespaces + - nodes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterroles + - clusterrolebindings + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resources: + - daemonsets + - replicasets + - statefulsets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + - extensions + resources: + - deployments + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - observability.kaasops.io + resources: + - clustervectorpipelines + - vectorpipelines + - vectors + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - observability.kaasops.io + resources: + - clustervectorpipelines/status + - vectorpipelines/status + - vectors/status + verbs: + - get + - patch + - update +{{- end }} \ No newline at end of file diff --git a/helm/templates/serviceaccount.yaml b/helm/templates/serviceaccount.yaml new file mode 100644 index 00000000..20822842 --- /dev/null +++ b/helm/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ toYaml .Values.serviceAccount.name | nindent 4 }} + namespace: vector-operator-system +{{- if .Values.serviceAccount.annotations }} + annotations: +{{ toYaml .Values.serviceAccount.annotations | nindent 4 }} +{{- end }} +secrets: + - name: vector-operator-sa-token +{{- end }} \ No newline at end of file diff --git a/helm/values.yaml b/helm/values.yaml new file mode 100644 index 00000000..27e12d31 --- /dev/null +++ b/helm/values.yaml @@ -0,0 +1,31 @@ +image: + repository: kaasops/vector-operator + tag: "latest" + pullPolicy: IfNotPresent + + securityContext: + allowPrivilegeEscalation: false + runAsGroup: 1000 + runAsNonRoot: true + readOnlyRootFilesystem: true + seccompProfile: + type: RuntimeDefault + capabilities: + drop: + - ALL + +resources: + limits: + cpu: "2" + memory: 1Gi + requests: + cpu: 100m + memory: 50Mi + +rbac: + create: true + +serviceAccount: + create: false + name: "vector-operator" + annotations: {} From fe2e4c81a1d3e47ede266bcd671f03192f86adfc Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Thu, 10 Nov 2022 09:19:31 +0200 Subject: [PATCH 082/316] Fix context forward Signed-off-by: Zemtsov Vladimir --- .../factory/config/configcheck/configcheck.go | 36 +++--- controllers/factory/utils/k8s/k8s.go | 108 +++++++++--------- controllers/factory/utils/k8s/k8s_test.go | 34 +++--- .../vectoragent/vectoragent_controller.go | 47 ++++---- controllers/vector_controller.go | 2 +- 5 files changed, 111 insertions(+), 116 deletions(-) diff --git a/controllers/factory/config/configcheck/configcheck.go b/controllers/factory/config/configcheck/configcheck.go index c2d7f666..c7511668 100644 --- a/controllers/factory/config/configcheck/configcheck.go +++ b/controllers/factory/config/configcheck/configcheck.go @@ -54,17 +54,17 @@ func New(config []byte, c client.Client, cs *kubernetes.Clientset, name, namespa } func (cc *ConfigCheck) Run(ctx context.Context) error { - log := log.FromContext(context.TODO()).WithValues("Vector ConfigCheck", cc.Name) + log := log.FromContext(ctx).WithValues("Vector ConfigCheck", cc.Name) log.Info("start ConfigCheck") - if err := cc.ensureVectorConfigCheckRBAC(); err != nil { + if err := cc.ensureVectorConfigCheckRBAC(ctx); err != nil { return err } cc.Hash = randStringRunes() - if err := cc.ensureVectorConfigCheckConfig(); err != nil { + if err := cc.ensureVectorConfigCheckConfig(ctx); err != nil { return err } @@ -75,33 +75,33 @@ func (cc *ConfigCheck) Run(ctx context.Context) error { return nil } -func (cc *ConfigCheck) ensureVectorConfigCheckRBAC() error { - return cc.ensureVectorConfigCheckServiceAccount() +func (cc *ConfigCheck) ensureVectorConfigCheckRBAC(ctx context.Context) error { + return cc.ensureVectorConfigCheckServiceAccount(ctx) } -func (cc *ConfigCheck) ensureVectorConfigCheckServiceAccount() error { +func (cc *ConfigCheck) ensureVectorConfigCheckServiceAccount(ctx context.Context) error { vectorAgentServiceAccount := cc.createVectorConfigCheckServiceAccount() - return k8s.CreateOrUpdateServiceAccount(vectorAgentServiceAccount, cc.Client) + return k8s.CreateOrUpdateServiceAccount(ctx, vectorAgentServiceAccount, cc.Client) } -func (cc *ConfigCheck) ensureVectorConfigCheckConfig() error { +func (cc *ConfigCheck) ensureVectorConfigCheckConfig(ctx context.Context) error { vectorConfigCheckSecret, err := cc.createVectorConfigCheckConfig() if err != nil { return err } - return k8s.CreateOrUpdateSecret(vectorConfigCheckSecret, cc.Client) + return k8s.CreateOrUpdateSecret(ctx, vectorConfigCheckSecret, cc.Client) } func (cc *ConfigCheck) checkVectorConfigCheckPod(ctx context.Context) error { vectorConfigCheckPod := cc.createVectorConfigCheckPod() - err := k8s.CreatePod(vectorConfigCheckPod, cc.Client) + err := k8s.CreatePod(ctx, vectorConfigCheckPod, cc.Client) if err != nil { return err } - err = cc.getCheckResult(vectorConfigCheckPod) + err = cc.getCheckResult(ctx, vectorConfigCheckPod) if err != nil { return err } @@ -138,11 +138,11 @@ func randStringRunes() string { return string(b) } -func (cc *ConfigCheck) getCheckResult(pod *corev1.Pod) error { - log := log.FromContext(context.TODO()).WithValues("Vector ConfigCheck", pod.Name) +func (cc *ConfigCheck) getCheckResult(ctx context.Context, pod *corev1.Pod) error { + log := log.FromContext(ctx).WithValues("Vector ConfigCheck", pod.Name) for { - existing, err := k8s.GetPod(pod, cc.Client) + existing, err := k8s.GetPod(ctx, pod, cc.Client) if err != nil { return err } @@ -152,7 +152,7 @@ func (cc *ConfigCheck) getCheckResult(pod *corev1.Pod) error { log.Info("wait Validate Vector Config Result") time.Sleep(5 * time.Second) case "Failed": - reason, err := k8s.GetPodLogs(pod, cc.ClientSet) + reason, err := k8s.GetPodLogs(ctx, pod, cc.ClientSet) if err != nil { return err } @@ -183,16 +183,16 @@ func (cc *ConfigCheck) cleanup(ctx context.Context) error { Name: v.Secret.SecretName, Namespace: pod.Namespace, } - secret, err := k8s.GetSecret(nn, cc.Client) + secret, err := k8s.GetSecret(ctx, nn, cc.Client) if err != nil { return err } - if err := k8s.DeleteSecret(secret, cc.Client); err != nil { + if err := k8s.DeleteSecret(ctx, secret, cc.Client); err != nil { return err } } } - if err := k8s.DeletePod(&pod, cc.Client); err != nil { + if err := k8s.DeletePod(ctx, &pod, cc.Client); err != nil { return err } } diff --git a/controllers/factory/utils/k8s/k8s.go b/controllers/factory/utils/k8s/k8s.go index a19fdbc4..aa435e8b 100644 --- a/controllers/factory/utils/k8s/k8s.go +++ b/controllers/factory/utils/k8s/k8s.go @@ -32,82 +32,82 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -func CreateOrUpdateService(svc *corev1.Service, c client.Client) error { - return reconcileService(svc, c) +func CreateOrUpdateService(ctx context.Context, svc *corev1.Service, c client.Client) error { + return reconcileService(ctx, svc, c) } -func CreateOrUpdateSecret(secret *corev1.Secret, c client.Client) error { - return reconcileSecret(secret, c) +func CreateOrUpdateSecret(ctx context.Context, secret *corev1.Secret, c client.Client) error { + return reconcileSecret(ctx, secret, c) } -func CreateOrUpdateDaemonSet(daemonSet *appsv1.DaemonSet, c client.Client) error { - return reconcileDaemonSet(daemonSet, c) +func CreateOrUpdateDaemonSet(ctx context.Context, daemonSet *appsv1.DaemonSet, c client.Client) error { + return reconcileDaemonSet(ctx, daemonSet, c) } -func CreateOrUpdateStatefulSet(statefulSet *appsv1.StatefulSet, c client.Client) error { - return reconcileStatefulSet(statefulSet, c) +func CreateOrUpdateStatefulSet(ctx context.Context, statefulSet *appsv1.StatefulSet, c client.Client) error { + return reconcileStatefulSet(ctx, statefulSet, c) } -func CreateOrUpdateServiceAccount(secret *corev1.ServiceAccount, c client.Client) error { - return reconcileServiceAccount(secret, c) +func CreateOrUpdateServiceAccount(ctx context.Context, secret *corev1.ServiceAccount, c client.Client) error { + return reconcileServiceAccount(ctx, secret, c) } -func CreateOrUpdateClusterRole(secret *rbacv1.ClusterRole, c client.Client) error { - return reconcileClusterRole(secret, c) +func CreateOrUpdateClusterRole(ctx context.Context, secret *rbacv1.ClusterRole, c client.Client) error { + return reconcileClusterRole(ctx, secret, c) } -func CreateOrUpdateClusterRoleBinding(secret *rbacv1.ClusterRoleBinding, c client.Client) error { - return reconcileClusterRoleBinding(secret, c) +func CreateOrUpdateClusterRoleBinding(ctx context.Context, secret *rbacv1.ClusterRoleBinding, c client.Client) error { + return reconcileClusterRoleBinding(ctx, secret, c) } -func CreatePod(pod *corev1.Pod, c client.Client) error { - err := c.Create(context.TODO(), pod) +func CreatePod(ctx context.Context, pod *corev1.Pod, c client.Client) error { + err := c.Create(ctx, pod) if err != nil { return err } return nil } -func GetPod(pod *corev1.Pod, c client.Client) (*corev1.Pod, error) { +func GetPod(ctx context.Context, pod *corev1.Pod, c client.Client) (*corev1.Pod, error) { result := &corev1.Pod{} - err := c.Get(context.TODO(), client.ObjectKeyFromObject(pod), result) + err := c.Get(ctx, client.ObjectKeyFromObject(pod), result) if err != nil { return nil, err } return result, nil } -func DeletePod(pod *corev1.Pod, c client.Client) error { - if err := c.Delete(context.TODO(), pod); err != nil { +func DeletePod(ctx context.Context, pod *corev1.Pod, c client.Client) error { + if err := c.Delete(ctx, pod); err != nil { return err } return nil } -func GetSecret(namespacedName types.NamespacedName, c client.Client) (*corev1.Secret, error) { +func GetSecret(ctx context.Context, namespacedName types.NamespacedName, c client.Client) (*corev1.Secret, error) { result := &corev1.Secret{} - err := c.Get(context.TODO(), namespacedName, result) + err := c.Get(ctx, namespacedName, result) if err != nil { return nil, err } return result, nil } -func DeleteSecret(secret *corev1.Secret, c client.Client) error { - if err := c.Delete(context.TODO(), secret); err != nil { +func DeleteSecret(ctx context.Context, secret *corev1.Secret, c client.Client) error { + if err := c.Delete(ctx, secret); err != nil { return err } return nil } -func GetPodLogs(pod *corev1.Pod, cs *kubernetes.Clientset) (string, error) { +func GetPodLogs(ctx context.Context, pod *corev1.Pod, cs *kubernetes.Clientset) (string, error) { count := int64(100) podLogOptions := corev1.PodLogOptions{ TailLines: &count, } req := cs.CoreV1().Pods(pod.Namespace).GetLogs(pod.Name, &podLogOptions) - podLogs, err := req.Stream(context.TODO()) + podLogs, err := req.Stream(ctx) if err != nil { return "", err } @@ -127,14 +127,14 @@ func UpdateStatus(ctx context.Context, obj client.Object, c client.Client) error return c.Status().Update(ctx, obj) } -func reconcileService(obj runtime.Object, c client.Client) error { +func reconcileService(ctx context.Context, obj runtime.Object, c client.Client) error { existing := &corev1.Service{} desired := obj.(*corev1.Service) - err := c.Create(context.TODO(), desired) + err := c.Create(ctx, desired) if errors.IsAlreadyExists(err) { - err := c.Get(context.TODO(), client.ObjectKeyFromObject(desired), existing) + err := c.Get(ctx, client.ObjectKeyFromObject(desired), existing) if err != nil { return err } @@ -144,21 +144,21 @@ func reconcileService(obj runtime.Object, c client.Client) error { existing.Labels = desired.Labels existing.Annotations = desired.Annotations existing.Spec = desired.Spec - return c.Update(context.TODO(), existing) + return c.Update(ctx, existing) } return nil } return err } -func reconcileSecret(obj runtime.Object, c client.Client) error { +func reconcileSecret(ctx context.Context, obj runtime.Object, c client.Client) error { existing := &corev1.Secret{} desired := obj.(*corev1.Secret) - err := c.Create(context.TODO(), desired) + err := c.Create(ctx, desired) if errors.IsAlreadyExists(err) { - err := c.Get(context.TODO(), client.ObjectKeyFromObject(desired), existing) + err := c.Get(ctx, client.ObjectKeyFromObject(desired), existing) if err != nil { return err } @@ -168,21 +168,21 @@ func reconcileSecret(obj runtime.Object, c client.Client) error { existing.Labels = desired.Labels existing.Annotations = desired.Annotations existing.Data = desired.Data - return c.Update(context.TODO(), existing) + return c.Update(ctx, existing) } return nil } return err } -func reconcileDaemonSet(obj runtime.Object, c client.Client) error { +func reconcileDaemonSet(ctx context.Context, obj runtime.Object, c client.Client) error { existing := &appsv1.DaemonSet{} desired := obj.(*appsv1.DaemonSet) - err := c.Create(context.TODO(), desired) + err := c.Create(ctx, desired) if errors.IsAlreadyExists(err) { - err := c.Get(context.TODO(), client.ObjectKeyFromObject(desired), existing) + err := c.Get(ctx, client.ObjectKeyFromObject(desired), existing) if err != nil { return err } @@ -192,21 +192,21 @@ func reconcileDaemonSet(obj runtime.Object, c client.Client) error { existing.Labels = desired.Labels existing.Annotations = desired.Annotations existing.Spec = desired.Spec - return c.Update(context.TODO(), existing) + return c.Update(ctx, existing) } return nil } return err } -func reconcileStatefulSet(obj runtime.Object, c client.Client) error { +func reconcileStatefulSet(ctx context.Context, obj runtime.Object, c client.Client) error { existing := &appsv1.StatefulSet{} desired := obj.(*appsv1.StatefulSet) - err := c.Create(context.TODO(), desired) + err := c.Create(ctx, desired) if errors.IsAlreadyExists(err) { - err := c.Get(context.TODO(), client.ObjectKeyFromObject(desired), existing) + err := c.Get(ctx, client.ObjectKeyFromObject(desired), existing) if err != nil { return err } @@ -216,21 +216,21 @@ func reconcileStatefulSet(obj runtime.Object, c client.Client) error { existing.Labels = desired.Labels existing.Annotations = desired.Annotations existing.Spec = desired.Spec - return c.Update(context.TODO(), existing) + return c.Update(ctx, existing) } return nil } return err } -func reconcileServiceAccount(obj runtime.Object, c client.Client) error { +func reconcileServiceAccount(ctx context.Context, obj runtime.Object, c client.Client) error { existing := &corev1.ServiceAccount{} desired := obj.(*corev1.ServiceAccount) - err := c.Create(context.TODO(), desired) + err := c.Create(ctx, desired) if errors.IsAlreadyExists(err) { - err := c.Get(context.TODO(), client.ObjectKeyFromObject(desired), existing) + err := c.Get(ctx, client.ObjectKeyFromObject(desired), existing) if err != nil { return err } @@ -238,21 +238,21 @@ func reconcileServiceAccount(obj runtime.Object, c client.Client) error { !equality.Semantic.DeepDerivative(desired.Annotations, existing.Annotations) { existing.Labels = desired.Labels existing.Annotations = desired.Annotations - return c.Update(context.TODO(), existing) + return c.Update(ctx, existing) } return nil } return err } -func reconcileClusterRole(obj runtime.Object, c client.Client) error { +func reconcileClusterRole(ctx context.Context, obj runtime.Object, c client.Client) error { existing := &rbacv1.ClusterRole{} desired := obj.(*rbacv1.ClusterRole) - err := c.Create(context.TODO(), desired) + err := c.Create(ctx, desired) if errors.IsAlreadyExists(err) { - err := c.Get(context.TODO(), client.ObjectKeyFromObject(desired), existing) + err := c.Get(ctx, client.ObjectKeyFromObject(desired), existing) if err != nil { return err } @@ -262,21 +262,21 @@ func reconcileClusterRole(obj runtime.Object, c client.Client) error { existing.Labels = desired.Labels existing.Annotations = desired.Annotations existing.Rules = desired.Rules - return c.Update(context.TODO(), existing) + return c.Update(ctx, existing) } return nil } return err } -func reconcileClusterRoleBinding(obj runtime.Object, c client.Client) error { +func reconcileClusterRoleBinding(ctx context.Context, obj runtime.Object, c client.Client) error { existing := &rbacv1.ClusterRoleBinding{} desired := obj.(*rbacv1.ClusterRoleBinding) - err := c.Create(context.TODO(), desired) + err := c.Create(ctx, desired) if errors.IsAlreadyExists(err) { - err := c.Get(context.TODO(), client.ObjectKeyFromObject(desired), existing) + err := c.Get(ctx, client.ObjectKeyFromObject(desired), existing) if err != nil { return err } @@ -288,7 +288,7 @@ func reconcileClusterRoleBinding(obj runtime.Object, c client.Client) error { existing.Annotations = desired.Annotations existing.RoleRef = desired.RoleRef existing.Subjects = desired.Subjects - return c.Update(context.TODO(), existing) + return c.Update(ctx, existing) } return nil } diff --git a/controllers/factory/utils/k8s/k8s_test.go b/controllers/factory/utils/k8s/k8s_test.go index 58ffa2e1..07f548b1 100644 --- a/controllers/factory/utils/k8s/k8s_test.go +++ b/controllers/factory/utils/k8s/k8s_test.go @@ -45,7 +45,7 @@ func getInitObjectMeta() metav1.ObjectMeta { return ObjectMeta } -var reconcileObjectCase = func(objInit, obj interface{}, want error) func(t *testing.T) { +var reconcileObjectCase = func(ctx context.Context, objInit, obj interface{}, want error) func(t *testing.T) { return func(t *testing.T) { t.Helper() t.Parallel() @@ -58,49 +58,49 @@ var reconcileObjectCase = func(objInit, obj interface{}, want error) func(t *tes service := obj.(*corev1.Service) cl := fake.NewClientBuilder().WithObjects(serviceInit).Build() - err := k8s.CreateOrUpdateService(service, cl) + err := k8s.CreateOrUpdateService(ctx, service, cl) req.Equal(err, want) case *corev1.Secret: secretInit := objInit.(*corev1.Secret) secret := obj.(*corev1.Secret) cl := fake.NewClientBuilder().WithObjects(secretInit).Build() - err := k8s.CreateOrUpdateSecret(secret, cl) + err := k8s.CreateOrUpdateSecret(ctx, secret, cl) req.Equal(err, want) case *appsv1.DaemonSet: daemonSetInit := objInit.(*appsv1.DaemonSet) daemonSet := obj.(*appsv1.DaemonSet) cl := fake.NewClientBuilder().WithObjects(daemonSetInit).Build() - err := k8s.CreateOrUpdateDaemonSet(daemonSet, cl) + err := k8s.CreateOrUpdateDaemonSet(ctx, daemonSet, cl) req.Equal(err, want) case *appsv1.StatefulSet: statefulSetInit := objInit.(*appsv1.StatefulSet) statefulSet := obj.(*appsv1.StatefulSet) cl := fake.NewClientBuilder().WithObjects(statefulSetInit).Build() - err := k8s.CreateOrUpdateStatefulSet(statefulSet, cl) + err := k8s.CreateOrUpdateStatefulSet(ctx, statefulSet, cl) req.Equal(err, want) case *corev1.ServiceAccount: serviceAccountInit := objInit.(*corev1.ServiceAccount) serviceAccount := obj.(*corev1.ServiceAccount) cl := fake.NewClientBuilder().WithObjects(serviceAccountInit).Build() - err := k8s.CreateOrUpdateServiceAccount(serviceAccount, cl) + err := k8s.CreateOrUpdateServiceAccount(ctx, serviceAccount, cl) req.Equal(err, want) case *rbacv1.ClusterRole: clusterRoleInit := objInit.(*rbacv1.ClusterRole) clusterRole := obj.(*rbacv1.ClusterRole) cl := fake.NewClientBuilder().WithObjects(clusterRoleInit).Build() - err := k8s.CreateOrUpdateClusterRole(clusterRole, cl) + err := k8s.CreateOrUpdateClusterRole(ctx, clusterRole, cl) req.Equal(err, want) case *rbacv1.ClusterRoleBinding: clusterRoleBindingInit := objInit.(*rbacv1.ClusterRoleBinding) clusterRoleBinding := obj.(*rbacv1.ClusterRoleBinding) cl := fake.NewClientBuilder().WithObjects(clusterRoleBindingInit).Build() - err := k8s.CreateOrUpdateClusterRoleBinding(clusterRoleBinding, cl) + err := k8s.CreateOrUpdateClusterRoleBinding(ctx, clusterRoleBinding, cl) req.Equal(err, want) } } @@ -167,7 +167,7 @@ func TestCreateOrUpdateService(t *testing.T) { // t.Parallel() for _, tc := range secriveCases { - t.Run(tc.name, reconcileObjectCase(initObj, tc.obj, tc.err)) + t.Run(tc.name, reconcileObjectCase(context.Background(), initObj, tc.obj, tc.err)) } } @@ -221,7 +221,7 @@ func TestCreateOrUpdateSecret(t *testing.T) { // t.Parallel() for _, tc := range secretCases { - t.Run(tc.name, reconcileObjectCase(initObj, tc.obj, tc.err)) + t.Run(tc.name, reconcileObjectCase(context.Background(), initObj, tc.obj, tc.err)) } } @@ -275,7 +275,7 @@ func TestCreateOrUpdateDaemonSet(t *testing.T) { // t.Parallel() for _, tc := range daemonSetCases { - t.Run(tc.name, reconcileObjectCase(initObj, tc.obj, tc.err)) + t.Run(tc.name, reconcileObjectCase(context.Background(), initObj, tc.obj, tc.err)) } } @@ -329,7 +329,7 @@ func TestCreateOrUpdateStatefulSet(t *testing.T) { // t.Parallel() for _, tc := range statefulSetCases { - t.Run(tc.name, reconcileObjectCase(initObj, tc.obj, tc.err)) + t.Run(tc.name, reconcileObjectCase(context.Background(), initObj, tc.obj, tc.err)) } } @@ -383,7 +383,7 @@ func TestCreateOrUpdateServiceAccount(t *testing.T) { // t.Parallel() for _, tc := range serviceAccountCases { - t.Run(tc.name, reconcileObjectCase(initObj, tc.obj, tc.err)) + t.Run(tc.name, reconcileObjectCase(context.Background(), initObj, tc.obj, tc.err)) } } @@ -437,7 +437,7 @@ func TestCreateOrUpdateClusterRole(t *testing.T) { // t.Parallel() for _, tc := range clusterRoleCases { - t.Run(tc.name, reconcileObjectCase(initObj, tc.obj, tc.err)) + t.Run(tc.name, reconcileObjectCase(context.Background(), initObj, tc.obj, tc.err)) } } @@ -491,7 +491,7 @@ func TestCreateOrUpdateClusterRoleBinding(t *testing.T) { // t.Parallel() for _, tc := range clusterRoleBindingCases { - t.Run(tc.name, reconcileObjectCase(initObj, tc.obj, tc.err)) + t.Run(tc.name, reconcileObjectCase(context.Background(), initObj, tc.obj, tc.err)) } } @@ -505,7 +505,7 @@ func TestCreatePod(t *testing.T) { cl := fake.NewClientBuilder().WithObjects(objInit).Build() - err := k8s.CreatePod(obj, cl) + err := k8s.CreatePod(context.Background(), obj, cl) req.Equal(err, want) } } @@ -562,7 +562,7 @@ func TestGetPod(t *testing.T) { cl := fake.NewClientBuilder().WithObjects(objInit).Build() - result, err := k8s.GetPod(obj, cl) + result, err := k8s.GetPod(context.Background(), obj, cl) if result != nil { req.Equal(result.ObjectMeta, wantPod.ObjectMeta) } diff --git a/controllers/factory/vector/vectoragent/vectoragent_controller.go b/controllers/factory/vector/vectoragent/vectoragent_controller.go index 84e75d3b..914600b1 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_controller.go +++ b/controllers/factory/vector/vectoragent/vectoragent_controller.go @@ -25,83 +25,79 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" ) -func (ctrl *Controller) EnsureVectorAgent() error { - ctx := context.Background() +func (ctrl *Controller) EnsureVectorAgent(ctx context.Context) error { log := log.FromContext(ctx).WithValues("vector-agent", ctrl.Vector.Name) log.Info("start Reconcile Vector Agent") - if err := ctrl.ensureVectorAgentRBAC(); err != nil { + if err := ctrl.ensureVectorAgentRBAC(ctx); err != nil { return err } if ctrl.Vector.Spec.Agent.Service { - if err := ctrl.ensureVectorAgentService(); err != nil { + if err := ctrl.ensureVectorAgentService(ctx); err != nil { return err } } - if err := ctrl.ensureVectorAgentConfig(); err != nil { + if err := ctrl.ensureVectorAgentConfig(ctx); err != nil { return err } - if err := ctrl.ensureVectorAgentDaemonSet(); err != nil { + if err := ctrl.ensureVectorAgentDaemonSet(ctx); err != nil { return err } return nil } -func (ctrl *Controller) ensureVectorAgentRBAC() error { - ctx := context.Background() +func (ctrl *Controller) ensureVectorAgentRBAC(ctx context.Context) error { log := log.FromContext(ctx).WithValues("vector-agent-rbac", ctrl.Vector.Name) log.Info("start Reconcile Vector Agent RBAC") - if err := ctrl.ensureVectorAgentServiceAccount(); err != nil { + if err := ctrl.ensureVectorAgentServiceAccount(ctx); err != nil { return err } - if err := ctrl.ensureVectorAgentClusterRole(); err != nil { + if err := ctrl.ensureVectorAgentClusterRole(ctx); err != nil { return err } - if err := ctrl.ensureVectorAgentClusterRoleBinding(); err != nil { + if err := ctrl.ensureVectorAgentClusterRoleBinding(ctx); err != nil { return err } return nil } -func (ctrl *Controller) ensureVectorAgentServiceAccount() error { +func (ctrl *Controller) ensureVectorAgentServiceAccount(ctx context.Context) error { vectorAgentServiceAccount := ctrl.createVectorAgentServiceAccount() - return k8s.CreateOrUpdateServiceAccount(vectorAgentServiceAccount, ctrl.Client) + return k8s.CreateOrUpdateServiceAccount(ctx, vectorAgentServiceAccount, ctrl.Client) } -func (ctrl *Controller) ensureVectorAgentClusterRole() error { +func (ctrl *Controller) ensureVectorAgentClusterRole(ctx context.Context) error { vectorAgentClusterRole := ctrl.createVectorAgentClusterRole() - return k8s.CreateOrUpdateClusterRole(vectorAgentClusterRole, ctrl.Client) + return k8s.CreateOrUpdateClusterRole(ctx, vectorAgentClusterRole, ctrl.Client) } -func (ctrl *Controller) ensureVectorAgentClusterRoleBinding() error { +func (ctrl *Controller) ensureVectorAgentClusterRoleBinding(ctx context.Context) error { vectorAgentClusterRoleBinding := ctrl.createVectorAgentClusterRoleBinding() - return k8s.CreateOrUpdateClusterRoleBinding(vectorAgentClusterRoleBinding, ctrl.Client) + return k8s.CreateOrUpdateClusterRoleBinding(ctx, vectorAgentClusterRoleBinding, ctrl.Client) } -func (ctrl *Controller) ensureVectorAgentService() error { - ctx := context.Background() +func (ctrl *Controller) ensureVectorAgentService(ctx context.Context) error { log := log.FromContext(ctx).WithValues("vector-agent-service", ctrl.Vector.Name) log.Info("start Reconcile Vector Agent Service") vectorAgentService := ctrl.createVectorAgentService() - return k8s.CreateOrUpdateService(vectorAgentService, ctrl.Client) + return k8s.CreateOrUpdateService(ctx, vectorAgentService, ctrl.Client) } -func (ctrl *Controller) ensureVectorAgentConfig() error { - ctx := context.Background() +func (ctrl *Controller) ensureVectorAgentConfig(ctx context.Context) error { log := log.FromContext(ctx).WithValues("vector-agent-secret", ctrl.Vector.Name) log.Info("start Reconcile Vector Agent Secret") @@ -111,18 +107,17 @@ func (ctrl *Controller) ensureVectorAgentConfig() error { return err } - return k8s.CreateOrUpdateSecret(vectorAgentSecret, ctrl.Client) + return k8s.CreateOrUpdateSecret(ctx, vectorAgentSecret, ctrl.Client) } -func (ctrl *Controller) ensureVectorAgentDaemonSet() error { - ctx := context.Background() +func (ctrl *Controller) ensureVectorAgentDaemonSet(ctx context.Context) error { log := log.FromContext(ctx).WithValues("vector-agent-daemon-set", ctrl.Vector.Name) log.Info("start Reconcile Vector Agent DaemonSet") vectorAgentDaemonSet := ctrl.createVectorAgentDaemonSet() - return k8s.CreateOrUpdateDaemonSet(vectorAgentDaemonSet, ctrl.Client) + return k8s.CreateOrUpdateDaemonSet(ctx, vectorAgentDaemonSet, ctrl.Client) } func (ctrl *Controller) labelsForVectorAgent() map[string]string { diff --git a/controllers/vector_controller.go b/controllers/vector_controller.go index 16508455..3bda6319 100644 --- a/controllers/vector_controller.go +++ b/controllers/vector_controller.go @@ -122,7 +122,7 @@ func (r *VectorReconciler) CreateOrUpdateVector(ctx context.Context, v *vectorv1 vaCtrl.Config = byteConfig // Start Reconcile Vector Agent - if err := vaCtrl.EnsureVectorAgent(); err != nil { + if err := vaCtrl.EnsureVectorAgent(ctx); err != nil { return ctrl.Result{}, err } return ctrl.Result{}, nil From 2534939e0b67cb4b9e1a40d1cc2426eed66b186a Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Thu, 10 Nov 2022 09:23:41 +0200 Subject: [PATCH 083/316] Update ChangeLog Signed-off-by: Zemtsov Vladimir --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b943256..7c0205cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,8 @@ ### Added +- [[40](https://github.com/kaasops/vector-operator/pull/40)] **Fix**: Sloved context forward errors + + +### v0.0.1 - Refactor: Refactor Pipeline for add ClusterVectorPipeline and checks - Feature: Add field reason to CR Vector and VectorPipeline - Feature: Add ConfigCheck for Vector From 6cdb77d099dd2d13adcfed33d73ee9efff38b5f2 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Thu, 10 Nov 2022 09:45:00 +0200 Subject: [PATCH 084/316] revert reconcile by time (#42) --- controllers/vector_controller.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/controllers/vector_controller.go b/controllers/vector_controller.go index 3bda6319..6dbedc18 100644 --- a/controllers/vector_controller.go +++ b/controllers/vector_controller.go @@ -18,12 +18,11 @@ package controllers import ( "context" + "time" "github.com/kaasops/vector-operator/controllers/factory/config" "github.com/kaasops/vector-operator/controllers/factory/pipeline" "github.com/kaasops/vector-operator/controllers/factory/vector/vectoragent" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" @@ -94,10 +93,6 @@ func (r *VectorReconciler) findVectorCustomResourceInstance(ctx context.Context, func (r *VectorReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&vectorv1alpha1.Vector{}). - Owns(&appsv1.DaemonSet{}). - Owns(&corev1.Service{}). - Owns(&corev1.Secret{}). - Owns(&corev1.ServiceAccount{}). Complete(r) } @@ -125,5 +120,5 @@ func (r *VectorReconciler) CreateOrUpdateVector(ctx context.Context, v *vectorv1 if err := vaCtrl.EnsureVectorAgent(ctx); err != nil { return ctrl.Result{}, err } - return ctrl.Result{}, nil + return ctrl.Result{RequeueAfter: 15 * time.Second}, nil } From 370a2d2c680a36d34fb303d7071c3a0f33a3f6ab Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Fri, 11 Nov 2022 11:28:50 +0200 Subject: [PATCH 085/316] Refactor k8s utils Signed-off-by: Zemtsov Vladimir --- .../factory/config/configcheck/configcheck.go | 6 +- controllers/factory/utils/k8s/k8s.go | 423 ++++++--- controllers/factory/utils/k8s/k8s_test.go | 866 ++++++++++++------ .../vectoragent/vectoragent_controller.go | 12 +- 4 files changed, 900 insertions(+), 407 deletions(-) diff --git a/controllers/factory/config/configcheck/configcheck.go b/controllers/factory/config/configcheck/configcheck.go index c7511668..aef5bf8e 100644 --- a/controllers/factory/config/configcheck/configcheck.go +++ b/controllers/factory/config/configcheck/configcheck.go @@ -82,7 +82,7 @@ func (cc *ConfigCheck) ensureVectorConfigCheckRBAC(ctx context.Context) error { func (cc *ConfigCheck) ensureVectorConfigCheckServiceAccount(ctx context.Context) error { vectorAgentServiceAccount := cc.createVectorConfigCheckServiceAccount() - return k8s.CreateOrUpdateServiceAccount(ctx, vectorAgentServiceAccount, cc.Client) + return k8s.CreateOrUpdateResource(ctx, vectorAgentServiceAccount, cc.Client) } func (cc *ConfigCheck) ensureVectorConfigCheckConfig(ctx context.Context) error { vectorConfigCheckSecret, err := cc.createVectorConfigCheckConfig() @@ -90,7 +90,7 @@ func (cc *ConfigCheck) ensureVectorConfigCheckConfig(ctx context.Context) error return err } - return k8s.CreateOrUpdateSecret(ctx, vectorConfigCheckSecret, cc.Client) + return k8s.CreateOrUpdateResource(ctx, vectorConfigCheckSecret, cc.Client) } func (cc *ConfigCheck) checkVectorConfigCheckPod(ctx context.Context) error { @@ -142,7 +142,7 @@ func (cc *ConfigCheck) getCheckResult(ctx context.Context, pod *corev1.Pod) erro log := log.FromContext(ctx).WithValues("Vector ConfigCheck", pod.Name) for { - existing, err := k8s.GetPod(ctx, pod, cc.Client) + existing, err := k8s.GetPod(ctx, client.ObjectKeyFromObject(pod), cc.Client) if err != nil { return err } diff --git a/controllers/factory/utils/k8s/k8s.go b/controllers/factory/utils/k8s/k8s.go index aa435e8b..be7242af 100644 --- a/controllers/factory/utils/k8s/k8s.go +++ b/controllers/factory/utils/k8s/k8s.go @@ -19,128 +19,120 @@ package k8s import ( "bytes" "context" + "errors" + "fmt" "io" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/api/equality" - "k8s.io/apimachinery/pkg/api/errors" + api_errors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes" "sigs.k8s.io/controller-runtime/pkg/client" ) -func CreateOrUpdateService(ctx context.Context, svc *corev1.Service, c client.Client) error { - return reconcileService(ctx, svc, c) -} - -func CreateOrUpdateSecret(ctx context.Context, secret *corev1.Secret, c client.Client) error { - return reconcileSecret(ctx, secret, c) -} - -func CreateOrUpdateDaemonSet(ctx context.Context, daemonSet *appsv1.DaemonSet, c client.Client) error { - return reconcileDaemonSet(ctx, daemonSet, c) -} - -func CreateOrUpdateStatefulSet(ctx context.Context, statefulSet *appsv1.StatefulSet, c client.Client) error { - return reconcileStatefulSet(ctx, statefulSet, c) -} - -func CreateOrUpdateServiceAccount(ctx context.Context, secret *corev1.ServiceAccount, c client.Client) error { - return reconcileServiceAccount(ctx, secret, c) -} - -func CreateOrUpdateClusterRole(ctx context.Context, secret *rbacv1.ClusterRole, c client.Client) error { - return reconcileClusterRole(ctx, secret, c) -} - -func CreateOrUpdateClusterRoleBinding(ctx context.Context, secret *rbacv1.ClusterRoleBinding, c client.Client) error { - return reconcileClusterRoleBinding(ctx, secret, c) -} - -func CreatePod(ctx context.Context, pod *corev1.Pod, c client.Client) error { - err := c.Create(ctx, pod) - if err != nil { - return err - } - return nil -} - -func GetPod(ctx context.Context, pod *corev1.Pod, c client.Client) (*corev1.Pod, error) { - result := &corev1.Pod{} - err := c.Get(ctx, client.ObjectKeyFromObject(pod), result) - if err != nil { - return nil, err - } - return result, nil -} +var ( + ErrNotSupported = errors.New("Not Supported type for create or update kubernetes resource") +) -func DeletePod(ctx context.Context, pod *corev1.Pod, c client.Client) error { - if err := c.Delete(ctx, pod); err != nil { - return err - } - return nil +func NewNotSupportedError(obj client.Object) error { + return fmt.Errorf("%w.\n %+v", ErrNotSupported, obj) } -func GetSecret(ctx context.Context, namespacedName types.NamespacedName, c client.Client) (*corev1.Secret, error) { - result := &corev1.Secret{} - err := c.Get(ctx, namespacedName, result) - if err != nil { - return nil, err +func CreateOrUpdateResource(ctx context.Context, obj client.Object, c client.Client) error { + switch obj.(type) { + case *appsv1.Deployment: + runtimeObj := obj.DeepCopyObject() + return createOrUpdateDeployment(ctx, runtimeObj, c) + case *appsv1.StatefulSet: + return createOrUpdateStatefulSet(ctx, obj, c) + case *appsv1.DaemonSet: + return createOrUpdateDaemonSet(ctx, obj, c) + case *corev1.Secret: + return createOrUpdateSecret(ctx, obj, c) + case *corev1.Service: + return createOrUpdateService(ctx, obj, c) + case *corev1.ServiceAccount: + runtimeObj := obj.DeepCopyObject() + return createOrUpdateServiceAccount(ctx, runtimeObj, c) + case *rbacv1.ClusterRole: + return createOrUpdateClusterRole(ctx, obj, c) + case *rbacv1.ClusterRoleBinding: + return createOrUpdateClusterRoleBinding(ctx, obj, c) + default: + return NewNotSupportedError(obj) } - return result, nil } -func DeleteSecret(ctx context.Context, secret *corev1.Secret, c client.Client) error { - if err := c.Delete(ctx, secret); err != nil { - return err - } - return nil -} +func createOrUpdateDeployment(ctx context.Context, obj runtime.Object, c client.Client) error { + desired := obj.(*appsv1.Deployment) -func GetPodLogs(ctx context.Context, pod *corev1.Pod, cs *kubernetes.Clientset) (string, error) { - count := int64(100) - podLogOptions := corev1.PodLogOptions{ - TailLines: &count, - } + // Create Deployment + err := c.Create(ctx, desired) + if api_errors.IsAlreadyExists(err) { + // If alredy exist - compare with existed + existing := &appsv1.Deployment{} + err := c.Get(ctx, client.ObjectKeyFromObject(desired), existing) + if err != nil { + return err + } - req := cs.CoreV1().Pods(pod.Namespace).GetLogs(pod.Name, &podLogOptions) - podLogs, err := req.Stream(ctx) - if err != nil { - return "", err - } - defer podLogs.Close() + // init Interface for compare + desiredFields := []interface{}{ + desired.GetAnnotations(), + desired.GetLabels(), + desired.Spec, + } + existingFields := []interface{}{ + existing.GetAnnotations(), + existing.GetLabels(), + existing.Spec, + } - buf := new(bytes.Buffer) - _, err = io.Copy(buf, podLogs) - if err != nil { - return "", err + // Compare + if !equality.Semantic.DeepDerivative(desiredFields, existingFields) { + // Update if not equal + existing.Labels = desired.Labels + existing.Annotations = desired.Annotations + existing.Spec = desired.Spec + return c.Update(ctx, existing) + } + return nil } - str := buf.String() - - return str, nil -} - -func UpdateStatus(ctx context.Context, obj client.Object, c client.Client) error { - return c.Status().Update(ctx, obj) + return err } -func reconcileService(ctx context.Context, obj runtime.Object, c client.Client) error { - - existing := &corev1.Service{} - desired := obj.(*corev1.Service) +func createOrUpdateStatefulSet(ctx context.Context, obj runtime.Object, c client.Client) error { + desired := obj.(*appsv1.StatefulSet) + // Create StatefulSet err := c.Create(ctx, desired) - if errors.IsAlreadyExists(err) { + if api_errors.IsAlreadyExists(err) { + // If alredy exist - compare with existed + existing := &appsv1.StatefulSet{} err := c.Get(ctx, client.ObjectKeyFromObject(desired), existing) if err != nil { return err } - if !equality.Semantic.DeepDerivative(desired.ObjectMeta.Labels, existing.ObjectMeta.Labels) || - !equality.Semantic.DeepDerivative(desired.Annotations, existing.Annotations) || - !equality.Semantic.DeepDerivative(desired.Spec, existing.Spec) { + + // init Interface for compare + desiredFields := []interface{}{ + desired.GetAnnotations(), + desired.GetLabels(), + desired.Spec, + } + existingFields := []interface{}{ + existing.GetAnnotations(), + existing.GetLabels(), + existing.Spec, + } + + // Compare + if !equality.Semantic.DeepDerivative(desiredFields, existingFields) { + // Update if not equal existing.Labels = desired.Labels existing.Annotations = desired.Annotations existing.Spec = desired.Spec @@ -151,23 +143,37 @@ func reconcileService(ctx context.Context, obj runtime.Object, c client.Client) return err } -func reconcileSecret(ctx context.Context, obj runtime.Object, c client.Client) error { - - existing := &corev1.Secret{} - desired := obj.(*corev1.Secret) +func createOrUpdateDaemonSet(ctx context.Context, obj runtime.Object, c client.Client) error { + desired := obj.(*appsv1.DaemonSet) + // Create DaemonSet err := c.Create(ctx, desired) - if errors.IsAlreadyExists(err) { + if api_errors.IsAlreadyExists(err) { + // If alredy exist - compare with existed + existing := &appsv1.DaemonSet{} err := c.Get(ctx, client.ObjectKeyFromObject(desired), existing) if err != nil { return err } - if !equality.Semantic.DeepDerivative(desired.ObjectMeta.Labels, existing.ObjectMeta.Labels) || - !equality.Semantic.DeepDerivative(desired.Annotations, existing.Annotations) || - !equality.Semantic.DeepDerivative(desired.Data, existing.Data) { + + // init Interface for compare + desiredFields := []interface{}{ + desired.GetAnnotations(), + desired.GetLabels(), + desired.Spec, + } + existingFields := []interface{}{ + existing.GetAnnotations(), + existing.GetLabels(), + existing.Spec, + } + + // Compare + if !equality.Semantic.DeepDerivative(desiredFields, existingFields) { + // Update if not equal existing.Labels = desired.Labels existing.Annotations = desired.Annotations - existing.Data = desired.Data + existing.Spec = desired.Spec return c.Update(ctx, existing) } return nil @@ -175,23 +181,37 @@ func reconcileSecret(ctx context.Context, obj runtime.Object, c client.Client) e return err } -func reconcileDaemonSet(ctx context.Context, obj runtime.Object, c client.Client) error { - - existing := &appsv1.DaemonSet{} - desired := obj.(*appsv1.DaemonSet) +func createOrUpdateSecret(ctx context.Context, obj runtime.Object, c client.Client) error { + desired := obj.(*corev1.Secret) + // Create Secret err := c.Create(ctx, desired) - if errors.IsAlreadyExists(err) { + if api_errors.IsAlreadyExists(err) { + // If alredy exist - compare with existed + existing := &corev1.Secret{} err := c.Get(ctx, client.ObjectKeyFromObject(desired), existing) if err != nil { return err } - if !equality.Semantic.DeepDerivative(desired.ObjectMeta.Labels, existing.ObjectMeta.Labels) || - !equality.Semantic.DeepDerivative(desired.Annotations, existing.Annotations) || - !equality.Semantic.DeepDerivative(desired.Spec, existing.Spec) { + + // init Interface for compare + desiredFields := []interface{}{ + desired.GetAnnotations(), + desired.GetLabels(), + desired.Data, + } + existingFields := []interface{}{ + existing.GetAnnotations(), + existing.GetLabels(), + existing.Data, + } + + // Compare + if !equality.Semantic.DeepDerivative(desiredFields, existingFields) { + // Update if not equal existing.Labels = desired.Labels existing.Annotations = desired.Annotations - existing.Spec = desired.Spec + existing.Data = desired.Data return c.Update(ctx, existing) } return nil @@ -199,20 +219,34 @@ func reconcileDaemonSet(ctx context.Context, obj runtime.Object, c client.Client return err } -func reconcileStatefulSet(ctx context.Context, obj runtime.Object, c client.Client) error { - - existing := &appsv1.StatefulSet{} - desired := obj.(*appsv1.StatefulSet) +func createOrUpdateService(ctx context.Context, obj runtime.Object, c client.Client) error { + desired := obj.(*corev1.Service) + // Create Service err := c.Create(ctx, desired) - if errors.IsAlreadyExists(err) { + if api_errors.IsAlreadyExists(err) { + // If alredy exist - compare with existed + existing := &corev1.Service{} err := c.Get(ctx, client.ObjectKeyFromObject(desired), existing) if err != nil { return err } - if !equality.Semantic.DeepDerivative(desired.ObjectMeta.Labels, existing.ObjectMeta.Labels) || - !equality.Semantic.DeepDerivative(desired.Annotations, existing.Annotations) || - !equality.Semantic.DeepDerivative(desired.Spec, existing.Spec) { + + // init Interface for compare + desiredFields := []interface{}{ + desired.GetAnnotations(), + desired.GetLabels(), + desired.Spec, + } + existingFields := []interface{}{ + existing.GetAnnotations(), + existing.GetLabels(), + existing.Spec, + } + + // Compare + if !equality.Semantic.DeepDerivative(desiredFields, existingFields) { + // Update if not equal existing.Labels = desired.Labels existing.Annotations = desired.Annotations existing.Spec = desired.Spec @@ -223,19 +257,32 @@ func reconcileStatefulSet(ctx context.Context, obj runtime.Object, c client.Clie return err } -func reconcileServiceAccount(ctx context.Context, obj runtime.Object, c client.Client) error { - - existing := &corev1.ServiceAccount{} +func createOrUpdateServiceAccount(ctx context.Context, obj runtime.Object, c client.Client) error { desired := obj.(*corev1.ServiceAccount) + // Create ServiceAccount err := c.Create(ctx, desired) - if errors.IsAlreadyExists(err) { + if api_errors.IsAlreadyExists(err) { + // If alredy exist - compare with existed + existing := &corev1.ServiceAccount{} err := c.Get(ctx, client.ObjectKeyFromObject(desired), existing) if err != nil { return err } - if !equality.Semantic.DeepDerivative(desired.ObjectMeta.Labels, existing.ObjectMeta.Labels) || - !equality.Semantic.DeepDerivative(desired.Annotations, existing.Annotations) { + + // init Interface for compare + desiredFields := []interface{}{ + desired.GetAnnotations(), + desired.GetLabels(), + } + existingFields := []interface{}{ + existing.GetAnnotations(), + existing.GetLabels(), + } + + // Compare + if !equality.Semantic.DeepDerivative(desiredFields, existingFields) { + // Update if not equal existing.Labels = desired.Labels existing.Annotations = desired.Annotations return c.Update(ctx, existing) @@ -245,20 +292,34 @@ func reconcileServiceAccount(ctx context.Context, obj runtime.Object, c client.C return err } -func reconcileClusterRole(ctx context.Context, obj runtime.Object, c client.Client) error { - - existing := &rbacv1.ClusterRole{} +func createOrUpdateClusterRole(ctx context.Context, obj runtime.Object, c client.Client) error { desired := obj.(*rbacv1.ClusterRole) + // Create ClusterRole err := c.Create(ctx, desired) - if errors.IsAlreadyExists(err) { + if api_errors.IsAlreadyExists(err) { + // If alredy exist - compare with existed + existing := &rbacv1.ClusterRole{} err := c.Get(ctx, client.ObjectKeyFromObject(desired), existing) if err != nil { return err } - if !equality.Semantic.DeepDerivative(desired.ObjectMeta.Labels, existing.ObjectMeta.Labels) || - !equality.Semantic.DeepDerivative(desired.Annotations, existing.Annotations) || - !equality.Semantic.DeepDerivative(desired.Rules, existing.Rules) { + + // init Interface for compare + desiredFields := []interface{}{ + desired.GetAnnotations(), + desired.GetLabels(), + desired.Rules, + } + existingFields := []interface{}{ + existing.GetAnnotations(), + existing.GetLabels(), + existing.Rules, + } + + // Compare + if !equality.Semantic.DeepDerivative(desiredFields, existingFields) { + // Update if not equal existing.Labels = desired.Labels existing.Annotations = desired.Annotations existing.Rules = desired.Rules @@ -269,21 +330,36 @@ func reconcileClusterRole(ctx context.Context, obj runtime.Object, c client.Clie return err } -func reconcileClusterRoleBinding(ctx context.Context, obj runtime.Object, c client.Client) error { - - existing := &rbacv1.ClusterRoleBinding{} +func createOrUpdateClusterRoleBinding(ctx context.Context, obj runtime.Object, c client.Client) error { desired := obj.(*rbacv1.ClusterRoleBinding) + // Create ClusterRoleBinding err := c.Create(ctx, desired) - if errors.IsAlreadyExists(err) { + if api_errors.IsAlreadyExists(err) { + // If alredy exist - compare with existed + existing := &rbacv1.ClusterRoleBinding{} err := c.Get(ctx, client.ObjectKeyFromObject(desired), existing) if err != nil { return err } - if !equality.Semantic.DeepDerivative(desired.ObjectMeta.Labels, existing.ObjectMeta.Labels) || - !equality.Semantic.DeepDerivative(desired.Annotations, existing.Annotations) || - !equality.Semantic.DeepDerivative(desired.RoleRef, existing.RoleRef) || - !equality.Semantic.DeepDerivative(desired.Subjects, existing.Subjects) { + + // Init Interface for compare + desiredFields := []interface{}{ + desired.GetAnnotations(), + desired.GetLabels(), + desired.RoleRef, + desired.Subjects, + } + existingFields := []interface{}{ + existing.GetAnnotations(), + existing.GetLabels(), + existing.RoleRef, + existing.Subjects, + } + + // Compare + if !equality.Semantic.DeepDerivative(desiredFields, existingFields) { + // Update if not equal existing.Labels = desired.Labels existing.Annotations = desired.Annotations existing.RoleRef = desired.RoleRef @@ -295,6 +371,75 @@ func reconcileClusterRoleBinding(ctx context.Context, obj runtime.Object, c clie return err } +// Something else: + +func CreatePod(ctx context.Context, pod *corev1.Pod, c client.Client) error { + err := c.Create(ctx, pod) + if api_errors.IsAlreadyExists(err) { + return nil + } + return err +} + +func GetPod(ctx context.Context, namespacedName types.NamespacedName, c client.Client) (*corev1.Pod, error) { + result := &corev1.Pod{} + err := c.Get(ctx, namespacedName, result) + if err != nil { + return nil, err + } + return result, nil +} + +func DeletePod(ctx context.Context, pod *corev1.Pod, c client.Client) error { + if err := c.Delete(ctx, pod); err != nil { + return err + } + return nil +} + +func GetSecret(ctx context.Context, namespacedName types.NamespacedName, c client.Client) (*corev1.Secret, error) { + result := &corev1.Secret{} + err := c.Get(ctx, namespacedName, result) + if err != nil { + return nil, err + } + return result, nil +} + +func DeleteSecret(ctx context.Context, secret *corev1.Secret, c client.Client) error { + if err := c.Delete(ctx, secret); err != nil { + return err + } + return nil +} + +func GetPodLogs(ctx context.Context, pod *corev1.Pod, cs *kubernetes.Clientset) (string, error) { + count := int64(100) + podLogOptions := corev1.PodLogOptions{ + TailLines: &count, + } + + req := cs.CoreV1().Pods(pod.Namespace).GetLogs(pod.Name, &podLogOptions) + podLogs, err := req.Stream(ctx) + if err != nil { + return "", err + } + defer podLogs.Close() + + buf := new(bytes.Buffer) + _, err = io.Copy(buf, podLogs) + if err != nil { + return "", err + } + str := buf.String() + + return str, nil +} + +func UpdateStatus(ctx context.Context, obj client.Object, c client.Client) error { + return c.Status().Update(ctx, obj) +} + func NamespaceNameToLabel(namespace string) string { return "kubernetes.io/metadata.name=" + namespace } diff --git a/controllers/factory/utils/k8s/k8s_test.go b/controllers/factory/utils/k8s/k8s_test.go index 07f548b1..cb5fe3e6 100644 --- a/controllers/factory/utils/k8s/k8s_test.go +++ b/controllers/factory/utils/k8s/k8s_test.go @@ -28,7 +28,7 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" + api_errors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" @@ -36,77 +36,14 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client/fake" ) -func getInitObjectMeta() metav1.ObjectMeta { - ObjectMeta := metav1.ObjectMeta{ - Name: "init", - Namespace: "test-namespace", - } - - return ObjectMeta +type objCase struct { + name string + initObj client.Object + obj client.Object + want error } -var reconcileObjectCase = func(ctx context.Context, objInit, obj interface{}, want error) func(t *testing.T) { - return func(t *testing.T) { - t.Helper() - t.Parallel() - - req := require.New(t) - - switch obj.(type) { - case *corev1.Service: - serviceInit := objInit.(*corev1.Service) - service := obj.(*corev1.Service) - - cl := fake.NewClientBuilder().WithObjects(serviceInit).Build() - err := k8s.CreateOrUpdateService(ctx, service, cl) - req.Equal(err, want) - case *corev1.Secret: - secretInit := objInit.(*corev1.Secret) - secret := obj.(*corev1.Secret) - - cl := fake.NewClientBuilder().WithObjects(secretInit).Build() - err := k8s.CreateOrUpdateSecret(ctx, secret, cl) - req.Equal(err, want) - case *appsv1.DaemonSet: - daemonSetInit := objInit.(*appsv1.DaemonSet) - daemonSet := obj.(*appsv1.DaemonSet) - - cl := fake.NewClientBuilder().WithObjects(daemonSetInit).Build() - err := k8s.CreateOrUpdateDaemonSet(ctx, daemonSet, cl) - req.Equal(err, want) - case *appsv1.StatefulSet: - statefulSetInit := objInit.(*appsv1.StatefulSet) - statefulSet := obj.(*appsv1.StatefulSet) - - cl := fake.NewClientBuilder().WithObjects(statefulSetInit).Build() - err := k8s.CreateOrUpdateStatefulSet(ctx, statefulSet, cl) - req.Equal(err, want) - case *corev1.ServiceAccount: - serviceAccountInit := objInit.(*corev1.ServiceAccount) - serviceAccount := obj.(*corev1.ServiceAccount) - - cl := fake.NewClientBuilder().WithObjects(serviceAccountInit).Build() - err := k8s.CreateOrUpdateServiceAccount(ctx, serviceAccount, cl) - req.Equal(err, want) - case *rbacv1.ClusterRole: - clusterRoleInit := objInit.(*rbacv1.ClusterRole) - clusterRole := obj.(*rbacv1.ClusterRole) - - cl := fake.NewClientBuilder().WithObjects(clusterRoleInit).Build() - err := k8s.CreateOrUpdateClusterRole(ctx, clusterRole, cl) - req.Equal(err, want) - case *rbacv1.ClusterRoleBinding: - clusterRoleBindingInit := objInit.(*rbacv1.ClusterRoleBinding) - clusterRoleBinding := obj.(*rbacv1.ClusterRoleBinding) - - cl := fake.NewClientBuilder().WithObjects(clusterRoleBindingInit).Build() - err := k8s.CreateOrUpdateClusterRoleBinding(ctx, clusterRoleBinding, cl) - req.Equal(err, want) - } - } -} - -var nameRequeriedError = apierrors.NewInvalid( +var nameRequeriedError = api_errors.NewInvalid( schema.GroupKind{}, "", field.ErrorList{ @@ -117,394 +54,594 @@ var nameRequeriedError = apierrors.NewInvalid( }, ) -func TestCreateOrUpdateService(t *testing.T) { - initObj := &corev1.Service{ - ObjectMeta: getInitObjectMeta(), +func getInitObjectMeta() metav1.ObjectMeta { + ObjectMeta := metav1.ObjectMeta{ + Name: "init", + Namespace: "test-namespace", } - type secriveCase struct { - name string - obj *corev1.Service - err error + return ObjectMeta +} + +func TestCreateOrUpdateResource(t *testing.T) { + createOrUpdateResourceCase := func(initObj, obj client.Object, want error) func(t *testing.T) { + return func(t *testing.T) { + t.Helper() + t.Parallel() + req := require.New(t) + + cl := fake.NewClientBuilder().WithObjects(initObj).Build() + result := k8s.CreateOrUpdateResource(context.Background(), obj, cl) + req.Equal(result, want) + } } - secriveCases := []secriveCase{ + var cases []objCase + + // Deployment cases + deploymentCases := []objCase{ { name: "Create Simple case", - obj: &corev1.Service{ + initObj: &appsv1.Deployment{ + ObjectMeta: getInitObjectMeta(), + }, + obj: &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace", }, }, - err: nil, + want: nil, }, { name: "Create Alredy exist case", - obj: &corev1.Service{ + initObj: &appsv1.Deployment{ ObjectMeta: getInitObjectMeta(), }, - err: nil, + obj: &appsv1.Deployment{ + ObjectMeta: getInitObjectMeta(), + }, + want: nil, }, { name: "Create with Another Namespace case", - obj: &corev1.Service{ + initObj: &appsv1.Deployment{ + ObjectMeta: getInitObjectMeta(), + }, + obj: &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace2", }, }, - err: nil, + want: nil, }, { name: "Create without Name case", - obj: &corev1.Service{ + initObj: &appsv1.Deployment{ + ObjectMeta: getInitObjectMeta(), + }, + obj: &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{}, }, - err: nameRequeriedError, + want: nameRequeriedError, + }, + { + name: "Update exist case", + initObj: &appsv1.Deployment{ + ObjectMeta: getInitObjectMeta(), + }, + obj: &appsv1.Deployment{ + ObjectMeta: getInitObjectMeta(), + Spec: appsv1.DeploymentSpec{ + MinReadySeconds: 2, + }, + }, + want: nil, }, } + cases = append(cases, deploymentCases...) - // t.Parallel() - for _, tc := range secriveCases { - t.Run(tc.name, reconcileObjectCase(context.Background(), initObj, tc.obj, tc.err)) - } -} - -func TestCreateOrUpdateSecret(t *testing.T) { - initObj := &corev1.Secret{ - ObjectMeta: getInitObjectMeta(), - } - - type secretCase struct { - name string - obj *corev1.Secret - err error - } - - secretCases := []secretCase{ + // StatefulSet cases + statefulSetCases := []objCase{ { name: "Create Simple case", - obj: &corev1.Secret{ + initObj: &appsv1.StatefulSet{ + ObjectMeta: getInitObjectMeta(), + }, + obj: &appsv1.StatefulSet{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace", }, }, - err: nil, + want: nil, }, { name: "Create Alredy exist case", - obj: &corev1.Secret{ + initObj: &appsv1.StatefulSet{ ObjectMeta: getInitObjectMeta(), }, - err: nil, + obj: &appsv1.StatefulSet{ + ObjectMeta: getInitObjectMeta(), + }, + want: nil, }, { name: "Create with Another Namespace case", - obj: &corev1.Secret{ + initObj: &appsv1.StatefulSet{ + ObjectMeta: getInitObjectMeta(), + }, + obj: &appsv1.StatefulSet{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace2", }, }, - err: nil, + want: nil, }, { name: "Create without Name case", - obj: &corev1.Secret{ + initObj: &appsv1.StatefulSet{ + ObjectMeta: getInitObjectMeta(), + }, + obj: &appsv1.StatefulSet{ ObjectMeta: metav1.ObjectMeta{}, }, - err: nameRequeriedError, + want: nameRequeriedError, + }, + { + name: "Update exist case", + initObj: &appsv1.StatefulSet{ + ObjectMeta: getInitObjectMeta(), + }, + obj: &appsv1.StatefulSet{ + ObjectMeta: getInitObjectMeta(), + Spec: appsv1.StatefulSetSpec{ + MinReadySeconds: 2, + }, + }, + want: nil, }, } + cases = append(cases, statefulSetCases...) - // t.Parallel() - for _, tc := range secretCases { - t.Run(tc.name, reconcileObjectCase(context.Background(), initObj, tc.obj, tc.err)) - } -} - -func TestCreateOrUpdateDaemonSet(t *testing.T) { - initObj := &appsv1.DaemonSet{ - ObjectMeta: getInitObjectMeta(), - } - - type daemonSetCase struct { - name string - obj *appsv1.DaemonSet - err error - } - - daemonSetCases := []daemonSetCase{ + // DaemonSet cases + daemonSetCases := []objCase{ { name: "Create Simple case", + initObj: &appsv1.DaemonSet{ + ObjectMeta: getInitObjectMeta(), + }, obj: &appsv1.DaemonSet{ ObjectMeta: metav1.ObjectMeta{ - Name: "init", + Name: "test", Namespace: "test-namespace", }, }, - err: nil, + want: nil, }, { name: "Create Alredy exist case", + initObj: &appsv1.DaemonSet{ + ObjectMeta: getInitObjectMeta(), + }, obj: &appsv1.DaemonSet{ ObjectMeta: getInitObjectMeta(), }, - err: nil, + want: nil, }, { name: "Create with Another Namespace case", + initObj: &appsv1.DaemonSet{ + ObjectMeta: getInitObjectMeta(), + }, obj: &appsv1.DaemonSet{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace2", }, }, - err: nil, + want: nil, }, { name: "Create without Name case", + initObj: &appsv1.DaemonSet{ + ObjectMeta: getInitObjectMeta(), + }, obj: &appsv1.DaemonSet{ ObjectMeta: metav1.ObjectMeta{}, }, - err: nameRequeriedError, + want: nameRequeriedError, + }, + { + name: "Update exist case", + initObj: &appsv1.DaemonSet{ + ObjectMeta: getInitObjectMeta(), + }, + obj: &appsv1.DaemonSet{ + ObjectMeta: getInitObjectMeta(), + Spec: appsv1.DaemonSetSpec{ + MinReadySeconds: 2, + }, + }, + want: nil, }, } + cases = append(cases, daemonSetCases...) - // t.Parallel() - for _, tc := range daemonSetCases { - t.Run(tc.name, reconcileObjectCase(context.Background(), initObj, tc.obj, tc.err)) - } -} - -func TestCreateOrUpdateStatefulSet(t *testing.T) { - initObj := &appsv1.StatefulSet{ - ObjectMeta: getInitObjectMeta(), - } - - type statefulSetCase struct { - name string - obj *appsv1.StatefulSet - err error - } - - statefulSetCases := []statefulSetCase{ + // Secret cases + secretCases := []objCase{ { name: "Create Simple case", - obj: &appsv1.StatefulSet{ + initObj: &corev1.Secret{ + ObjectMeta: getInitObjectMeta(), + }, + obj: &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: "init", + Name: "test", Namespace: "test-namespace", }, }, - err: nil, + want: nil, }, { name: "Create Alredy exist case", - obj: &appsv1.StatefulSet{ + initObj: &corev1.Secret{ ObjectMeta: getInitObjectMeta(), }, - err: nil, + obj: &corev1.Secret{ + ObjectMeta: getInitObjectMeta(), + }, + want: nil, }, { name: "Create with Another Namespace case", - obj: &appsv1.StatefulSet{ + initObj: &corev1.Secret{ + ObjectMeta: getInitObjectMeta(), + }, + obj: &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace2", }, }, - err: nil, + want: nil, }, { name: "Create without Name case", - obj: &appsv1.StatefulSet{ + initObj: &corev1.Secret{ + ObjectMeta: getInitObjectMeta(), + }, + obj: &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{}, }, - err: nameRequeriedError, + want: nameRequeriedError, + }, + { + name: "Update exist case", + initObj: &corev1.Secret{ + ObjectMeta: getInitObjectMeta(), + }, + obj: &corev1.Secret{ + ObjectMeta: getInitObjectMeta(), + Data: map[string][]byte{ + "test": []byte("test"), + }, + }, + want: nil, }, } + cases = append(cases, secretCases...) - // t.Parallel() - for _, tc := range statefulSetCases { - t.Run(tc.name, reconcileObjectCase(context.Background(), initObj, tc.obj, tc.err)) - } -} - -func TestCreateOrUpdateServiceAccount(t *testing.T) { - initObj := &corev1.ServiceAccount{ - ObjectMeta: getInitObjectMeta(), - } - - type serviceAccountCase struct { - name string - obj *corev1.ServiceAccount - err error + // Service cases + serviceCases := []objCase{ + { + name: "Create Simple case", + initObj: &corev1.Service{ + ObjectMeta: getInitObjectMeta(), + }, + obj: &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace", + }, + }, + want: nil, + }, + { + name: "Create Alredy exist case", + initObj: &corev1.Service{ + ObjectMeta: getInitObjectMeta(), + }, + obj: &corev1.Service{ + ObjectMeta: getInitObjectMeta(), + }, + want: nil, + }, + { + name: "Create with Another Namespace case", + initObj: &corev1.Service{ + ObjectMeta: getInitObjectMeta(), + }, + obj: &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace2", + }, + }, + want: nil, + }, + { + name: "Create without Name case", + initObj: &corev1.Service{ + ObjectMeta: getInitObjectMeta(), + }, + obj: &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{}, + }, + want: nameRequeriedError, + }, + { + name: "Update exist case", + initObj: &corev1.Service{ + ObjectMeta: getInitObjectMeta(), + }, + obj: &corev1.Service{ + ObjectMeta: getInitObjectMeta(), + Spec: corev1.ServiceSpec{ + ClusterIP: "1.1.1.1", + }, + }, + want: nil, + }, } + cases = append(cases, serviceCases...) - serviceAccountCases := []serviceAccountCase{ + // ServiceAccount cases + serviceAccountCases := []objCase{ { name: "Create Simple case", + initObj: &corev1.ServiceAccount{ + ObjectMeta: getInitObjectMeta(), + }, obj: &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ - Name: "init", + Name: "test", Namespace: "test-namespace", }, }, - err: nil, + want: nil, }, { name: "Create Alredy exist case", + initObj: &corev1.ServiceAccount{ + ObjectMeta: getInitObjectMeta(), + }, obj: &corev1.ServiceAccount{ ObjectMeta: getInitObjectMeta(), }, - err: nil, + want: nil, }, { name: "Create with Another Namespace case", + initObj: &corev1.ServiceAccount{ + ObjectMeta: getInitObjectMeta(), + }, obj: &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace2", }, }, - err: nil, + want: nil, }, { name: "Create without Name case", + initObj: &corev1.ServiceAccount{ + ObjectMeta: getInitObjectMeta(), + }, obj: &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{}, }, - err: nameRequeriedError, + want: nameRequeriedError, + }, + { + name: "Update exist case", + initObj: &corev1.ServiceAccount{ + ObjectMeta: getInitObjectMeta(), + }, + obj: &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: "init", + Namespace: "test-namespace", + Labels: map[string]string{ + "test": "test", + }, + }, + }, + want: nil, }, } + cases = append(cases, serviceAccountCases...) - // t.Parallel() - for _, tc := range serviceAccountCases { - t.Run(tc.name, reconcileObjectCase(context.Background(), initObj, tc.obj, tc.err)) - } -} - -func TestCreateOrUpdateClusterRole(t *testing.T) { - initObj := &rbacv1.ClusterRole{ - ObjectMeta: getInitObjectMeta(), - } - - type clusterRoleCase struct { - name string - obj *rbacv1.ClusterRole - err error - } - - clusterRoleCases := []clusterRoleCase{ + // ClusterRole cases + clusterRoleCases := []objCase{ { name: "Create Simple case", + initObj: &rbacv1.ClusterRole{ + ObjectMeta: getInitObjectMeta(), + }, obj: &rbacv1.ClusterRole{ ObjectMeta: metav1.ObjectMeta{ - Name: "init", + Name: "test", Namespace: "test-namespace", }, }, - err: nil, + want: nil, }, { name: "Create Alredy exist case", + initObj: &rbacv1.ClusterRole{ + ObjectMeta: getInitObjectMeta(), + }, obj: &rbacv1.ClusterRole{ ObjectMeta: getInitObjectMeta(), }, - err: nil, + want: nil, }, { name: "Create with Another Namespace case", + initObj: &rbacv1.ClusterRole{ + ObjectMeta: getInitObjectMeta(), + }, obj: &rbacv1.ClusterRole{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace2", }, }, - err: nil, + want: nil, }, { name: "Create without Name case", + initObj: &rbacv1.ClusterRole{ + ObjectMeta: getInitObjectMeta(), + }, obj: &rbacv1.ClusterRole{ ObjectMeta: metav1.ObjectMeta{}, }, - err: nameRequeriedError, + want: nameRequeriedError, + }, + { + name: "Update exist case", + initObj: &rbacv1.ClusterRole{ + ObjectMeta: getInitObjectMeta(), + }, + obj: &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "init", + Namespace: "test-namespace", + Labels: map[string]string{ + "test": "test", + }, + }, + }, + want: nil, }, } + cases = append(cases, clusterRoleCases...) - // t.Parallel() - for _, tc := range clusterRoleCases { - t.Run(tc.name, reconcileObjectCase(context.Background(), initObj, tc.obj, tc.err)) - } -} - -func TestCreateOrUpdateClusterRoleBinding(t *testing.T) { - initObj := &rbacv1.ClusterRoleBinding{ - ObjectMeta: getInitObjectMeta(), - } - - type clusterRoleBindingCase struct { - name string - obj *rbacv1.ClusterRoleBinding - err error - } - - clusterRoleBindingCases := []clusterRoleBindingCase{ + // ClusterRoleBinding cases + clusterRoleBindingCases := []objCase{ { name: "Create Simple case", + initObj: &rbacv1.ClusterRoleBinding{ + ObjectMeta: getInitObjectMeta(), + }, obj: &rbacv1.ClusterRoleBinding{ ObjectMeta: metav1.ObjectMeta{ - Name: "init", + Name: "test", Namespace: "test-namespace", }, }, - err: nil, + want: nil, }, { name: "Create Alredy exist case", + initObj: &rbacv1.ClusterRoleBinding{ + ObjectMeta: getInitObjectMeta(), + }, obj: &rbacv1.ClusterRoleBinding{ ObjectMeta: getInitObjectMeta(), }, - err: nil, + want: nil, }, { name: "Create with Another Namespace case", + initObj: &rbacv1.ClusterRoleBinding{ + ObjectMeta: getInitObjectMeta(), + }, obj: &rbacv1.ClusterRoleBinding{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test-namespace2", }, }, - err: nil, + want: nil, }, { name: "Create without Name case", + initObj: &rbacv1.ClusterRoleBinding{ + ObjectMeta: getInitObjectMeta(), + }, obj: &rbacv1.ClusterRoleBinding{ ObjectMeta: metav1.ObjectMeta{}, }, - err: nameRequeriedError, + want: nameRequeriedError, + }, + { + name: "Update exist case", + initObj: &rbacv1.ClusterRoleBinding{ + ObjectMeta: getInitObjectMeta(), + }, + obj: &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "init", + Namespace: "test-namespace", + Labels: map[string]string{ + "test": "test", + }, + }, + }, + want: nil, + }, + } + cases = append(cases, clusterRoleBindingCases...) + + // Not supported type case + notSuppurtedcase := []objCase{ + { + name: "Update exist case", + initObj: &rbacv1.RoleBinding{ + ObjectMeta: getInitObjectMeta(), + }, + obj: &rbacv1.RoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "init", + Namespace: "test-namespace", + Labels: map[string]string{ + "test": "test", + }, + }, + }, + want: k8s.NewNotSupportedError(&rbacv1.RoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "init", + Namespace: "test-namespace", + Labels: map[string]string{ + "test": "test", + }, + }, + }, + ), }, } + cases = append(cases, notSuppurtedcase...) - // t.Parallel() - for _, tc := range clusterRoleBindingCases { - t.Run(tc.name, reconcileObjectCase(context.Background(), initObj, tc.obj, tc.err)) + for _, tc := range cases { + t.Run(tc.name, createOrUpdateResourceCase(tc.initObj, tc.obj, tc.want)) } } func TestCreatePod(t *testing.T) { createPodCase := func(objInit, obj *corev1.Pod, want error) func(t *testing.T) { - // t.Parallel() return func(t *testing.T) { t.Helper() - + t.Parallel() req := require.New(t) cl := fake.NewClientBuilder().WithObjects(objInit).Build() - err := k8s.CreatePod(context.Background(), obj, cl) req.Equal(err, want) } @@ -536,17 +673,17 @@ func TestCreatePod(t *testing.T) { obj: &corev1.Pod{ ObjectMeta: getInitObjectMeta(), }, - err: apierrors.NewAlreadyExists( - schema.GroupResource{ - Group: "", - Resource: "pods", - }, - "init", - ), + err: nil, + // err: api_errors.NewAlreadyExists( + // schema.GroupResource{ + // Group: "", + // Resource: "pods", + // }, + // "init", + // ), }, } - // t.Parallel() for _, tc := range podCases { t.Run(tc.name, createPodCase(initObj, tc.obj, tc.err)) } @@ -554,15 +691,13 @@ func TestCreatePod(t *testing.T) { func TestGetPod(t *testing.T) { getPodCase := func(objInit, obj, wantPod *corev1.Pod, want error) func(t *testing.T) { - // t.Parallel() return func(t *testing.T) { t.Helper() - + t.Parallel() req := require.New(t) cl := fake.NewClientBuilder().WithObjects(objInit).Build() - - result, err := k8s.GetPod(context.Background(), obj, cl) + result, err := k8s.GetPod(context.Background(), client.ObjectKeyFromObject(obj), cl) if result != nil { req.Equal(result.ObjectMeta, wantPod.ObjectMeta) } @@ -570,12 +705,9 @@ func TestGetPod(t *testing.T) { } } - initObj := &corev1.Pod{ - ObjectMeta: getInitObjectMeta(), - } - type podCase struct { name string + initObj *corev1.Pod obj *corev1.Pod wantObj *corev1.Pod err error @@ -584,6 +716,9 @@ func TestGetPod(t *testing.T) { podCases := []podCase{ { name: "Get not exist pod case", + initObj: &corev1.Pod{ + ObjectMeta: getInitObjectMeta(), + }, obj: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "test", @@ -591,7 +726,7 @@ func TestGetPod(t *testing.T) { }, }, wantObj: nil, - err: apierrors.NewNotFound( + err: api_errors.NewNotFound( schema.GroupResource{ Group: "", Resource: "pods", @@ -601,6 +736,9 @@ func TestGetPod(t *testing.T) { }, { name: "Get exist pod case", + initObj: &corev1.Pod{ + ObjectMeta: getInitObjectMeta(), + }, obj: &corev1.Pod{ ObjectMeta: getInitObjectMeta(), }, @@ -615,24 +753,199 @@ func TestGetPod(t *testing.T) { }, } - // t.Parallel() for _, tc := range podCases { - t.Run(tc.name, getPodCase(initObj, tc.obj, tc.wantObj, tc.err)) + t.Run(tc.name, getPodCase(tc.initObj, tc.obj, tc.wantObj, tc.err)) } } -func TestUpdateStatus(t *testing.T) { - updateStatusCase := func(objInit, obj client.Object, want error) func(t *testing.T) { - // t.Parallel() +func TestDeletePod(t *testing.T) { + deletePodCase := func(objInit, obj client.Object, want error) func(t *testing.T) { + return func(t *testing.T) { + t.Helper() + t.Parallel() + req := require.New(t) + + cl := fake.NewClientBuilder().WithObjects(objInit).Build() + pod := obj.(*corev1.Pod) + err := k8s.DeletePod(context.Background(), pod, cl) + req.Equal(err, want) + } + } + + var cases []objCase + + // DeletePodcases + deletePodCases := []objCase{ + { + name: "Delete Simple case", + initObj: &corev1.Pod{ + ObjectMeta: getInitObjectMeta(), + }, + obj: &corev1.Pod{ + ObjectMeta: getInitObjectMeta(), + }, + want: nil, + }, + { + name: "Delete not exist case", + initObj: &corev1.Pod{ + ObjectMeta: getInitObjectMeta(), + }, + obj: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace2", + }, + }, + want: api_errors.NewNotFound( + schema.GroupResource{ + Group: "", + Resource: "pods", + }, + "test", + ), + }, + } + cases = append(cases, deletePodCases...) + + for _, tc := range cases { + t.Run(tc.name, deletePodCase(tc.initObj, tc.obj, tc.want)) + } +} + +func TestGetSecret(t *testing.T) { + getSecretCase := func(objInit, obj, wantSecret *corev1.Secret, want error) func(t *testing.T) { return func(t *testing.T) { t.Helper() + t.Parallel() + req := require.New(t) + + cl := fake.NewClientBuilder().WithObjects(objInit).Build() + result, err := k8s.GetSecret(context.Background(), client.ObjectKeyFromObject(obj), cl) + if result != nil { + req.Equal(result.ObjectMeta, wantSecret.ObjectMeta) + } + req.Equal(err, want) + } + } + + type secretCase struct { + name string + initObj *corev1.Secret + obj *corev1.Secret + wantObj *corev1.Secret + err error + } + + podCases := []secretCase{ + { + name: "Get not exist secret case", + initObj: &corev1.Secret{ + ObjectMeta: getInitObjectMeta(), + }, + obj: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace", + }, + }, + wantObj: nil, + err: api_errors.NewNotFound( + schema.GroupResource{ + Group: "", + Resource: "secrets", + }, + "test", + ), + }, + { + name: "Get exist secret case", + initObj: &corev1.Secret{ + ObjectMeta: getInitObjectMeta(), + }, + obj: &corev1.Secret{ + ObjectMeta: getInitObjectMeta(), + }, + wantObj: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "init", + Namespace: "test-namespace", + ResourceVersion: "999", + }, + }, + err: nil, + }, + } + + for _, tc := range podCases { + t.Run(tc.name, getSecretCase(tc.initObj, tc.obj, tc.wantObj, tc.err)) + } +} +func TestDeleteSecret(t *testing.T) { + deleteSecretCase := func(objInit, obj client.Object, want error) func(t *testing.T) { + return func(t *testing.T) { + t.Helper() + t.Parallel() req := require.New(t) cl := fake.NewClientBuilder().WithObjects(objInit).Build() + secret := obj.(*corev1.Secret) + err := k8s.DeleteSecret(context.Background(), secret, cl) + req.Equal(err, want) + } + } - err := k8s.UpdateStatus(context.Background(), obj, cl) + var cases []objCase + + // DeletePodcases + deleteSecretCases := []objCase{ + { + name: "Delete Simple case", + initObj: &corev1.Secret{ + ObjectMeta: getInitObjectMeta(), + }, + obj: &corev1.Secret{ + ObjectMeta: getInitObjectMeta(), + }, + want: nil, + }, + { + name: "Delete not exist case", + initObj: &corev1.Secret{ + ObjectMeta: getInitObjectMeta(), + }, + obj: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace2", + }, + }, + want: api_errors.NewNotFound( + schema.GroupResource{ + Group: "", + Resource: "secrets", + }, + "test", + ), + }, + } + cases = append(cases, deleteSecretCases...) + + for _, tc := range cases { + t.Run(tc.name, deleteSecretCase(tc.initObj, tc.obj, tc.want)) + } +} + +func TestUpdateStatus(t *testing.T) { + updateStatusCase := func(objInit, obj client.Object, want error) func(t *testing.T) { + return func(t *testing.T) { + t.Helper() + t.Parallel() + req := require.New(t) + cl := fake.NewClientBuilder().WithObjects(objInit).Build() + err := k8s.UpdateStatus(context.Background(), obj, cl) req.Equal(err, want) } } @@ -690,8 +1003,43 @@ func TestUpdateStatus(t *testing.T) { }, } - // t.Parallel() for _, tc := range testCases { t.Run(tc.name, updateStatusCase(tc.initObj, tc.updateObj, tc.err)) } } + +func TestNamespaceNameToLabel(t *testing.T) { + namespaceNameToLabelCase := func(in, want string) func(t *testing.T) { + return func(t *testing.T) { + t.Helper() + t.Parallel() + req := require.New(t) + + result := k8s.NamespaceNameToLabel(in) + req.Equal(result, want) + } + } + + type testCase struct { + name string + in string + want string + } + + testCases := []testCase{ + { + name: "Simple case", + in: "test", + want: "kubernetes.io/metadata.name=test", + }, + { + name: "Zero case", + in: "", + want: "kubernetes.io/metadata.name=", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, namespaceNameToLabelCase(tc.in, tc.want)) + } +} diff --git a/controllers/factory/vector/vectoragent/vectoragent_controller.go b/controllers/factory/vector/vectoragent/vectoragent_controller.go index 914600b1..1c1dfa22 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_controller.go +++ b/controllers/factory/vector/vectoragent/vectoragent_controller.go @@ -72,19 +72,19 @@ func (ctrl *Controller) ensureVectorAgentRBAC(ctx context.Context) error { func (ctrl *Controller) ensureVectorAgentServiceAccount(ctx context.Context) error { vectorAgentServiceAccount := ctrl.createVectorAgentServiceAccount() - return k8s.CreateOrUpdateServiceAccount(ctx, vectorAgentServiceAccount, ctrl.Client) + return k8s.CreateOrUpdateResource(ctx, vectorAgentServiceAccount, ctrl.Client) } func (ctrl *Controller) ensureVectorAgentClusterRole(ctx context.Context) error { vectorAgentClusterRole := ctrl.createVectorAgentClusterRole() - return k8s.CreateOrUpdateClusterRole(ctx, vectorAgentClusterRole, ctrl.Client) + return k8s.CreateOrUpdateResource(ctx, vectorAgentClusterRole, ctrl.Client) } func (ctrl *Controller) ensureVectorAgentClusterRoleBinding(ctx context.Context) error { vectorAgentClusterRoleBinding := ctrl.createVectorAgentClusterRoleBinding() - return k8s.CreateOrUpdateClusterRoleBinding(ctx, vectorAgentClusterRoleBinding, ctrl.Client) + return k8s.CreateOrUpdateResource(ctx, vectorAgentClusterRoleBinding, ctrl.Client) } func (ctrl *Controller) ensureVectorAgentService(ctx context.Context) error { @@ -94,7 +94,7 @@ func (ctrl *Controller) ensureVectorAgentService(ctx context.Context) error { vectorAgentService := ctrl.createVectorAgentService() - return k8s.CreateOrUpdateService(ctx, vectorAgentService, ctrl.Client) + return k8s.CreateOrUpdateResource(ctx, vectorAgentService, ctrl.Client) } func (ctrl *Controller) ensureVectorAgentConfig(ctx context.Context) error { @@ -107,7 +107,7 @@ func (ctrl *Controller) ensureVectorAgentConfig(ctx context.Context) error { return err } - return k8s.CreateOrUpdateSecret(ctx, vectorAgentSecret, ctrl.Client) + return k8s.CreateOrUpdateResource(ctx, vectorAgentSecret, ctrl.Client) } func (ctrl *Controller) ensureVectorAgentDaemonSet(ctx context.Context) error { @@ -117,7 +117,7 @@ func (ctrl *Controller) ensureVectorAgentDaemonSet(ctx context.Context) error { vectorAgentDaemonSet := ctrl.createVectorAgentDaemonSet() - return k8s.CreateOrUpdateDaemonSet(ctx, vectorAgentDaemonSet, ctrl.Client) + return k8s.CreateOrUpdateResource(ctx, vectorAgentDaemonSet, ctrl.Client) } func (ctrl *Controller) labelsForVectorAgent() map[string]string { From b8b499c7ed16a9de8abda50b2071918ed210e131 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Fri, 11 Nov 2022 11:36:03 +0200 Subject: [PATCH 086/316] Cleanup k8s utils Signed-off-by: Zemtsov Vladimir --- controllers/factory/utils/k8s/k8s.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/controllers/factory/utils/k8s/k8s.go b/controllers/factory/utils/k8s/k8s.go index be7242af..6706c24a 100644 --- a/controllers/factory/utils/k8s/k8s.go +++ b/controllers/factory/utils/k8s/k8s.go @@ -45,8 +45,7 @@ func NewNotSupportedError(obj client.Object) error { func CreateOrUpdateResource(ctx context.Context, obj client.Object, c client.Client) error { switch obj.(type) { case *appsv1.Deployment: - runtimeObj := obj.DeepCopyObject() - return createOrUpdateDeployment(ctx, runtimeObj, c) + return createOrUpdateDeployment(ctx, obj, c) case *appsv1.StatefulSet: return createOrUpdateStatefulSet(ctx, obj, c) case *appsv1.DaemonSet: @@ -56,8 +55,7 @@ func CreateOrUpdateResource(ctx context.Context, obj client.Object, c client.Cli case *corev1.Service: return createOrUpdateService(ctx, obj, c) case *corev1.ServiceAccount: - runtimeObj := obj.DeepCopyObject() - return createOrUpdateServiceAccount(ctx, runtimeObj, c) + return createOrUpdateServiceAccount(ctx, obj, c) case *rbacv1.ClusterRole: return createOrUpdateClusterRole(ctx, obj, c) case *rbacv1.ClusterRoleBinding: From 7239cabfd6e1bcad5390322023682334bc36c70e Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Sat, 12 Nov 2022 09:49:46 +0200 Subject: [PATCH 087/316] first commit --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ac393f96..7e3ff295 100644 --- a/README.md +++ b/README.md @@ -115,3 +115,4 @@ make manifests More information can be found via the [Kubebuilder Documentation](https://book.kubebuilder.io/introduction.html) +# lighthouse-role From 49fae632d446178f4e367c7c92798f3ae6a8f642 Mon Sep 17 00:00:00 2001 From: Vladimir <31961982+zvlb@users.noreply.github.com> Date: Sat, 12 Nov 2022 09:53:45 +0200 Subject: [PATCH 088/316] Update README.md Fix mistake --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 7e3ff295..998e05c2 100644 --- a/README.md +++ b/README.md @@ -114,5 +114,3 @@ make manifests **NOTE:** Run `make --help` for more information on all potential `make` targets More information can be found via the [Kubebuilder Documentation](https://book.kubebuilder.io/introduction.html) - -# lighthouse-role From 01269f5b9a8d9bc20e60a1124eda2a33b4844d26 Mon Sep 17 00:00:00 2001 From: Ilya Batyukov Date: Tue, 15 Nov 2022 11:09:10 +0300 Subject: [PATCH 089/316] try to add vectorpipeline & clustervectorpipeline --- helm/templates/clustervectorpipeline.yaml | 8 ++++++++ helm/templates/vectorpipeline.yaml | 8 ++++++++ helm/values.yaml | 8 ++++++++ 3 files changed, 24 insertions(+) create mode 100644 helm/templates/clustervectorpipeline.yaml create mode 100644 helm/templates/vectorpipeline.yaml diff --git a/helm/templates/clustervectorpipeline.yaml b/helm/templates/clustervectorpipeline.yaml new file mode 100644 index 00000000..f8fd5dfa --- /dev/null +++ b/helm/templates/clustervectorpipeline.yaml @@ -0,0 +1,8 @@ +{{- if .Values.ClusterVectorPipeline -}} +apiVersion: observability.kaasops.io/v1alpha1 +kind: ClusterVectorPipeline +metadata: + name: {{ .Values.ClusterVectorPipeline.name }} +spec: +{{- toYaml .Values.ClusterVectorPipeline.spec | nindent 2 }} + {{- end }} \ No newline at end of file diff --git a/helm/templates/vectorpipeline.yaml b/helm/templates/vectorpipeline.yaml new file mode 100644 index 00000000..55142a87 --- /dev/null +++ b/helm/templates/vectorpipeline.yaml @@ -0,0 +1,8 @@ +{{- if .Values.VectorPipeline -}} +apiVersion: observability.kaasops.io/v1alpha1 +kind: VectorPipeline +metadata: + name: {{ .Values.VectorPipeline.name }} +spec: +{{- toYaml .Values.VectorPipeline.spec | nindent 2 }} + {{- end }} \ No newline at end of file diff --git a/helm/values.yaml b/helm/values.yaml index 27e12d31..f2127b2e 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -29,3 +29,11 @@ serviceAccount: create: false name: "vector-operator" annotations: {} + +ClusterVectorPipeline: + name: test1 + spec: {} + +VectorPipeline: + name: test12 + spec: {} From 06e616379588fed1a4b6be355c6dee818a0999ae Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Tue, 15 Nov 2022 10:48:25 +0200 Subject: [PATCH 090/316] fix namespace validate logic (#44) --- controllers/factory/config/config_build.go | 20 ++++++++++++++------ controllers/factory/config/types.go | 2 +- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/controllers/factory/config/config_build.go b/controllers/factory/config/config_build.go index 8512d3ea..be158360 100644 --- a/controllers/factory/config/config_build.go +++ b/controllers/factory/config/config_build.go @@ -44,6 +44,11 @@ var ( } ) +var ( + PipelineTypeError error = errors.New("type kubernetes_logs only allowed") + PipelineScopeError error = errors.New("logs from external namespace not allowed") +) + type Builder struct { Name string vaCtrl *vectoragent.Controller @@ -72,7 +77,6 @@ func (b *Builder) GetByteConfig() ([]byte, error) { } func (b *Builder) GetByteConfigWithValidate() ([]byte, error) { - validateError := errors.New("type kubernetes_logs only allowed") config, err := b.generateVectorConfig() if err != nil { return nil, err @@ -82,10 +86,12 @@ func (b *Builder) GetByteConfigWithValidate() ([]byte, error) { for _, source := range config.Sources { if pipeline.Type() != vectorv1alpha1.ClusterPipelineKind { if source.Type != KubernetesSourceType { - return nil, validateError + return nil, PipelineTypeError } - if source.ExtraNamespaceLabelSelector != "" && source.ExtraNamespaceLabelSelector != k8s.NamespaceNameToLabel(pipeline.GetNamespace()) { - return nil, validateError + if source.ExtraNamespaceLabelSelector != "" { + if source.ExtraNamespaceLabelSelector != k8s.NamespaceNameToLabel(pipeline.GetNamespace()) { + return nil, PipelineScopeError + } } } } @@ -131,8 +137,10 @@ func (b *Builder) getComponents() (sources []*Source, transforms []*Transform, s if err != nil { return nil, nil, nil, err } - if pipeline.Type() != vectorv1alpha1.ClusterPipelineKind && source.Type == KubernetesSourceType { - source.ExtraNamespaceLabelSelector = k8s.NamespaceNameToLabel(pipeline.GetNamespace()) + if source.Type == KubernetesSourceType { + if pipeline.Type() != vectorv1alpha1.ClusterPipelineKind && source.ExtraNamespaceLabelSelector == "" { + source.ExtraNamespaceLabelSelector = k8s.NamespaceNameToLabel(pipeline.GetNamespace()) + } } sources = append(sources, source) } diff --git a/controllers/factory/config/types.go b/controllers/factory/config/types.go index d62ca2a5..834e4ede 100644 --- a/controllers/factory/config/types.go +++ b/controllers/factory/config/types.go @@ -31,7 +31,7 @@ type VectorConfig struct { type Source struct { Name string Type string `mapper:"type"` - ExtraNamespaceLabelSelector string `mapper:"extra_namespace_label_selector,omitempty"` + ExtraNamespaceLabelSelector string `mapstructure:"extra_namespace_label_selector" mapper:"extra_namespace_label_selector,omitempty"` Options map[string]interface{} `mapstructure:",remain"` } From 800aff4c0f7ba5a6e7bf32e8d4960da6c598f4a7 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Tue, 15 Nov 2022 10:51:02 +0200 Subject: [PATCH 091/316] GetPodLogs test (#45) --- controllers/factory/utils/k8s/k8s.go | 2 +- controllers/factory/utils/k8s/k8s_test.go | 40 +++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/controllers/factory/utils/k8s/k8s.go b/controllers/factory/utils/k8s/k8s.go index 6706c24a..31e2666d 100644 --- a/controllers/factory/utils/k8s/k8s.go +++ b/controllers/factory/utils/k8s/k8s.go @@ -411,7 +411,7 @@ func DeleteSecret(ctx context.Context, secret *corev1.Secret, c client.Client) e return nil } -func GetPodLogs(ctx context.Context, pod *corev1.Pod, cs *kubernetes.Clientset) (string, error) { +func GetPodLogs(ctx context.Context, pod *corev1.Pod, cs kubernetes.Interface) (string, error) { count := int64(100) podLogOptions := corev1.PodLogOptions{ TailLines: &count, diff --git a/controllers/factory/utils/k8s/k8s_test.go b/controllers/factory/utils/k8s/k8s_test.go index cb5fe3e6..c2c71975 100644 --- a/controllers/factory/utils/k8s/k8s_test.go +++ b/controllers/factory/utils/k8s/k8s_test.go @@ -32,6 +32,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" + fakeclientset "k8s.io/client-go/kubernetes/fake" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" ) @@ -1043,3 +1044,42 @@ func TestNamespaceNameToLabel(t *testing.T) { t.Run(tc.name, namespaceNameToLabelCase(tc.in, tc.want)) } } + +func TestGetPodLogs(t *testing.T) { + getPodLogsCase := func(initPod, pod *corev1.Pod, want string, err error) func(t *testing.T) { + return func(t *testing.T) { + t.Helper() + t.Parallel() + req := require.New(t) + clientset := fakeclientset.NewSimpleClientset(initPod) + result, err1 := k8s.GetPodLogs(context.TODO(), pod, clientset) + if result != "" { + req.Equal(result, want) + } + req.Equal(err1, err) + } + } + type testCase struct { + name string + initPod *corev1.Pod + pod *corev1.Pod + want string + err error + } + testCases := []testCase{ + { + name: "Simple case", + initPod: &corev1.Pod{ + ObjectMeta: getInitObjectMeta(), + }, + pod: &corev1.Pod{ + ObjectMeta: getInitObjectMeta(), + }, + want: "fake logs", + err: nil, + }, + } + for _, tc := range testCases { + t.Run(tc.name, getPodLogsCase(tc.initPod, tc.pod, tc.want, tc.err)) + } +} From 91b4bf108880bb8f92ee67ec4f4775d7e0ef75f5 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Tue, 15 Nov 2022 11:04:15 +0200 Subject: [PATCH 092/316] Update changelog for release Signed-off-by: Zemtsov Vladimir --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c0205cb..873a06b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,8 @@ ### Added +- [[45](https://github.com/kaasops/vector-operator/pull/45)] **Tests**: Add tests for k8s utils && refactor k8s utils +- [[44](https://github.com/kaasops/vector-operator/pull/44)] **Features**: Add validations errors for VectorPipeline +- [[37](https://github.com/kaasops/vector-operator/pull/37)] **Cleanup**: Fix context-in-struct warning +- [[32](https://github.com/kaasops/vector-operator/pull/32)] **Refactor**: Config build refactoring - [[40](https://github.com/kaasops/vector-operator/pull/40)] **Fix**: Sloved context forward errors From b68f3c58dd5285b186ac90722a9b02db1bac9b57 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Tue, 15 Nov 2022 11:05:25 +0200 Subject: [PATCH 093/316] Release changelog Signed-off-by: Zemtsov Vladimir --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 873a06b3..333c3b53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,7 @@ ### Added + + +### v0.0.2 - [[45](https://github.com/kaasops/vector-operator/pull/45)] **Tests**: Add tests for k8s utils && refactor k8s utils - [[44](https://github.com/kaasops/vector-operator/pull/44)] **Features**: Add validations errors for VectorPipeline - [[37](https://github.com/kaasops/vector-operator/pull/37)] **Cleanup**: Fix context-in-struct warning From 4803492d062311b3e3439662070dcad45ef12d00 Mon Sep 17 00:00:00 2001 From: Ilya Batyukov Date: Wed, 16 Nov 2022 15:04:41 +0300 Subject: [PATCH 094/316] try to add vectorpipeline & clustervectorpipeline --- helm/templates/clustervectorpipeline.yaml | 21 ++++++++++----- helm/templates/vector.yaml | 15 +++++++++++ helm/templates/vectorpipeline.yaml | 17 ++++++++---- helm/values.yaml | 33 +++++++++++++++++++---- 4 files changed, 70 insertions(+), 16 deletions(-) create mode 100644 helm/templates/vector.yaml diff --git a/helm/templates/clustervectorpipeline.yaml b/helm/templates/clustervectorpipeline.yaml index f8fd5dfa..f4ef2b8d 100644 --- a/helm/templates/clustervectorpipeline.yaml +++ b/helm/templates/clustervectorpipeline.yaml @@ -1,8 +1,17 @@ -{{- if .Values.ClusterVectorPipeline -}} +{{- range $pipeline := .Values.ClusterVectorPipeline }} apiVersion: observability.kaasops.io/v1alpha1 kind: ClusterVectorPipeline -metadata: - name: {{ .Values.ClusterVectorPipeline.name }} -spec: -{{- toYaml .Values.ClusterVectorPipeline.spec | nindent 2 }} - {{- end }} \ No newline at end of file +metadata: + name: {{ $pipeline.name }} +spec: + sources: + {{ toYaml $pipeline.sources | nindent 6 }} + transforms: + {{ toYaml $pipeline.sources | nindent 6 }} + sinks: + {{ toYaml $pipeline.sources | nindent 6 }} +--- +{{- end }} + + + diff --git a/helm/templates/vector.yaml b/helm/templates/vector.yaml new file mode 100644 index 00000000..95288e21 --- /dev/null +++ b/helm/templates/vector.yaml @@ -0,0 +1,15 @@ +{{- if .Values.vector.create -}} +apiVersion: observability.kaasops.io/v1alpha1 +kind: Vector +metadata: + name: {{ .Values.vector.name }} + namespace: vector-operator-system +spec: + agent: + service: true + image: "timberio/vector:0.24.0-distroless-libc" + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/helm/templates/vectorpipeline.yaml b/helm/templates/vectorpipeline.yaml index 55142a87..c4bc1439 100644 --- a/helm/templates/vectorpipeline.yaml +++ b/helm/templates/vectorpipeline.yaml @@ -1,8 +1,15 @@ -{{- if .Values.VectorPipeline -}} +{{- range $pipeline := .Values.VectorPipeline }} apiVersion: observability.kaasops.io/v1alpha1 kind: VectorPipeline metadata: - name: {{ .Values.VectorPipeline.name }} -spec: -{{- toYaml .Values.VectorPipeline.spec | nindent 2 }} - {{- end }} \ No newline at end of file + name: {{ $pipeline.name }} + namespace: {{ $pipeline.nanamespace }} +spec: + sources: + {{ toYaml $pipeline.sources | nindent 6 }} + transforms: + {{ toYaml $pipeline.sources | nindent 6 }} + sinks: + {{ toYaml $pipeline.sources | nindent 6 }} +--- +{{- end }} diff --git a/helm/values.yaml b/helm/values.yaml index f2127b2e..dc307874 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -25,15 +25,38 @@ resources: rbac: create: true +vector: + create: true + name: "vector-agent" + tolerations: [] + + serviceAccount: create: false name: "vector-operator" annotations: {} -ClusterVectorPipeline: - name: test1 - spec: {} +ClusterVectorPipeline: + - name: "" + sources: {} + transforms: {} + sinks: {} + - name: "" + sources: {} + transforms: {} + sinks: {} VectorPipeline: - name: test12 - spec: {} + - name: "" + namespace: "" + sources: {} + transforms: {} + sinks: {} + - name: "" + namespace: "" + sources: {} + transforms: {} + sinks: {} + + + From 4b7af593744016d1492391dec5c6f971f5f9c684 Mon Sep 17 00:00:00 2001 From: Ilya Batyukov Date: Wed, 16 Nov 2022 15:18:34 +0300 Subject: [PATCH 095/316] fix ClusterVectorPipeline --- helm/templates/clustervectorpipeline.yaml | 4 ++-- helm/templates/vectorpipeline.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/helm/templates/clustervectorpipeline.yaml b/helm/templates/clustervectorpipeline.yaml index f4ef2b8d..9cb635b8 100644 --- a/helm/templates/clustervectorpipeline.yaml +++ b/helm/templates/clustervectorpipeline.yaml @@ -7,9 +7,9 @@ spec: sources: {{ toYaml $pipeline.sources | nindent 6 }} transforms: - {{ toYaml $pipeline.sources | nindent 6 }} + {{ toYaml $pipeline.transforms | nindent 6 }} sinks: - {{ toYaml $pipeline.sources | nindent 6 }} + {{ toYaml $pipeline.transforms | nindent 6 }} --- {{- end }} diff --git a/helm/templates/vectorpipeline.yaml b/helm/templates/vectorpipeline.yaml index c4bc1439..4496441d 100644 --- a/helm/templates/vectorpipeline.yaml +++ b/helm/templates/vectorpipeline.yaml @@ -8,8 +8,8 @@ spec: sources: {{ toYaml $pipeline.sources | nindent 6 }} transforms: - {{ toYaml $pipeline.sources | nindent 6 }} + {{ toYaml $pipeline.transforms | nindent 6 }} sinks: - {{ toYaml $pipeline.sources | nindent 6 }} + {{ toYaml $pipeline.transforms | nindent 6 }} --- {{- end }} From 449623656148597e1938d3c8a76c6e94b03afc81 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Wed, 16 Nov 2022 16:36:31 +0200 Subject: [PATCH 096/316] Fix error with envs forfarding from CR Vector Signed-off-by: Zemtsov Vladimir --- .../factory/vector/vectoragent/vectoragent_daemonset.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/controllers/factory/vector/vectoragent/vectoragent_daemonset.go b/controllers/factory/vector/vectoragent/vectoragent_daemonset.go index 6351b703..deccbcaa 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_daemonset.go +++ b/controllers/factory/vector/vectoragent/vectoragent_daemonset.go @@ -156,7 +156,9 @@ func (ctrl *Controller) generateVectorAgentVolumeMounts() []corev1.VolumeMount { } func (ctrl *Controller) generateVectorAgentEnvs() []corev1.EnvVar { - envs := []corev1.EnvVar{ + envs := ctrl.Vector.Spec.Agent.Env + + envs = append(envs, []corev1.EnvVar{ { Name: "VECTOR_SELF_NODE_NAME", ValueFrom: &corev1.EnvVarSource{ @@ -192,7 +194,7 @@ func (ctrl *Controller) generateVectorAgentEnvs() []corev1.EnvVar { Name: "SYSFS_ROOT", Value: "/host/sys", }, - } + }...) return envs } From c33aa749db03abf1b388923284b02ddf8dc8bc41 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Wed, 16 Nov 2022 16:37:55 +0200 Subject: [PATCH 097/316] Update changelog Signed-off-by: Zemtsov Vladimir --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 333c3b53..7f256bb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ### Added +### v0.0.3 +- [[46](https://github.com/kaasops/vector-operator/pull/46)] **Fix**: Fix error with envs forwarding from CR Vector ### v0.0.2 - [[45](https://github.com/kaasops/vector-operator/pull/45)] **Tests**: Add tests for k8s utils && refactor k8s utils From fbe2920a2e985112e9301cbdf0ebc83aa2682c0f Mon Sep 17 00:00:00 2001 From: Ilya Batyukov Date: Thu, 17 Nov 2022 13:54:26 +0300 Subject: [PATCH 098/316] small fix --- helm/Chart.yaml | 2 +- helm/templates/_helpers.tpl | 33 ++++++++++-------------------- helm/templates/deployment.yaml | 15 +++++++------- helm/templates/rbac.yaml | 19 ++++++++--------- helm/templates/serviceaccount.yaml | 3 +-- helm/templates/vector.yaml | 1 - helm/templates/vectorpipeline.yaml | 1 - helm/values.yaml | 12 ++++++----- 8 files changed, 36 insertions(+), 50 deletions(-) diff --git a/helm/Chart.yaml b/helm/Chart.yaml index 39087628..f6bba612 100644 --- a/helm/Chart.yaml +++ b/helm/Chart.yaml @@ -1,6 +1,6 @@ apiVersion: v2 name: vector-operator -description: A Helm chart A Helm chart to install vector-operator +description: A Helm chart to install vector-operator # A chart can be either an 'application' or a 'library' chart. # diff --git a/helm/templates/_helpers.tpl b/helm/templates/_helpers.tpl index dc13106d..6e03383e 100644 --- a/helm/templates/_helpers.tpl +++ b/helm/templates/_helpers.tpl @@ -5,6 +5,15 @@ Expand the name of the chart. {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} {{- end }} +{{- define "vector-operator.namespace" -}} +{{- if .Values.namespaceOverride -}} +{{ .Values.namespaceOverride -}} +{{- else -}} +{{ .Release.Namespace }} +{{- end -}} +{{- end -}} + + {{/* Create a default fully qualified app name. We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). @@ -30,33 +39,13 @@ Create chart name and version as used by the chart label. {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} {{- end }} -{{/* -Common labels -*/}} -{{- define "vector-operator.labels" -}} -helm.sh/chart: {{ include "vector-operator.chart" . }} -{{ include "vector-operator.selectorLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end }} - -{{/* -Selector labels -*/}} -{{- define "vector-operator.selectorLabels" -}} -app.kubernetes.io/name: {{ include "vector-operator.name" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- end }} - {{/* Create the name of the service account to use */}} {{- define "vector-operator.serviceAccountName" -}} {{- if .Values.serviceAccount.create }} -{{- default (include "vector-operator.fullname" .) .Values.serviceAccount.name }} +{{- default .Values.serviceAccount.name }} {{- else }} -{{- default "default" .Values.serviceAccount.name }} +{{- default (include "vector-operator.fullname" .) }} {{- end }} {{- end }} diff --git a/helm/templates/deployment.yaml b/helm/templates/deployment.yaml index 413f679f..4637d52f 100644 --- a/helm/templates/deployment.yaml +++ b/helm/templates/deployment.yaml @@ -1,20 +1,20 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: vector-operator - namespace: vector-operator-system + name: {{ include "vector-operator.name" . }} + namespace: {{ include "vector-operator.namespace" . }} labels: - app.kubernetes.io/name: vector-operator + app.kubernetes.io/name: {{ include "vector-operator.name" . }} spec: selector: matchLabels: - app.kubernetes.io/name: vector-operator + app.kubernetes.io/name: {{ include "vector-operator.name" . }} template: metadata: labels: - app.kubernetes.io/name: vector-operator + app.kubernetes.io/name: {{ include "vector-operator.name" . }} app.kubernetes.io/managed-by: "helm" - meta.helm.sh/release-name: vector-operator + meta.helm.sh/release-name: {{ include "vector-operator.name" . }} spec: volumes: - name: kube-api-access @@ -22,8 +22,7 @@ spec: secretName: vector-operator-sa-token defaultMode: 420 automountServiceAccountToken: false - serviceAccount: vector-operator - serviceAccountName: vector-operator + serviceAccountName: {{ include "vector-operator.serviceAccountName" . }} containers: - image: {{ .Values.image.repository }}:{{ .Values.image.tag }} imagePullPolicy: {{ .Values.image.pullPolicy }} diff --git a/helm/templates/rbac.yaml b/helm/templates/rbac.yaml index 80e0dd23..220effe3 100644 --- a/helm/templates/rbac.yaml +++ b/helm/templates/rbac.yaml @@ -1,4 +1,4 @@ -{{- if .Values.rbac.create -}} +{{- if .Values.rbac.create -}} --- {{- if semverCompare ">= 1.24" .Capabilities.KubeVersion.Version }} @@ -6,9 +6,8 @@ apiVersion: v1 kind: Secret metadata: name: vector-operator-sa-token - namespace: vector-operator-system annotations: - kubernetes.io/service-account.name: vector-operator + kubernetes.io/service-account.name: {{ include "vector-operator.serviceAccountName" . }} type: kubernetes.io/service-account-token {{- end }} @@ -16,8 +15,7 @@ type: kubernetes.io/service-account-token apiVersion: v1 kind: ServiceAccount metadata: - name: vector-operator - namespace: vector-operator-system + name: {{ include "vector-operator.serviceAccountName" . }} secrets: - name: vector-operator-sa-token @@ -25,21 +23,22 @@ secrets: apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: - name: vector-operator + name: {{ include "vector-operator.name" . }} + roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: vector-operator + name: {{ include "vector-operator.name" . }} subjects: - kind: ServiceAccount - name: vector-operator - namespace: vector-operator-system + name: {{ include "vector-operator.name" . }} + namespace: {{ include "vector-operator.namespace" . }} --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - name: vector-operator + name: {{ include "vector-operator.name" . }} rules: - apiGroups: - "" diff --git a/helm/templates/serviceaccount.yaml b/helm/templates/serviceaccount.yaml index 20822842..06a9ae6b 100644 --- a/helm/templates/serviceaccount.yaml +++ b/helm/templates/serviceaccount.yaml @@ -2,8 +2,7 @@ apiVersion: v1 kind: ServiceAccount metadata: - name: {{ toYaml .Values.serviceAccount.name | nindent 4 }} - namespace: vector-operator-system + name: {{ include "vector-operator.serviceAccountName" . }} {{- if .Values.serviceAccount.annotations }} annotations: {{ toYaml .Values.serviceAccount.annotations | nindent 4 }} diff --git a/helm/templates/vector.yaml b/helm/templates/vector.yaml index 95288e21..17f708b9 100644 --- a/helm/templates/vector.yaml +++ b/helm/templates/vector.yaml @@ -3,7 +3,6 @@ apiVersion: observability.kaasops.io/v1alpha1 kind: Vector metadata: name: {{ .Values.vector.name }} - namespace: vector-operator-system spec: agent: service: true diff --git a/helm/templates/vectorpipeline.yaml b/helm/templates/vectorpipeline.yaml index 4496441d..3faad7c1 100644 --- a/helm/templates/vectorpipeline.yaml +++ b/helm/templates/vectorpipeline.yaml @@ -3,7 +3,6 @@ apiVersion: observability.kaasops.io/v1alpha1 kind: VectorPipeline metadata: name: {{ $pipeline.name }} - namespace: {{ $pipeline.nanamespace }} spec: sources: {{ toYaml $pipeline.sources | nindent 6 }} diff --git a/helm/values.yaml b/helm/values.yaml index dc307874..46c44ab5 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -1,3 +1,7 @@ +fullnameOverride: "" +nameOverride: "" +namespaceOverride: "" + image: repository: kaasops/vector-operator tag: "latest" @@ -17,7 +21,7 @@ image: resources: limits: cpu: "2" - memory: 1Gi + memory: 2Gi requests: cpu: 100m memory: 50Mi @@ -27,13 +31,13 @@ rbac: vector: create: true - name: "vector-agent" + name: "" tolerations: [] serviceAccount: create: false - name: "vector-operator" + name: "" annotations: {} ClusterVectorPipeline: @@ -48,12 +52,10 @@ ClusterVectorPipeline: VectorPipeline: - name: "" - namespace: "" sources: {} transforms: {} sinks: {} - name: "" - namespace: "" sources: {} transforms: {} sinks: {} From 1b79827c1388f876b05a45def93110d5e59f375d Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Thu, 17 Nov 2022 16:37:59 +0200 Subject: [PATCH 099/316] Update helm Signed-off-by: Zemtsov Vladimir --- helm/Chart.yaml | 6 +- ...ity.kaasops.io_clustervectorpipelines.yaml | 73 +++++++++ ...ervability.kaasops.io_vectorpipelines.yaml | 72 +++++++++ .../observability.kaasops.io_vectors.yaml} | 149 +----------------- helm/templates/_helpers.tpl | 76 +++++---- .../templates/{rbac.yaml => clusterrole.yaml} | 52 ++---- helm/templates/clusterrolebinding.yaml | 25 +++ helm/templates/clustervectorpipeline.yaml | 3 +- helm/templates/deployment.yaml | 41 +++-- helm/templates/monitor.yaml | 18 --- helm/templates/serviceaccount.yaml | 27 +++- helm/templates/vector.yaml | 14 +- helm/values.yaml | 103 ++++++------ 13 files changed, 348 insertions(+), 311 deletions(-) create mode 100644 helm/crds/observability.kaasops.io_clustervectorpipelines.yaml create mode 100644 helm/crds/observability.kaasops.io_vectorpipelines.yaml rename helm/{templates/crds.yaml => crds/observability.kaasops.io_vectors.yaml} (94%) rename helm/templates/{rbac.yaml => clusterrole.yaml} (56%) create mode 100644 helm/templates/clusterrolebinding.yaml delete mode 100644 helm/templates/monitor.yaml diff --git a/helm/Chart.yaml b/helm/Chart.yaml index f6bba612..e664fec8 100644 --- a/helm/Chart.yaml +++ b/helm/Chart.yaml @@ -1,6 +1,6 @@ apiVersion: v2 name: vector-operator -description: A Helm chart to install vector-operator +description: A Helm chart to install Vector Operator # A chart can be either an 'application' or a 'library' chart. # @@ -21,8 +21,8 @@ version: 0.0.1 # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "0.0.1" +appVersion: "v0.0.3" home: https://github.com/kaasops/vector-operator sources: - - https://github.com/kaasops/vector-operator \ No newline at end of file + - https://github.com/kaasops/vector-operator diff --git a/helm/crds/observability.kaasops.io_clustervectorpipelines.yaml b/helm/crds/observability.kaasops.io_clustervectorpipelines.yaml new file mode 100644 index 00000000..715b8145 --- /dev/null +++ b/helm/crds/observability.kaasops.io_clustervectorpipelines.yaml @@ -0,0 +1,73 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.2 + creationTimestamp: null + name: clustervectorpipelines.observability.kaasops.io +spec: + group: observability.kaasops.io + names: + kind: ClusterVectorPipeline + listKind: ClusterVectorPipelineList + plural: clustervectorpipelines + shortNames: + - cvp + singular: clustervectorpipeline + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.configCheckResult + name: Valid + type: boolean + name: v1alpha1 + schema: + openAPIV3Schema: + description: ClusterVectorPipeline is the Schema for the clustervectorpipelines + API + 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: + type: object + spec: + description: VectorPipelineSpec defines the desired state of VectorPipeline + properties: + sinks: + type: object + x-kubernetes-preserve-unknown-fields: true + sources: + type: object + x-kubernetes-preserve-unknown-fields: true + transforms: + type: object + x-kubernetes-preserve-unknown-fields: true + type: object + status: + description: VectorPipelineStatus defines the observed state of VectorPipeline + properties: + LastAppliedPipelineHash: + format: int32 + type: integer + configCheckResult: + type: boolean + reason: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/helm/crds/observability.kaasops.io_vectorpipelines.yaml b/helm/crds/observability.kaasops.io_vectorpipelines.yaml new file mode 100644 index 00000000..e40dd908 --- /dev/null +++ b/helm/crds/observability.kaasops.io_vectorpipelines.yaml @@ -0,0 +1,72 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.2 + creationTimestamp: null + name: vectorpipelines.observability.kaasops.io +spec: + group: observability.kaasops.io + names: + kind: VectorPipeline + listKind: VectorPipelineList + plural: vectorpipelines + shortNames: + - vp + singular: vectorpipeline + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.configCheckResult + name: Valid + type: boolean + name: v1alpha1 + schema: + openAPIV3Schema: + description: VectorPipeline is the Schema for the vectorpipelines API + 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: + type: object + spec: + description: VectorPipelineSpec defines the desired state of VectorPipeline + properties: + sinks: + type: object + x-kubernetes-preserve-unknown-fields: true + sources: + type: object + x-kubernetes-preserve-unknown-fields: true + transforms: + type: object + x-kubernetes-preserve-unknown-fields: true + type: object + status: + description: VectorPipelineStatus defines the observed state of VectorPipeline + properties: + LastAppliedPipelineHash: + format: int32 + type: integer + configCheckResult: + type: boolean + reason: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/helm/templates/crds.yaml b/helm/crds/observability.kaasops.io_vectors.yaml similarity index 94% rename from helm/templates/crds.yaml rename to helm/crds/observability.kaasops.io_vectors.yaml index 2f16cfeb..1bc130b3 100644 --- a/helm/templates/crds.yaml +++ b/helm/crds/observability.kaasops.io_vectors.yaml @@ -1,153 +1,6 @@ --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.9.2 - creationTimestamp: null - name: clustervectorpipelines.observability.kaasops.io -spec: - group: observability.kaasops.io - names: - kind: ClusterVectorPipeline - listKind: ClusterVectorPipelineList - plural: clustervectorpipelines - shortNames: - - cvp - singular: clustervectorpipeline - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .status.configCheckResult - name: Valid - type: boolean - name: v1alpha1 - schema: - openAPIV3Schema: - description: ClusterVectorPipeline is the Schema for the clustervectorpipelines - API - 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: - type: object - spec: - description: VectorPipelineSpec defines the desired state of VectorPipeline - properties: - sinks: - type: object - x-kubernetes-preserve-unknown-fields: true - sources: - type: object - x-kubernetes-preserve-unknown-fields: true - transforms: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - status: - description: VectorPipelineStatus defines the observed state of VectorPipeline - properties: - LastAppliedPipelineHash: - format: int32 - type: integer - configCheckResult: - type: boolean - reason: - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.9.2 - creationTimestamp: null - name: vectorpipelines.observability.kaasops.io -spec: - group: observability.kaasops.io - names: - kind: VectorPipeline - listKind: VectorPipelineList - plural: vectorpipelines - shortNames: - - vp - singular: vectorpipeline - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .status.configCheckResult - name: Valid - type: boolean - name: v1alpha1 - schema: - openAPIV3Schema: - description: VectorPipeline is the Schema for the vectorpipelines API - 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: - type: object - spec: - description: VectorPipelineSpec defines the desired state of VectorPipeline - properties: - sinks: - type: object - x-kubernetes-preserve-unknown-fields: true - sources: - type: object - x-kubernetes-preserve-unknown-fields: true - transforms: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - status: - description: VectorPipelineStatus defines the observed state of VectorPipeline - properties: - LastAppliedPipelineHash: - format: int32 - type: integer - configCheckResult: - type: boolean - reason: - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition metadata: annotations: controller-gen.kubebuilder.io/version: v0.9.2 @@ -1503,4 +1356,4 @@ spec: served: true storage: true subresources: - status: {} + status: {} diff --git a/helm/templates/_helpers.tpl b/helm/templates/_helpers.tpl index 6e03383e..c1589807 100644 --- a/helm/templates/_helpers.tpl +++ b/helm/templates/_helpers.tpl @@ -1,51 +1,63 @@ +{{/* vim: set filetype=mustache: */}} {{/* Expand the name of the chart. */}} -{{- define "vector-operator.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{- define "vector-operator.namespace" -}} -{{- if .Values.namespaceOverride -}} -{{ .Values.namespaceOverride -}} -{{- else -}} -{{ .Release.Namespace }} -{{- end -}} +{{- define "chart.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} {{- end -}} - {{/* Create a default fully qualified app name. We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). If release name contains chart name it will be used as a full name. */}} -{{- define "vector-operator.fullname" -}} -{{- if .Values.fullnameOverride }} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- $name := default .Chart.Name .Values.nameOverride }} -{{- if contains $name .Release.Name }} -{{- .Release.Name | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} -{{- end }} -{{- end }} -{{- end }} +{{- define "chart.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} {{/* Create chart name and version as used by the chart label. */}} -{{- define "vector-operator.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- define "chart.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Common labels +*/}} +{{- define "chart.labels" -}} +helm.sh/chart: {{ include "chart.chart" . }} +{{ include "chart.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} {{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end -}} + +{{/* +Selector labels +*/}} +{{- define "chart.selectorLabels" -}} +app.kubernetes.io/name: {{ include "chart.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} {{/* Create the name of the service account to use */}} -{{- define "vector-operator.serviceAccountName" -}} -{{- if .Values.serviceAccount.create }} -{{- default .Values.serviceAccount.name }} -{{- else }} -{{- default (include "vector-operator.fullname" .) }} -{{- end }} -{{- end }} +{{- define "chart.serviceAccountName" -}} +{{- if .Values.serviceAccount.create -}} + {{ default (include "chart.fullname" .) .Values.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.serviceAccount.name }} +{{- end -}} +{{- end -}} \ No newline at end of file diff --git a/helm/templates/rbac.yaml b/helm/templates/clusterrole.yaml similarity index 56% rename from helm/templates/rbac.yaml rename to helm/templates/clusterrole.yaml index 220effe3..fdf9170f 100644 --- a/helm/templates/rbac.yaml +++ b/helm/templates/clusterrole.yaml @@ -1,44 +1,18 @@ {{- if .Values.rbac.create -}} - ---- -{{- if semverCompare ">= 1.24" .Capabilities.KubeVersion.Version }} -apiVersion: v1 -kind: Secret -metadata: - name: vector-operator-sa-token - annotations: - kubernetes.io/service-account.name: {{ include "vector-operator.serviceAccountName" . }} -type: kubernetes.io/service-account-token -{{- end }} - ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include "vector-operator.serviceAccountName" . }} -secrets: - - name: vector-operator-sa-token - ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: {{ include "vector-operator.name" . }} - -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: {{ include "vector-operator.name" . }} -subjects: -- kind: ServiceAccount - name: {{ include "vector-operator.name" . }} - namespace: {{ include "vector-operator.namespace" . }} - ---- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - name: {{ include "vector-operator.name" . }} + name: {{ template "chart.fullname" . }}-clusterrole + namespace: {{ .Release.Namespace }} + labels: + {{- include "chart.labels" . | nindent 4 }} +{{- with .Values.rbac.extraLabels }} +{{ toYaml . | indent 4}} +{{- end }} + {{- with .Values.rbac.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} rules: - apiGroups: - "" @@ -120,5 +94,5 @@ rules: verbs: - get - patch - - update -{{- end }} \ No newline at end of file + - update +{{- end -}} diff --git a/helm/templates/clusterrolebinding.yaml b/helm/templates/clusterrolebinding.yaml new file mode 100644 index 00000000..25a9e074 --- /dev/null +++ b/helm/templates/clusterrolebinding.yaml @@ -0,0 +1,25 @@ + +{{- if .Values.rbac.create -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ template "chart.fullname" . }}-clusterrolebinding + namespace: {{ .Release.Namespace }} + labels: + {{- include "chart.labels" . | nindent 4 }} +{{- with .Values.rbac.extraLabels }} +{{ toYaml . | indent 4}} +{{- end }} + {{- with .Values.rbac.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "chart.fullname" . }}-clusterrole +subjects: + - kind: ServiceAccount + name: {{ template "chart.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +{{- end -}} diff --git a/helm/templates/clustervectorpipeline.yaml b/helm/templates/clustervectorpipeline.yaml index 9cb635b8..ceb9ff85 100644 --- a/helm/templates/clustervectorpipeline.yaml +++ b/helm/templates/clustervectorpipeline.yaml @@ -2,7 +2,8 @@ apiVersion: observability.kaasops.io/v1alpha1 kind: ClusterVectorPipeline metadata: - name: {{ $pipeline.name }} + name: {{ $pipeline.name }} + namespace: {{ .Release.Namespace }} spec: sources: {{ toYaml $pipeline.sources | nindent 6 }} diff --git a/helm/templates/deployment.yaml b/helm/templates/deployment.yaml index 4637d52f..c76c7587 100644 --- a/helm/templates/deployment.yaml +++ b/helm/templates/deployment.yaml @@ -1,30 +1,47 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: {{ include "vector-operator.name" . }} - namespace: {{ include "vector-operator.namespace" . }} + name: {{ include "chart.fullname" . }} + namespace: {{ .Release.Namespace }} labels: - app.kubernetes.io/name: {{ include "vector-operator.name" . }} + {{- include "chart.labels" . | nindent 4 }} +{{- with .Values.extraLabels }} +{{ toYaml . | indent 4 }} +{{- end }} + {{- with .Values.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} spec: + replicas: {{ .Values.replicaCount }} + {{- with .Values.strategy }} + strategy: + {{- toYaml . | nindent 4 }} + {{- end }} selector: matchLabels: - app.kubernetes.io/name: {{ include "vector-operator.name" . }} + {{- include "chart.selectorLabels" . | nindent 6 }} template: metadata: labels: - app.kubernetes.io/name: {{ include "vector-operator.name" . }} - app.kubernetes.io/managed-by: "helm" - meta.helm.sh/release-name: {{ include "vector-operator.name" . }} + {{- include "chart.selectorLabels" . | nindent 8 }} spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} volumes: - name: kube-api-access secret: - secretName: vector-operator-sa-token + secretName: {{ include "chart.serviceAccountName" . }} defaultMode: 420 - automountServiceAccountToken: false - serviceAccountName: {{ include "vector-operator.serviceAccountName" . }} + serviceAccountName: {{ include "chart.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} containers: - - image: {{ .Values.image.repository }}:{{ .Values.image.tag }} + - image: {{ .Values.image.repository }}:{{ default .Chart.AppVersion .Values.image.tag }} + securityContext: + {{- toYaml .Values.securityContext | nindent 10 }} imagePullPolicy: {{ .Values.image.pullPolicy }} name: vector-operator volumeMounts: @@ -33,7 +50,5 @@ spec: mountPath: /var/run/secrets/kubernetes.io/serviceaccount resources: {{ toYaml .Values.resources | indent 12 }} - securityContext: -{{- toYaml .Values.image.securityContext | nindent 10 }} \ No newline at end of file diff --git a/helm/templates/monitor.yaml b/helm/templates/monitor.yaml deleted file mode 100644 index 0b3952c3..00000000 --- a/helm/templates/monitor.yaml +++ /dev/null @@ -1,18 +0,0 @@ -apiVersion: monitoring.coreos.com/v1 -kind: ServiceMonitor -metadata: - labels: - control-plane: controller-manager - name: controller-manager-metrics-monitor - namespace: vector-operator-system -spec: - endpoints: - - path: /metrics - port: https - scheme: https - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token - tlsConfig: - insecureSkipVerify: true - selector: - matchLabels: - control-plane: controller-manager \ No newline at end of file diff --git a/helm/templates/serviceaccount.yaml b/helm/templates/serviceaccount.yaml index 06a9ae6b..7bdd8504 100644 --- a/helm/templates/serviceaccount.yaml +++ b/helm/templates/serviceaccount.yaml @@ -1,12 +1,25 @@ + {{- if .Values.serviceAccount.create -}} +{{- if semverCompare ">= 1.24" .Capabilities.KubeVersion.Version }} apiVersion: v1 -kind: ServiceAccount +kind: Secret metadata: - name: {{ include "vector-operator.serviceAccountName" . }} -{{- if .Values.serviceAccount.annotations }} + name: {{ include "chart.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} annotations: -{{ toYaml .Values.serviceAccount.annotations | nindent 4 }} + kubernetes.io/service-account.name: {{ include "chart.serviceAccountName" . }} +type: kubernetes.io/service-account-token {{- end }} -secrets: - - name: vector-operator-sa-token -{{- end }} \ No newline at end of file +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "chart.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "chart.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end -}} \ No newline at end of file diff --git a/helm/templates/vector.yaml b/helm/templates/vector.yaml index 17f708b9..10b5272b 100644 --- a/helm/templates/vector.yaml +++ b/helm/templates/vector.yaml @@ -3,12 +3,16 @@ apiVersion: observability.kaasops.io/v1alpha1 kind: Vector metadata: name: {{ .Values.vector.name }} + namespace: {{ .Release.Namespace }} spec: agent: - service: true - image: "timberio/vector:0.24.0-distroless-libc" - {{- with .Values.tolerations }} + image: {{ .Values.vector.agent.image }} + {{- with .Values.tolerations }} tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} + {{- toYaml . | nindent 6 }} + {{- end }} + {{- with .Values.env }} + env: + {{- toYaml . | nindent 6 }} + {{- end }} {{- end }} \ No newline at end of file diff --git a/helm/values.yaml b/helm/values.yaml index 46c44ab5..369cedb7 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -2,63 +2,76 @@ fullnameOverride: "" nameOverride: "" namespaceOverride: "" +replicaCount: 1 + image: repository: kaasops/vector-operator - tag: "latest" + tag: "" # rewrites Chart.AppVersion pullPolicy: IfNotPresent - - securityContext: - allowPrivilegeEscalation: false - runAsGroup: 1000 - runAsNonRoot: true - readOnlyRootFilesystem: true - seccompProfile: - type: RuntimeDefault - capabilities: - drop: - - ALL -resources: +# -- enables CRD creation and management. +# -- with this option, if you remove this chart, all crd resources will be deleted with it. +createCRD: true + +strategy: {} +# rollingUpdate: +# maxSurge: 25% +# maxUnavailable: 25% +# type: RollingUpdate + +imagePullSecrets: [] + +securityContext: + allowPrivilegeEscalation: false + runAsGroup: 1000 + runAsNonRoot: true + readOnlyRootFilesystem: true + seccompProfile: + type: RuntimeDefault + capabilities: + drop: + - ALL +resources: limits: - cpu: "2" - memory: 2Gi + cpu: "1" + memory: 1Gi requests: cpu: 100m memory: 50Mi -rbac: - create: true - -vector: - create: true - name: "" - tolerations: [] - - serviceAccount: - create: false - name: "" + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: + +rbac: + create: true annotations: {} + extraLabels: {} + +ClusterVectorPipeline: {} +# - name: "" +# sources: {} +# transforms: {} +# sinks: {} +# - name: "" +# sources: {} +# transforms: {} +# sinks: {} -ClusterVectorPipeline: - - name: "" - sources: {} - transforms: {} - sinks: {} - - name: "" - sources: {} - transforms: {} - sinks: {} +# extra Labels for Pods, Deployment +extraLabels: {} -VectorPipeline: - - name: "" - sources: {} - transforms: {} - sinks: {} - - name: "" - sources: {} - transforms: {} - sinks: {} - +# Annotations to be added to the deployment +annotations: {} +vector: + create: true + name: "vector" + agent: + image: timberio/vector:0.24.0-distroless-libc From 7b685fade6cd538e6215e02b745850c9d88ad11c Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Thu, 17 Nov 2022 16:39:13 +0200 Subject: [PATCH 100/316] Update changelog Signed-off-by: Zemtsov Vladimir --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f256bb1..2e085a10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ### Added +- [[47](https://github.com/kaasops/vector-operator/pull/47)] **Features**: Init Helm chart ### v0.0.3 - [[46](https://github.com/kaasops/vector-operator/pull/46)] **Fix**: Fix error with envs forwarding from CR Vector From a78f334c9f532d4287602b851632e680041b650a Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Fri, 18 Nov 2022 10:33:59 +0200 Subject: [PATCH 101/316] Update helm Signed-off-by: Zemtsov Vladimir --- Makefile | 1 + helm/Makefile | 17 ++ helm/{ => charts/vector-operator}/.helmignore | 0 helm/{ => charts/vector-operator}/Chart.yaml | 0 ...ity.kaasops.io_clustervectorpipelines.yaml | 0 ...ervability.kaasops.io_vectorpipelines.yaml | 0 .../observability.kaasops.io_vectors.yaml | 0 .../vector-operator}/templates/_helpers.tpl | 0 .../templates/clusterrole.yaml | 0 .../templates/clusterrolebinding.yaml | 0 .../templates/clustervectorpipeline.yaml | 0 .../templates/deployment.yaml | 0 .../templates/serviceaccount.yaml | 0 helm/{ => charts/vector-operator}/values.yaml | 0 helm/index.yaml | 17 ++ helm/packages/vector-operator-0.0.1.tgz | Bin 0 -> 12171 bytes helm/templates/vector.yaml | 18 -- helm/templates/vectorpipeline.yaml | 14 -- index.yaml | 3 + vector.yaml | 26 +++ vectorpipelines.yaml | 194 ++++++++++++++++++ 21 files changed, 258 insertions(+), 32 deletions(-) create mode 100644 helm/Makefile rename helm/{ => charts/vector-operator}/.helmignore (100%) rename helm/{ => charts/vector-operator}/Chart.yaml (100%) rename helm/{ => charts/vector-operator}/crds/observability.kaasops.io_clustervectorpipelines.yaml (100%) rename helm/{ => charts/vector-operator}/crds/observability.kaasops.io_vectorpipelines.yaml (100%) rename helm/{ => charts/vector-operator}/crds/observability.kaasops.io_vectors.yaml (100%) rename helm/{ => charts/vector-operator}/templates/_helpers.tpl (100%) rename helm/{ => charts/vector-operator}/templates/clusterrole.yaml (100%) rename helm/{ => charts/vector-operator}/templates/clusterrolebinding.yaml (100%) rename helm/{ => charts/vector-operator}/templates/clustervectorpipeline.yaml (100%) rename helm/{ => charts/vector-operator}/templates/deployment.yaml (100%) rename helm/{ => charts/vector-operator}/templates/serviceaccount.yaml (100%) rename helm/{ => charts/vector-operator}/values.yaml (100%) create mode 100644 helm/index.yaml create mode 100644 helm/packages/vector-operator-0.0.1.tgz delete mode 100644 helm/templates/vector.yaml delete mode 100644 helm/templates/vectorpipeline.yaml create mode 100644 index.yaml create mode 100644 vector.yaml create mode 100644 vectorpipelines.yaml diff --git a/Makefile b/Makefile index c8419f6d..92b3a5b7 100644 --- a/Makefile +++ b/Makefile @@ -233,3 +233,4 @@ catalog-build: opm ## Build a catalog image. .PHONY: catalog-push catalog-push: ## Push a catalog image. $(MAKE) docker-push IMG=$(CATALOG_IMG) + \ No newline at end of file diff --git a/helm/Makefile b/helm/Makefile new file mode 100644 index 00000000..73087366 --- /dev/null +++ b/helm/Makefile @@ -0,0 +1,17 @@ +URL=https://kaasops.github.io/helm-charts/ +HELM_IMAGE = alpine/helm:3.9.1 +HELM_DOCS_IMAGE = jnorwood/helm-docs:v1.11.0 +KNOWN_TARGETS=helm + +# Run linter for helm chart +lint: + helm lint charts/vector-operator + +# Package chart into zip file +package: + helm package charts/* -d packages + +# Create index file (use only for initial setup) +index: + helm repo index --url ${URL} / + mv index.yaml \ No newline at end of file diff --git a/helm/.helmignore b/helm/charts/vector-operator/.helmignore similarity index 100% rename from helm/.helmignore rename to helm/charts/vector-operator/.helmignore diff --git a/helm/Chart.yaml b/helm/charts/vector-operator/Chart.yaml similarity index 100% rename from helm/Chart.yaml rename to helm/charts/vector-operator/Chart.yaml diff --git a/helm/crds/observability.kaasops.io_clustervectorpipelines.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectorpipelines.yaml similarity index 100% rename from helm/crds/observability.kaasops.io_clustervectorpipelines.yaml rename to helm/charts/vector-operator/crds/observability.kaasops.io_clustervectorpipelines.yaml diff --git a/helm/crds/observability.kaasops.io_vectorpipelines.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_vectorpipelines.yaml similarity index 100% rename from helm/crds/observability.kaasops.io_vectorpipelines.yaml rename to helm/charts/vector-operator/crds/observability.kaasops.io_vectorpipelines.yaml diff --git a/helm/crds/observability.kaasops.io_vectors.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml similarity index 100% rename from helm/crds/observability.kaasops.io_vectors.yaml rename to helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml diff --git a/helm/templates/_helpers.tpl b/helm/charts/vector-operator/templates/_helpers.tpl similarity index 100% rename from helm/templates/_helpers.tpl rename to helm/charts/vector-operator/templates/_helpers.tpl diff --git a/helm/templates/clusterrole.yaml b/helm/charts/vector-operator/templates/clusterrole.yaml similarity index 100% rename from helm/templates/clusterrole.yaml rename to helm/charts/vector-operator/templates/clusterrole.yaml diff --git a/helm/templates/clusterrolebinding.yaml b/helm/charts/vector-operator/templates/clusterrolebinding.yaml similarity index 100% rename from helm/templates/clusterrolebinding.yaml rename to helm/charts/vector-operator/templates/clusterrolebinding.yaml diff --git a/helm/templates/clustervectorpipeline.yaml b/helm/charts/vector-operator/templates/clustervectorpipeline.yaml similarity index 100% rename from helm/templates/clustervectorpipeline.yaml rename to helm/charts/vector-operator/templates/clustervectorpipeline.yaml diff --git a/helm/templates/deployment.yaml b/helm/charts/vector-operator/templates/deployment.yaml similarity index 100% rename from helm/templates/deployment.yaml rename to helm/charts/vector-operator/templates/deployment.yaml diff --git a/helm/templates/serviceaccount.yaml b/helm/charts/vector-operator/templates/serviceaccount.yaml similarity index 100% rename from helm/templates/serviceaccount.yaml rename to helm/charts/vector-operator/templates/serviceaccount.yaml diff --git a/helm/values.yaml b/helm/charts/vector-operator/values.yaml similarity index 100% rename from helm/values.yaml rename to helm/charts/vector-operator/values.yaml diff --git a/helm/index.yaml b/helm/index.yaml new file mode 100644 index 00000000..057356b9 --- /dev/null +++ b/helm/index.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +entries: + vector-operator: + - apiVersion: v2 + appVersion: v0.0.3 + created: "2022-11-18T09:59:54.491957+02:00" + description: A Helm chart to install Vector Operator + digest: 89ea474465728f855baea8d8e2fdfa2c70050ae7443eb84a555cf8cbd233fc67 + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/helm-charts/packages/vector-operator-0.0.1.tgz + version: 0.0.1 +generated: "2022-11-18T09:59:54.49135+02:00" diff --git a/helm/packages/vector-operator-0.0.1.tgz b/helm/packages/vector-operator-0.0.1.tgz new file mode 100644 index 0000000000000000000000000000000000000000..04ca1c89c42c8a9f5ea6441e40ca65e6e5354f3b GIT binary patch literal 12171 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 literal 0 HcmV?d00001 diff --git a/helm/templates/vector.yaml b/helm/templates/vector.yaml deleted file mode 100644 index 10b5272b..00000000 --- a/helm/templates/vector.yaml +++ /dev/null @@ -1,18 +0,0 @@ -{{- if .Values.vector.create -}} -apiVersion: observability.kaasops.io/v1alpha1 -kind: Vector -metadata: - name: {{ .Values.vector.name }} - namespace: {{ .Release.Namespace }} -spec: - agent: - image: {{ .Values.vector.agent.image }} - {{- with .Values.tolerations }} - tolerations: - {{- toYaml . | nindent 6 }} - {{- end }} - {{- with .Values.env }} - env: - {{- toYaml . | nindent 6 }} - {{- end }} -{{- end }} \ No newline at end of file diff --git a/helm/templates/vectorpipeline.yaml b/helm/templates/vectorpipeline.yaml deleted file mode 100644 index 3faad7c1..00000000 --- a/helm/templates/vectorpipeline.yaml +++ /dev/null @@ -1,14 +0,0 @@ -{{- range $pipeline := .Values.VectorPipeline }} -apiVersion: observability.kaasops.io/v1alpha1 -kind: VectorPipeline -metadata: - name: {{ $pipeline.name }} -spec: - sources: - {{ toYaml $pipeline.sources | nindent 6 }} - transforms: - {{ toYaml $pipeline.transforms | nindent 6 }} - sinks: - {{ toYaml $pipeline.transforms | nindent 6 }} ---- -{{- end }} diff --git a/index.yaml b/index.yaml new file mode 100644 index 00000000..8e363c5c --- /dev/null +++ b/index.yaml @@ -0,0 +1,3 @@ +apiVersion: v1 +entries: {} +generated: "2022-11-18T10:05:12.719734+02:00" diff --git a/vector.yaml b/vector.yaml new file mode 100644 index 00000000..b6668829 --- /dev/null +++ b/vector.yaml @@ -0,0 +1,26 @@ +apiVersion: observability.kaasops.io/v1alpha1 +kind: Vector +metadata: + name: vector-sample + namespace: vector +spec: + agent: + image: timberio/vector:0.24.0-distroless-libc + service: true + env: + - name: "LUX-LOG-ELASTIC-HOST" + value: "lux-log.xbet.lan" + - name: "LUX-LOG-ELASTIC-USER" + value: "lux-write" + - name: "LUX-LOG-ELASTIC-PASSWORD" + value: "bieteuH3keileR}" + tolerations: + - effect: NoSchedule + key: node-role.kubernetes.io/ingress + operator: Exists + - effect: NoSchedule + key: node-role.kubernetes.io/master + operator: Exists + - effect: NoSchedule + key: node-role.kubernetes.io/control-plane + operator: Exists diff --git a/vectorpipelines.yaml b/vectorpipelines.yaml new file mode 100644 index 00000000..f1119374 --- /dev/null +++ b/vectorpipelines.yaml @@ -0,0 +1,194 @@ +# ### Fluentd +# apiVersion: observability.kaasops.io/v1alpha1 +# kind: VectorPipeline +# metadata: +# name: fluentd +# namespace: vector-operator-system +# spec: +# sources: +# fluentd: +# type: "kubernetes_logs" +# extra_label_selector: "app.kubernetes.io/component=fluentd" +# extra_namespace_label_selector: "kubernetes.io/metadata.name=logging" +# transforms: +# fluentd-transform: +# type: "remap" +# inputs: +# - fluentd +# source: | +# .@timestamp = del(.timestamp) + +# .cluster = "lux" +# sinks: +# elastic: +# type: "elasticsearch" +# inputs: +# - fluentd-transform +# endpoint: "https://ams-kube-log.xbet.lan:9260" +# bulk: +# index: "vector-fluentd-%Y-%m-%d" +# auth: +# user: for_services +# password: oodoo8EiL]o]sh9aQuie +# strategy: basic +# tls: +# verify_certificate: false +# --- +### Ingress +apiVersion: observability.kaasops.io/v1alpha1 +kind: VectorPipeline +metadata: + name: ingress + namespace: ingress-nginx +spec: + sources: + ingress: + type: "kubernetes_logs" + extra_label_selector: "app.kubernetes.io/name=ingress-nginx" + extra_namespace_label_selector: "kubernetes.io/metadata.name=ingress-nginx" + transforms: + ingress-transform: + type: "remap" + inputs: + - ingress + source: | + res, errjson = parse_json(.message) + if errjson != null { + res, errregex = parse_regex(.message, r'^(?P-SQ2@9rz0%2u%Yps3t_$^Aeu(QR~i`&XwS16-*r+LrC=Imc5NK-Vl45+Wu1iGJM z_KQ-O2L&WiS5JsTRxIg5zLGd%Bv7|CgUs$b8_<8;VOUQP;p)|R{)%?+Z|myIG{De` zF*yI;VVi!#Re~%nMG>{eVtY{-Wu;NBFLyc1@>C=rTxsB(M5EZ+X@r4lpUU7%&{scC z7FyeZD$C0>Wf9`(Q8Zgpwl|DP)E9AqCL^dBTB?T z)DgQ<>SgOx<(4a#NtN8=r$MW%s{APnGn3zOuz!~dN&Qh?X)k^WXjh%q{C-`k-TKXU zFtawKPq3+6a^zOGN3+c^_lv%MQ-r=Ar(1X?xqs)+DdeXwZ;)db=mJi$-qyTlr7z zPNSXtnLYA^6}>70f{VGz0LGskJi!kOd;ER3x=~gG4|zwdv5XIn95Vu8ASS)U%ym&d zTIWi1FC2YHU1yl3T=87gETZ7df(HnN1|8CiZm9G1_HSs^41#}dlEu5=+3c>;IAd|b zqBobj0Wx7S0RK~j9apcL1*^8oeo8~z1@rwJ4&DTQy8-Xeucmw*prq7qOb9TkcZkK> ziu~AWmI6Y{XTjEYD$CA4ns6zn#g*TW4Nfjezh0w;HlmE&MPskJR2SM_wvyP)@ZL*6=Lp%3qu~y!2kR^d0Irm{)ZAbZgwD9ovj4X1wm7h^Hin zt{BXxDK3;ELDLa&N*Bf*S!%mV!Mk%7<-)f(@QNXJuX{#Yqofz|n(lQjuz+Orw`g`( zyLM6Ur&Srh>SOgw#i=kkiC|+aKV2l)JQv8_@l`LaN+5Sht{yW7IKDsdPA_g?+QOH5 z*2?eG4ve3GluwL_U4;$j#{R0Q^Z|4|h)7IcVms!=p$2}w_7Yf}&Iorv$eCshXXES4 z^~=%OLw^P*+1%JFDwWW7xU*&_Dq=uJx7d2=8k(g`y~|lq;SArX%M3tTobv1#=4(2_ z*&|#iaUnijAxLtwuUlrO4Cmb0y6sLxYN*vTiKE|^s7V7--KZ0pBWD4R{(|Q8vBL44 zThQO|*1vL-eLd!czzqHB+cTL9K!->x9<}uoz?r?gy?-(WY5&yB@YgH1=qrGTPvanY z&6itzZ8o-4oXcPH*WC=K=;5TPUv$&fU|P<(deXb=-VjB_)BcS%={%*PF6@ZrPE>Ad zC)?KilYRmn7p8`l@^Q>(xkx{1^i)kKGo4_)s*aW~z zM(sIeZCG4gflzfGA7FY$EbTgsM}lMJ_c zs!W{)mfX|`P9rn4d2xq*qz~+^M;J`85(5LKTBRm~IY!Lq5A(^Wtq8F0Bj0B7FDW^-G^1>sdC|A zJ2i*BWl!X=7bg=(nNE5jwuqjxQ9)HQHe7A;yY6pYXJ?Lue`$IbZpEj1-!nS)ms~)@LL5YR zZ^pAJos-N#`I%T-bG%3m_3|4(lHEkoHZACd2g9?DY<}%xTNiU_(Ggg%UpmB=nFkI# zZcPEZY4wtmn#P3ao>~Jey}8oq&B_Hty1RgURLnLZo<@K85}scEoVMy2-%BO(U#oHN zR*Jd7ejhPzh{e_^($)W5S*F%VNaBNoQ127#fICq@xoA8#;s>&H1dZ&&WAOAe@Bq{z zAzy{WoJ^Tg)1|Mp8!jq=aOLuA(1=<;vD_fYFyt{Ym48Wf3%b>IZpw(K)LQS@&-rT^ zf<0?3y?|x$f|{l{T|G}{pk+J^4ZC51$)n<*t~77>0w`2tGLtpw?S@U$GJmI;g|bMx zms%kn#E-(ARtp-9iHUJE-dC`nU*@DOL7X9cTCpi8e4~=5@&QQ zHl_k-kU#C@2mbQx{=9;^YRrM{Z>;op`U}O4>2)G2+Vp2lZKJ>WUA}eI8p0?O(f^tE cWs7%&J;C{0@H6m#ne$*^Gwff8R!E5d0|TtIWdHyG literal 0 HcmV?d00001 diff --git a/main.go b/main.go index 84d59b15..be7ddce3 100644 --- a/main.go +++ b/main.go @@ -61,14 +61,14 @@ func main() { var enableLeaderElection bool var probeAddr string var namespace string - var loggingRef string + var watchLabel string flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.") flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") flag.BoolVar(&enableLeaderElection, "leader-elect", false, "Enable leader election for controller manager. "+ "Enabling this will ensure there is only one active controller manager.") flag.StringVar(&namespace, "watch-namespace", "", "Namespace to filter the list of watched objects") - flag.StringVar(&loggingRef, "watch-logging-name", "", "Filter the list of watched objects based on which logging they belong to by checking the app.kubernetes.io/managed-by label") + flag.StringVar(&watchLabel, "watch-name", "", "Filter the list of watched objects by checking the app.kubernetes.io/managed-by label") opts := zap.Options{ Development: true, } @@ -92,7 +92,7 @@ func main() { LeaderElection: enableLeaderElection, LeaderElectionID: "79cbe7f3.kaasops.io", } - customMgrOptions, err := setupCustomCache(&mgrOptions, namespace, loggingRef) + customMgrOptions, err := setupCustomCache(&mgrOptions, namespace, watchLabel) if err != nil { setupLog.Error(err, "unable to set up custom cache settings") @@ -147,8 +147,8 @@ func main() { } } -func setupCustomCache(mgrOptions *ctrl.Options, namespace string, loggingRef string) (*ctrl.Options, error) { - if namespace == "" && loggingRef == "" { +func setupCustomCache(mgrOptions *ctrl.Options, namespace string, watchLabel string) (*ctrl.Options, error) { + if namespace == "" && watchLabel == "" { return mgrOptions, nil } @@ -157,8 +157,8 @@ func setupCustomCache(mgrOptions *ctrl.Options, namespace string, loggingRef str if namespace != "" { namespaceSelector = fields.Set{"metadata.namespace": namespace}.AsSelector() } - if loggingRef != "" { - labelSelector = labels.Set{k8s.ManagedByLabelKey: "vector-operator", k8s.NameLabelKey: loggingRef}.AsSelector() + if watchLabel != "" { + labelSelector = labels.Set{k8s.ManagedByLabelKey: "vector-operator", k8s.NameLabelKey: watchLabel}.AsSelector() } selectorsByObject := cache.SelectorsByObject{ @@ -170,10 +170,18 @@ func setupCustomCache(mgrOptions *ctrl.Options, namespace string, loggingRef str Field: namespaceSelector, Label: labelSelector, }, + &corev1.Service{}: { + Field: namespaceSelector, + Label: labelSelector, + }, &corev1.Secret{}: { Field: namespaceSelector, Label: labelSelector, }, + &corev1.ServiceAccount{}: { + Field: namespaceSelector, + Label: labelSelector, + }, } mgrOptions.NewCache = cache.BuilderWithOptions(cache.Options{SelectorsByObject: selectorsByObject}) From 96332f1281146e4f173821c55850c20931eb4831 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Fri, 25 Nov 2022 12:08:24 +0200 Subject: [PATCH 133/316] update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b769acf..35a61b2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ### Added +### v0.0.7 +- [[61]](https://github.com/kaasops/vector-operator/pull/61) **Feature** Filter cache and disable time reconcile + ### v0.0.6 - [[60](https://github.com/kaasops/vector-operator/pull/60)] **Fix**: Fix Vector agent DaemosSet for collect journald service logs - [[60](https://github.com/kaasops/vector-operator/pull/60)] **Docs**: Add docs From c9cd7eb5fa7fa4a523f3f8e4dca65606eecb3cc0 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Fri, 25 Nov 2022 12:21:45 +0200 Subject: [PATCH 134/316] improve reconcile cluster objects --- controllers/vector_controller.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/controllers/vector_controller.go b/controllers/vector_controller.go index 9bae76ed..b1e1c00a 100644 --- a/controllers/vector_controller.go +++ b/controllers/vector_controller.go @@ -69,11 +69,13 @@ func (r *VectorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr return ctrl.Result{}, err } for _, v := range vectors { - log.Info("start Reconcile Vector") - _, err := createOrUpdateVector(ctx, r.Client, r.Clientset, v) - if err != nil { - log.Error(err, "Failed to reconciler vector") - return ctrl.Result{}, err + if v.Name == req.Name { + log.Info("start Reconcile Vector") + _, err := createOrUpdateVector(ctx, r.Client, r.Clientset, v) + if err != nil { + log.Error(err, "Failed to reconciler vector") + return ctrl.Result{}, err + } } } return ctrl.Result{}, nil From 461136068d764c27ab1bf4e24f0c149438e70f3f Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Fri, 25 Nov 2022 18:07:35 +0200 Subject: [PATCH 135/316] improve configcheck and reconcile --- .../clustervectorpipeline_controller.go | 6 +- .../factory/config/configcheck/configcheck.go | 64 +++++++-------- .../config/configcheck/configcheck_pod.go | 4 +- controllers/factory/utils/k8s/k8s.go | 8 ++ .../vector/vectoragent/vectoragent_config.go | 38 --------- .../vectoragent/vectoragent_controller.go | 25 +++--- controllers/vector_controller.go | 79 ++++++++++++++----- controllers/vectorpipeline_controller.go | 6 +- 8 files changed, 117 insertions(+), 113 deletions(-) diff --git a/controllers/clustervectorpipeline_controller.go b/controllers/clustervectorpipeline_controller.go index f8981de7..cd9e0e42 100644 --- a/controllers/clustervectorpipeline_controller.go +++ b/controllers/clustervectorpipeline_controller.go @@ -78,9 +78,9 @@ func (r *ClusterVectorPipelineReconciler) Reconcile(ctx context.Context, req ctr return ctrl.Result{}, nil } - if vectorPipelineCR == nil { + if vectorPipelineCR == nil || vectorPipelineCR.DeletionTimestamp != nil { log.Info("ClusterVectorPIpeline CR not found. Ignoring since object must be deleted") - return reconcileVectors(ctx, r.Client, r.Clientset, vectorInstances...) + return reconcileVectors(ctx, r.Client, r.Clientset, true, vectorInstances...) } // Check Pipeline hash @@ -110,7 +110,7 @@ func (r *ClusterVectorPipelineReconciler) Reconcile(ctx context.Context, req ctr } log.Info("finish Reconcile ClusterVectorPipeline") - return reconcileVectors(ctx, r.Client, r.Clientset, vectorInstances...) + return reconcileVectors(ctx, r.Client, r.Clientset, true, vectorInstances...) } func (r *ClusterVectorPipelineReconciler) findClusterVectorPipelineCustomResourceInstance(ctx context.Context, req ctrl.Request) (*vectorv1alpha1.ClusterVectorPipeline, error) { diff --git a/controllers/factory/config/configcheck/configcheck.go b/controllers/factory/config/configcheck/configcheck.go index d1f7afd6..d16873fd 100644 --- a/controllers/factory/config/configcheck/configcheck.go +++ b/controllers/factory/config/configcheck/configcheck.go @@ -23,6 +23,8 @@ import ( "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes" @@ -114,7 +116,7 @@ func (cc *ConfigCheck) checkVectorConfigCheckPod(ctx context.Context) error { return err } - err = cc.cleanup(ctx) + err = cc.cleanup(ctx, vectorConfigCheckPod) if err != nil { return err } @@ -131,7 +133,7 @@ func labelsForVectorConfigCheck() map[string]string { } func (cc *ConfigCheck) getNameVectorConfigCheck() string { - n := "configcheck-" + "-" + cc.Name + "-" + cc.Hash + n := "configcheck" + "-" + cc.Name + "-" + cc.Hash return n } @@ -146,16 +148,19 @@ func randStringRunes() string { return string(b) } -func (cc *ConfigCheck) getCheckResult(ctx context.Context, pod *corev1.Pod) error { +func (cc *ConfigCheck) getCheckResult(ctx context.Context, pod *corev1.Pod) (err error) { log := log.FromContext(ctx).WithValues("Vector ConfigCheck", pod.Name) - + log.Info("Trying to get configcheck result") for { - existing, err := k8s.GetPod(ctx, client.ObjectKeyFromObject(pod), cc.Client) + pod, err := k8s.FetchPod(ctx, pod, cc.Client) if err != nil { + if apierrors.IsNotFound(err) { + continue + } return err } - switch existing.Status.Phase { + switch pod.Status.Phase { case "Pending": log.Info("wait Validate Vector Config Result") time.Sleep(5 * time.Second) @@ -172,37 +177,32 @@ func (cc *ConfigCheck) getCheckResult(ctx context.Context, pod *corev1.Pod) erro } } -func (cc *ConfigCheck) cleanup(ctx context.Context) error { - listOpts, err := cc.gcRListOptions() - if err != nil { - return err - } - - podlist := corev1.PodList{} - err = cc.Client.List(ctx, &podlist, &listOpts) +func (cc *ConfigCheck) cleanup(ctx context.Context, pod *corev1.Pod) error { + pod, err := k8s.FetchPod(ctx, pod, cc.Client) if err != nil { + if errors.IsNotFound(err) { + return nil + } return err } - for _, pod := range podlist.Items { - if pod.Status.Phase == "Succeeded" { - for _, v := range pod.Spec.Volumes { - if v.Name == "config" { - nn := types.NamespacedName{ - Name: v.Secret.SecretName, - Namespace: pod.Namespace, - } - secret, err := k8s.GetSecret(ctx, nn, cc.Client) - if err != nil { - return err - } - if err := k8s.DeleteSecret(ctx, secret, cc.Client); err != nil { - return err - } + if pod.Status.Phase == "Succeeded" { + for _, v := range pod.Spec.Volumes { + if v.Name == "config" { + nn := types.NamespacedName{ + Name: v.Secret.SecretName, + Namespace: pod.Namespace, + } + secret, err := k8s.GetSecret(ctx, nn, cc.Client) + if err != nil { + return err + } + if err := k8s.DeleteSecret(ctx, secret, cc.Client); err != nil { + return err } } - if err := k8s.DeletePod(ctx, &pod, cc.Client); err != nil { - return err - } + } + if err := k8s.DeletePod(ctx, pod, cc.Client); err != nil { + return err } } return nil diff --git a/controllers/factory/config/configcheck/configcheck_pod.go b/controllers/factory/config/configcheck/configcheck_pod.go index 55376aca..47c74549 100644 --- a/controllers/factory/config/configcheck/configcheck_pod.go +++ b/controllers/factory/config/configcheck/configcheck_pod.go @@ -81,9 +81,7 @@ func (cc *ConfigCheck) generateVectorConfigCheckVolume() []corev1.Volume { { Name: "data", VolumeSource: corev1.VolumeSource{ - HostPath: &corev1.HostPathVolumeSource{ - Path: "/var/lib/vector", - }, + EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, { diff --git a/controllers/factory/utils/k8s/k8s.go b/controllers/factory/utils/k8s/k8s.go index 31e2666d..95cda200 100644 --- a/controllers/factory/utils/k8s/k8s.go +++ b/controllers/factory/utils/k8s/k8s.go @@ -388,6 +388,14 @@ func GetPod(ctx context.Context, namespacedName types.NamespacedName, c client.C return result, nil } +func FetchPod(ctx context.Context, pod *corev1.Pod, c client.Client) (*corev1.Pod, error) { + err := c.Get(ctx, client.ObjectKeyFromObject(pod), pod) + if err != nil { + return nil, err + } + return pod, nil +} + func DeletePod(ctx context.Context, pod *corev1.Pod, c client.Client) error { if err := c.Delete(ctx, pod); err != nil { return err diff --git a/controllers/factory/vector/vectoragent/vectoragent_config.go b/controllers/factory/vector/vectoragent/vectoragent_config.go index 5756cb2c..0db7ff43 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_config.go +++ b/controllers/factory/vector/vectoragent/vectoragent_config.go @@ -18,49 +18,11 @@ package vectoragent import ( "context" - "errors" corev1 "k8s.io/api/core/v1" - - "github.com/kaasops/vector-operator/controllers/factory/config/configcheck" - "github.com/kaasops/vector-operator/controllers/factory/utils/hash" ) func (ctrl *Controller) createVectorAgentConfig(ctx context.Context) (*corev1.Secret, error) { - cfgHash := hash.Get(ctrl.Config) - - configCheck := configcheck.New( - ctrl.Config, - ctrl.Client, - ctrl.ClientSet, - ctrl.Vector.Name, - ctrl.Vector.Namespace, - ctrl.Vector.Spec.Agent.Image, - ctrl.Vector.Spec.Agent.Env, - ) - - if ctrl.Vector.Status.LastAppliedConfigHash == nil || *ctrl.Vector.Status.LastAppliedConfigHash != cfgHash { - err := configCheck.Run(ctx) - if errors.Is(err, configcheck.ValidationError) { - if err := ctrl.SetFailedStatus(ctx, err); err != nil { - return nil, err - } - return nil, err - } - if err != nil { - return nil, err - } - - if err := ctrl.SetLastAppliedPipelineStatus(ctx, &cfgHash); err != nil { - return nil, err - } - - if err := ctrl.SetSucceesStatus(ctx); err != nil { - return nil, err - } - - } - labels := ctrl.labelsForVectorAgent() config := map[string][]byte{ "agent.json": ctrl.Config, diff --git a/controllers/factory/vector/vectoragent/vectoragent_controller.go b/controllers/factory/vector/vectoragent/vectoragent_controller.go index 1c1dfa22..19f82ef9 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_controller.go +++ b/controllers/factory/vector/vectoragent/vectoragent_controller.go @@ -25,29 +25,28 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" ) -func (ctrl *Controller) EnsureVectorAgent(ctx context.Context) error { +func (ctrl *Controller) EnsureVectorAgent(ctx context.Context, configOnly bool) error { log := log.FromContext(ctx).WithValues("vector-agent", ctrl.Vector.Name) - log.Info("start Reconcile Vector Agent") - if err := ctrl.ensureVectorAgentRBAC(ctx); err != nil { + if err := ctrl.ensureVectorAgentConfig(ctx); err != nil { return err } - - if ctrl.Vector.Spec.Agent.Service { - if err := ctrl.ensureVectorAgentService(ctx); err != nil { + if !configOnly { + if err := ctrl.ensureVectorAgentRBAC(ctx); err != nil { return err } - } - if err := ctrl.ensureVectorAgentConfig(ctx); err != nil { - return err - } + if ctrl.Vector.Spec.Agent.Service { + if err := ctrl.ensureVectorAgentService(ctx); err != nil { + return err + } + } - if err := ctrl.ensureVectorAgentDaemonSet(ctx); err != nil { - return err + if err := ctrl.ensureVectorAgentDaemonSet(ctx); err != nil { + return err + } } - return nil } diff --git a/controllers/vector_controller.go b/controllers/vector_controller.go index b1e1c00a..9cbff40a 100644 --- a/controllers/vector_controller.go +++ b/controllers/vector_controller.go @@ -18,20 +18,27 @@ package controllers import ( "context" + "errors" "github.com/kaasops/vector-operator/controllers/factory/config" + "github.com/kaasops/vector-operator/controllers/factory/config/configcheck" "github.com/kaasops/vector-operator/controllers/factory/pipeline" + "github.com/kaasops/vector-operator/controllers/factory/utils/hash" "github.com/kaasops/vector-operator/controllers/factory/vector/vectoragent" + appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" - "k8s.io/apimachinery/pkg/api/errors" + api_errors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/predicate" vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" ) @@ -61,6 +68,7 @@ type VectorReconciler struct { func (r *VectorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { _ = log.FromContext(ctx) log := log.FromContext(ctx).WithValues("Vector", req.NamespacedName) + log.Info("start Reconcile Vector") if req.Namespace == "" { vectors, err := listVectorCustomResourceInstances(ctx, r.Client) @@ -68,21 +76,9 @@ func (r *VectorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr log.Error(err, "Failed to list vector instances") return ctrl.Result{}, err } - for _, v := range vectors { - if v.Name == req.Name { - log.Info("start Reconcile Vector") - _, err := createOrUpdateVector(ctx, r.Client, r.Clientset, v) - if err != nil { - log.Error(err, "Failed to reconciler vector") - return ctrl.Result{}, err - } - } - } - return ctrl.Result{}, nil + return reconcileVectors(ctx, r.Client, r.Clientset, false, vectors...) } - log.Info("start Reconcile Vector") - vectorCR, err := r.findVectorCustomResourceInstance(ctx, req) if err != nil { log.Error(err, "Failed to get Vector") @@ -93,13 +89,13 @@ func (r *VectorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr return ctrl.Result{}, nil } - return createOrUpdateVector(ctx, r.Client, r.Clientset, vectorCR) + return createOrUpdateVector(ctx, r.Client, r.Clientset, vectorCR, false) } // SetupWithManager sets up the controller with the Manager. func (r *VectorReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). - For(&vectorv1alpha1.Vector{}). + For(&vectorv1alpha1.Vector{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})). Owns(&appsv1.DaemonSet{}). Owns(&corev1.Service{}). Owns(&corev1.Secret{}). @@ -126,7 +122,7 @@ func (r *VectorReconciler) findVectorCustomResourceInstance(ctx context.Context, vectorCR := &vectorv1alpha1.Vector{} err := r.Get(ctx, req.NamespacedName, vectorCR) if err != nil { - if errors.IsNotFound(err) { + if api_errors.IsNotFound(err) { return nil, nil } return nil, err @@ -135,7 +131,7 @@ func (r *VectorReconciler) findVectorCustomResourceInstance(ctx context.Context, return vectorCR, nil } -func reconcileVectors(ctx context.Context, client client.Client, clientset *kubernetes.Clientset, vectors ...*vectorv1alpha1.Vector) (ctrl.Result, error) { +func reconcileVectors(ctx context.Context, client client.Client, clientset *kubernetes.Clientset, configOnly bool, vectors ...*vectorv1alpha1.Vector) (ctrl.Result, error) { if len(vectors) == 0 { return ctrl.Result{}, nil } @@ -151,14 +147,14 @@ func reconcileVectors(ctx context.Context, client client.Client, clientset *kube vaCtrl.Vector.Spec.Agent.DataDir = "/vector-data-dir" } - if _, err := createOrUpdateVector(ctx, client, clientset, vector); err != nil { + if _, err := createOrUpdateVector(ctx, client, clientset, vector, configOnly); err != nil { return ctrl.Result{}, err } } return ctrl.Result{}, nil } -func createOrUpdateVector(ctx context.Context, client client.Client, clientset *kubernetes.Clientset, v *vectorv1alpha1.Vector) (ctrl.Result, error) { +func createOrUpdateVector(ctx context.Context, client client.Client, clientset *kubernetes.Clientset, v *vectorv1alpha1.Vector, configOnly bool) (ctrl.Result, error) { // Init Controller for Vector Agent vaCtrl := vectoragent.NewController(v, client, clientset) @@ -176,11 +172,52 @@ func createOrUpdateVector(ctx context.Context, client client.Client, clientset * if err != nil { return ctrl.Result{}, err } + cfgHash := hash.Get(byteConfig) + + if vaCtrl.Vector.Status.LastAppliedConfigHash == nil || *vaCtrl.Vector.Status.LastAppliedConfigHash != cfgHash { + configCheck := configcheck.New( + byteConfig, + vaCtrl.Client, + vaCtrl.ClientSet, + vaCtrl.Vector.Name, + vaCtrl.Vector.Namespace, + vaCtrl.Vector.Spec.Agent.Image, + vaCtrl.Vector.Spec.Agent.Env, + ) + err := configCheck.Run(ctx) + if errors.Is(err, configcheck.ValidationError) { + if err := vaCtrl.SetFailedStatus(ctx, err); err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{}, err + } + if err != nil { + return ctrl.Result{}, err + } + + } vaCtrl.Config = byteConfig + if err := vaCtrl.SetLastAppliedPipelineStatus(ctx, &cfgHash); err != nil { + //TODO: Handle err: Operation cannot be fulfilled on vectors.observability.kaasops.io \"vector-sample\": the object has been modified; please apply your changes to the latest version and try again + if api_errors.IsConflict(err) { + return ctrl.Result{}, err + } + return ctrl.Result{}, err + } + + if err := vaCtrl.SetSucceesStatus(ctx); err != nil { + // TODO: Handle err: Operation cannot be fulfilled on vectors.observability.kaasops.io \"vector-sample\": the object has been modified; please apply your changes to the latest version and try again + if api_errors.IsConflict(err) { + return ctrl.Result{}, err + } + return ctrl.Result{}, err + } + // Start Reconcile Vector Agent - if err := vaCtrl.EnsureVectorAgent(ctx); err != nil { + if err := vaCtrl.EnsureVectorAgent(ctx, configOnly); err != nil { return ctrl.Result{}, err } + return ctrl.Result{}, nil } diff --git a/controllers/vectorpipeline_controller.go b/controllers/vectorpipeline_controller.go index 18965188..defd68e6 100644 --- a/controllers/vectorpipeline_controller.go +++ b/controllers/vectorpipeline_controller.go @@ -78,9 +78,9 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque return ctrl.Result{}, nil } - if vectorPipelineCR == nil { + if vectorPipelineCR == nil || vectorPipelineCR.DeletionTimestamp != nil { log.Info("VectorPIpeline CR not found. Ignoring since object must be deleted") - return reconcileVectors(ctx, r.Client, r.Clientset, vectorInstances...) + return reconcileVectors(ctx, r.Client, r.Clientset, true, vectorInstances...) } // Check Pipeline hash @@ -110,7 +110,7 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque } log.Info("finish Reconcile VectorPipeline") - return reconcileVectors(ctx, r.Client, r.Clientset, vectorInstances...) + return reconcileVectors(ctx, r.Client, r.Clientset, true, vectorInstances...) } func (r *VectorPipelineReconciler) findVectorPipelineCustomResourceInstance(ctx context.Context, req ctrl.Request) (*vectorv1alpha1.VectorPipeline, error) { From 1e62758320710ba5648e57e86067e3f2f5c87a8e Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Tue, 29 Nov 2022 15:46:20 +0200 Subject: [PATCH 136/316] trigger reconcile from channel --- controllers/vector_controller.go | 7 +++++++ controllers/vectorpipeline_controller.go | 6 +++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/controllers/vector_controller.go b/controllers/vector_controller.go index 9cbff40a..184a5b5f 100644 --- a/controllers/vector_controller.go +++ b/controllers/vector_controller.go @@ -37,8 +37,11 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/predicate" + "sigs.k8s.io/controller-runtime/pkg/source" vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" ) @@ -65,6 +68,9 @@ type VectorReconciler struct { // // For more details, check Reconcile and its Result here: // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.12.2/pkg/reconcile + +var ReconciliationSourceChannel = make(chan event.GenericEvent) + func (r *VectorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { _ = log.FromContext(ctx) log := log.FromContext(ctx).WithValues("Vector", req.NamespacedName) @@ -96,6 +102,7 @@ func (r *VectorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr func (r *VectorReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&vectorv1alpha1.Vector{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})). + Watches(&source.Channel{Source: ReconciliationSourceChannel}, &handler.EnqueueRequestForObject{}). Owns(&appsv1.DaemonSet{}). Owns(&corev1.Service{}). Owns(&corev1.Secret{}). diff --git a/controllers/vectorpipeline_controller.go b/controllers/vectorpipeline_controller.go index defd68e6..fdb33a1c 100644 --- a/controllers/vectorpipeline_controller.go +++ b/controllers/vectorpipeline_controller.go @@ -24,6 +24,7 @@ import ( "k8s.io/client-go/kubernetes" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/event" "sigs.k8s.io/controller-runtime/pkg/log" vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" @@ -80,7 +81,10 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque if vectorPipelineCR == nil || vectorPipelineCR.DeletionTimestamp != nil { log.Info("VectorPIpeline CR not found. Ignoring since object must be deleted") - return reconcileVectors(ctx, r.Client, r.Clientset, true, vectorInstances...) + for _, vector := range vectorInstances { + ReconciliationSourceChannel <- event.GenericEvent{Object: vector} + return ctrl.Result{}, nil + } } // Check Pipeline hash From 51b278b37571bdf427964dd26171fb928c972684 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Tue, 29 Nov 2022 15:51:39 +0200 Subject: [PATCH 137/316] trigger vector reconilevia channel in cvp --- controllers/clustervectorpipeline_controller.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/controllers/clustervectorpipeline_controller.go b/controllers/clustervectorpipeline_controller.go index cd9e0e42..60960e85 100644 --- a/controllers/clustervectorpipeline_controller.go +++ b/controllers/clustervectorpipeline_controller.go @@ -24,6 +24,7 @@ import ( "k8s.io/client-go/kubernetes" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/event" "sigs.k8s.io/controller-runtime/pkg/log" vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" @@ -80,7 +81,10 @@ func (r *ClusterVectorPipelineReconciler) Reconcile(ctx context.Context, req ctr if vectorPipelineCR == nil || vectorPipelineCR.DeletionTimestamp != nil { log.Info("ClusterVectorPIpeline CR not found. Ignoring since object must be deleted") - return reconcileVectors(ctx, r.Client, r.Clientset, true, vectorInstances...) + for _, vector := range vectorInstances { + ReconciliationSourceChannel <- event.GenericEvent{Object: vector} + return ctrl.Result{}, nil + } } // Check Pipeline hash From 53928ac31f0af9a071cef95a5f57c83722a0d771 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Tue, 29 Nov 2022 15:53:52 +0200 Subject: [PATCH 138/316] rename vector agent reconcilation channel --- controllers/clustervectorpipeline_controller.go | 2 +- controllers/vector_controller.go | 4 ++-- controllers/vectorpipeline_controller.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/controllers/clustervectorpipeline_controller.go b/controllers/clustervectorpipeline_controller.go index 60960e85..53b7bbde 100644 --- a/controllers/clustervectorpipeline_controller.go +++ b/controllers/clustervectorpipeline_controller.go @@ -82,7 +82,7 @@ func (r *ClusterVectorPipelineReconciler) Reconcile(ctx context.Context, req ctr if vectorPipelineCR == nil || vectorPipelineCR.DeletionTimestamp != nil { log.Info("ClusterVectorPIpeline CR not found. Ignoring since object must be deleted") for _, vector := range vectorInstances { - ReconciliationSourceChannel <- event.GenericEvent{Object: vector} + VectorAgentReconciliationSourceChannel <- event.GenericEvent{Object: vector} return ctrl.Result{}, nil } } diff --git a/controllers/vector_controller.go b/controllers/vector_controller.go index 184a5b5f..6ee4a5aa 100644 --- a/controllers/vector_controller.go +++ b/controllers/vector_controller.go @@ -69,7 +69,7 @@ type VectorReconciler struct { // For more details, check Reconcile and its Result here: // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.12.2/pkg/reconcile -var ReconciliationSourceChannel = make(chan event.GenericEvent) +var VectorAgentReconciliationSourceChannel = make(chan event.GenericEvent) func (r *VectorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { _ = log.FromContext(ctx) @@ -102,7 +102,7 @@ func (r *VectorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr func (r *VectorReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&vectorv1alpha1.Vector{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})). - Watches(&source.Channel{Source: ReconciliationSourceChannel}, &handler.EnqueueRequestForObject{}). + Watches(&source.Channel{Source: VectorAgentReconciliationSourceChannel}, &handler.EnqueueRequestForObject{}). Owns(&appsv1.DaemonSet{}). Owns(&corev1.Service{}). Owns(&corev1.Secret{}). diff --git a/controllers/vectorpipeline_controller.go b/controllers/vectorpipeline_controller.go index fdb33a1c..2b0dec8b 100644 --- a/controllers/vectorpipeline_controller.go +++ b/controllers/vectorpipeline_controller.go @@ -82,7 +82,7 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque if vectorPipelineCR == nil || vectorPipelineCR.DeletionTimestamp != nil { log.Info("VectorPIpeline CR not found. Ignoring since object must be deleted") for _, vector := range vectorInstances { - ReconciliationSourceChannel <- event.GenericEvent{Object: vector} + VectorAgentReconciliationSourceChannel <- event.GenericEvent{Object: vector} return ctrl.Result{}, nil } } From 0db6ace257d0e91ca087c8267ae7f1c1e2d3c650 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Tue, 29 Nov 2022 18:26:01 +0200 Subject: [PATCH 139/316] do not reconile vector if vp check fail --- controllers/clustervectorpipeline_controller.go | 7 ++++++- controllers/vectorpipeline_controller.go | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/controllers/clustervectorpipeline_controller.go b/controllers/clustervectorpipeline_controller.go index 53b7bbde..3ea09462 100644 --- a/controllers/clustervectorpipeline_controller.go +++ b/controllers/clustervectorpipeline_controller.go @@ -111,10 +111,15 @@ func (r *ClusterVectorPipelineReconciler) Reconcile(ctx context.Context, req ctr if err := config.ReconcileConfig(ctx, r.Client, vectorPipelineCR, vaCtrl); err != nil { return ctrl.Result{}, err } + + // Start vector reconcilation + if *vectorPipelineCR.Status.ConfigCheckResult { + VectorAgentReconciliationSourceChannel <- event.GenericEvent{Object: vector} + } } log.Info("finish Reconcile ClusterVectorPipeline") - return reconcileVectors(ctx, r.Client, r.Clientset, true, vectorInstances...) + return ctrl.Result{}, nil } func (r *ClusterVectorPipelineReconciler) findClusterVectorPipelineCustomResourceInstance(ctx context.Context, req ctrl.Request) (*vectorv1alpha1.ClusterVectorPipeline, error) { diff --git a/controllers/vectorpipeline_controller.go b/controllers/vectorpipeline_controller.go index 2b0dec8b..db96f682 100644 --- a/controllers/vectorpipeline_controller.go +++ b/controllers/vectorpipeline_controller.go @@ -81,6 +81,7 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque if vectorPipelineCR == nil || vectorPipelineCR.DeletionTimestamp != nil { log.Info("VectorPIpeline CR not found. Ignoring since object must be deleted") + // Start vector reconcilation for _, vector := range vectorInstances { VectorAgentReconciliationSourceChannel <- event.GenericEvent{Object: vector} return ctrl.Result{}, nil @@ -111,10 +112,14 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque if err := config.ReconcileConfig(ctx, r.Client, vectorPipelineCR, vaCtrl); err != nil { return ctrl.Result{}, err } + // Start vector reconcilation + if *vectorPipelineCR.Status.ConfigCheckResult { + VectorAgentReconciliationSourceChannel <- event.GenericEvent{Object: vector} + } } log.Info("finish Reconcile VectorPipeline") - return reconcileVectors(ctx, r.Client, r.Clientset, true, vectorInstances...) + return ctrl.Result{}, nil } func (r *VectorPipelineReconciler) findVectorPipelineCustomResourceInstance(ctx context.Context, req ctrl.Request) (*vectorv1alpha1.VectorPipeline, error) { From 49b0d8706a848ef013a1fe0452becd754de73fca Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Thu, 1 Dec 2022 15:39:02 +0200 Subject: [PATCH 140/316] merge vp and cvp reconcile funcs --- api/v1alpha1/clustervectorpipeline.go | 4 + api/v1alpha1/clustervectorpipeline_types.go | 2 +- api/v1alpha1/vectorpipeline.go | 4 + ...ity.kaasops.io_clustervectorpipelines.yaml | 2 +- config/rbac/role.yaml | 52 ------- ...bility_v1alpha1_clustervectorpipeline.yaml | 12 +- .../clustervectorpipeline_controller.go | 143 ------------------ controllers/factory/config/utils.go | 5 +- controllers/factory/pipeline/pipeline.go | 1 + ...e_controller.go => pipeline_controller.go} | 83 +++++----- main.go | 10 +- 11 files changed, 67 insertions(+), 251 deletions(-) delete mode 100644 controllers/clustervectorpipeline_controller.go rename controllers/{vectorpipeline_controller.go => pipeline_controller.go} (51%) diff --git a/api/v1alpha1/clustervectorpipeline.go b/api/v1alpha1/clustervectorpipeline.go index b27b437a..aba26cc5 100644 --- a/api/v1alpha1/clustervectorpipeline.go +++ b/api/v1alpha1/clustervectorpipeline.go @@ -42,6 +42,10 @@ func (vp *ClusterVectorPipeline) SetConfigCheck(value bool) { vp.Status.ConfigCheckResult = &value } +func (vp *ClusterVectorPipeline) GetConfigCheckResult() *bool { + return vp.Status.ConfigCheckResult +} + func (vp *ClusterVectorPipeline) SetReason(reason *string) { vp.Status.Reason = reason } diff --git a/api/v1alpha1/clustervectorpipeline_types.go b/api/v1alpha1/clustervectorpipeline_types.go index 41fe3883..898aaf52 100644 --- a/api/v1alpha1/clustervectorpipeline_types.go +++ b/api/v1alpha1/clustervectorpipeline_types.go @@ -25,7 +25,7 @@ import ( //+kubebuilder:object:root=true //+kubebuilder:subresource:status -//+kubebuilder:resource:shortName=cvp +//+kubebuilder:resource:scope=Cluster,shortName=cvp //+kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" //+kubebuilder:printcolumn:name="Valid",type="boolean",JSONPath=".status.configCheckResult" diff --git a/api/v1alpha1/vectorpipeline.go b/api/v1alpha1/vectorpipeline.go index c99d3a5c..ebcde2f4 100644 --- a/api/v1alpha1/vectorpipeline.go +++ b/api/v1alpha1/vectorpipeline.go @@ -42,6 +42,10 @@ func (vp *VectorPipeline) SetConfigCheck(value bool) { vp.Status.ConfigCheckResult = &value } +func (vp *VectorPipeline) GetConfigCheckResult() *bool { + return vp.Status.ConfigCheckResult +} + func (vp *VectorPipeline) SetReason(reason *string) { vp.Status.Reason = reason } diff --git a/config/crd/bases/observability.kaasops.io_clustervectorpipelines.yaml b/config/crd/bases/observability.kaasops.io_clustervectorpipelines.yaml index 715b8145..760cca22 100644 --- a/config/crd/bases/observability.kaasops.io_clustervectorpipelines.yaml +++ b/config/crd/bases/observability.kaasops.io_clustervectorpipelines.yaml @@ -15,7 +15,7 @@ spec: shortNames: - cvp singular: clustervectorpipeline - scope: Namespaced + scope: Cluster versions: - additionalPrinterColumns: - jsonPath: .metadata.creationTimestamp diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 276b6e63..187fbbb9 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -5,58 +5,6 @@ metadata: creationTimestamp: null name: manager-role rules: -- apiGroups: - - observability.kaasops.io - resources: - - clustervectorpipelines - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - observability.kaasops.io - resources: - - clustervectorpipelines/finalizers - verbs: - - update -- apiGroups: - - observability.kaasops.io - resources: - - clustervectorpipelines/status - verbs: - - get - - patch - - update -- apiGroups: - - observability.kaasops.io - resources: - - vectorpipelines - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - observability.kaasops.io - resources: - - vectorpipelines/finalizers - verbs: - - update -- apiGroups: - - observability.kaasops.io - resources: - - vectorpipelines/status - verbs: - - get - - patch - - update - apiGroups: - observability.kaasops.io resources: diff --git a/config/samples/observability_v1alpha1_clustervectorpipeline.yaml b/config/samples/observability_v1alpha1_clustervectorpipeline.yaml index 4a453702..98b57654 100644 --- a/config/samples/observability_v1alpha1_clustervectorpipeline.yaml +++ b/config/samples/observability_v1alpha1_clustervectorpipeline.yaml @@ -3,4 +3,14 @@ kind: ClusterVectorPipeline metadata: name: clustervectorpipeline-sample spec: - # TODO(user): Add fields here + sources: + test1: + type: "kubernetes_logs" + extra_label_selector: "app!=testdeployment3" + sinks: + test2: + type: "console" + encoding: + codec: "json" + inputs: + - test1 \ No newline at end of file diff --git a/controllers/clustervectorpipeline_controller.go b/controllers/clustervectorpipeline_controller.go deleted file mode 100644 index 3ea09462..00000000 --- a/controllers/clustervectorpipeline_controller.go +++ /dev/null @@ -1,143 +0,0 @@ -/* -Copyright 2022. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package controllers - -import ( - "context" - - api_errors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/kubernetes" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/event" - "sigs.k8s.io/controller-runtime/pkg/log" - - vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" - "github.com/kaasops/vector-operator/controllers/factory/config" - "github.com/kaasops/vector-operator/controllers/factory/pipeline" - "github.com/kaasops/vector-operator/controllers/factory/vector/vectoragent" -) - -// ClusterVectorPipelineReconciler reconciles a ClusterVectorPipeline object -type ClusterVectorPipelineReconciler struct { - client.Client - Scheme *runtime.Scheme - - // Temp. Wait this issue - https://github.com/kubernetes-sigs/controller-runtime/issues/452 - Clientset *kubernetes.Clientset -} - -//+kubebuilder:rbac:groups=observability.kaasops.io,resources=clustervectorpipelines,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=observability.kaasops.io,resources=clustervectorpipelines/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=observability.kaasops.io,resources=clustervectorpipelines/finalizers,verbs=update - -// Reconcile is part of the main kubernetes reconciliation loop which aims to -// move the current state of the cluster closer to the desired state. -// TODO(user): Modify the Reconcile function to compare the state specified by -// the ClusterVectorPipeline object against the actual cluster state, and then -// perform operations to make the cluster state reflect the state specified by -// the user. -// -// For more details, check Reconcile and its Result here: -// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.12.2/pkg/reconcile -func (r *ClusterVectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - log := log.FromContext(ctx).WithValues("ClusterVectorPipeline", req.Name) - - log.Info("start Reconcile VectorPipeline") - - // Get CR VectorPipeline - vectorPipelineCR, err := r.findClusterVectorPipelineCustomResourceInstance(ctx, req) - if err != nil { - log.Error(err, "Failed to get Vector Pipeline") - return ctrl.Result{}, err - } - - vectorInstances, err := listVectorCustomResourceInstances(ctx, r.Client) - - if err != nil { - log.Error(err, "Failed to get Vector Instances") - return ctrl.Result{}, nil - } - - if len(vectorInstances) == 0 { - log.Info("Vertors not found") - return ctrl.Result{}, nil - } - - if vectorPipelineCR == nil || vectorPipelineCR.DeletionTimestamp != nil { - log.Info("ClusterVectorPIpeline CR not found. Ignoring since object must be deleted") - for _, vector := range vectorInstances { - VectorAgentReconciliationSourceChannel <- event.GenericEvent{Object: vector} - return ctrl.Result{}, nil - } - } - - // Check Pipeline hash - checkResult, err := pipeline.CheckHash(vectorPipelineCR) - if err != nil { - return ctrl.Result{}, err - } - if checkResult { - log.Info("ClusterVectorPipeline has no changes. Finish Reconcile VectorPipeline") - return ctrl.Result{}, nil - } - - for _, vector := range vectorInstances { - if vector.DeletionTimestamp != nil { - continue - } - - // Init Controller for Vector Agent - vaCtrl := vectoragent.NewController(vector, r.Client, r.Clientset) - if vaCtrl.Vector.Spec.Agent.DataDir == "" { - vaCtrl.Vector.Spec.Agent.DataDir = "/vector-data-dir" - } - - if err := config.ReconcileConfig(ctx, r.Client, vectorPipelineCR, vaCtrl); err != nil { - return ctrl.Result{}, err - } - - // Start vector reconcilation - if *vectorPipelineCR.Status.ConfigCheckResult { - VectorAgentReconciliationSourceChannel <- event.GenericEvent{Object: vector} - } - } - - log.Info("finish Reconcile ClusterVectorPipeline") - return ctrl.Result{}, nil -} - -func (r *ClusterVectorPipelineReconciler) findClusterVectorPipelineCustomResourceInstance(ctx context.Context, req ctrl.Request) (*vectorv1alpha1.ClusterVectorPipeline, error) { - // fetch the master instance - cvp := &vectorv1alpha1.ClusterVectorPipeline{} - err := r.Get(ctx, req.NamespacedName, cvp) - if err != nil { - if api_errors.IsNotFound(err) { - return nil, nil - } - return nil, err - } - return cvp, nil -} - -// SetupWithManager sets up the controller with the Manager. -func (r *ClusterVectorPipelineReconciler) SetupWithManager(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - For(&vectorv1alpha1.ClusterVectorPipeline{}). - Complete(r) -} diff --git a/controllers/factory/config/utils.go b/controllers/factory/config/utils.go index fc37bf91..4d8ab0f4 100644 --- a/controllers/factory/config/utils.go +++ b/controllers/factory/config/utils.go @@ -24,5 +24,8 @@ func addPrefix(Namespace, Name, componentName string) string { } func generateName(Namespace, Name string) string { - return Namespace + "-" + Name + if Namespace != "" { + return Namespace + "-" + Name + } + return Name } diff --git a/controllers/factory/pipeline/pipeline.go b/controllers/factory/pipeline/pipeline.go index 699dc894..59c7297d 100644 --- a/controllers/factory/pipeline/pipeline.go +++ b/controllers/factory/pipeline/pipeline.go @@ -32,6 +32,7 @@ type Pipeline interface { SetReason(*string) GetLastAppliedPipeline() *uint32 SetLastAppliedPipeline(*uint32) + GetConfigCheckResult() *bool IsValid() bool IsDeleted() bool UpdateStatus(context.Context, client.Client) error diff --git a/controllers/vectorpipeline_controller.go b/controllers/pipeline_controller.go similarity index 51% rename from controllers/vectorpipeline_controller.go rename to controllers/pipeline_controller.go index db96f682..b02b26a1 100644 --- a/controllers/vectorpipeline_controller.go +++ b/controllers/pipeline_controller.go @@ -25,7 +25,9 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/source" vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" "github.com/kaasops/vector-operator/controllers/factory/config" @@ -33,8 +35,7 @@ import ( "github.com/kaasops/vector-operator/controllers/factory/vector/vectoragent" ) -// VectorPipelineReconciler reconciles a VectorPipeline object -type VectorPipelineReconciler struct { +type PipelineReconciler struct { client.Client Scheme *runtime.Scheme @@ -42,35 +43,19 @@ type VectorPipelineReconciler struct { Clientset *kubernetes.Clientset } -//+kubebuilder:rbac:groups=observability.kaasops.io,resources=vectorpipelines,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=observability.kaasops.io,resources=vectorpipelines/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=observability.kaasops.io,resources=vectorpipelines/finalizers,verbs=update - -// Reconcile is part of the main kubernetes reconciliation loop which aims to -// move the current state of the cluster closer to the desired state. -// TODO(user): Modify the Reconcile function to compare the state specified by -// the VectorPipeline object against the actual cluster state, and then -// perform operations to make the cluster state reflect the state specified by -// the user. -// -// For more details, check Reconcile and its Result here: -// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.12.2/pkg/reconcile -func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - log := log.FromContext(ctx).WithValues("VectorPipeline", req.Name) - - log.Info("start Reconcile VectorPipeline") - - // Get CR VectorPipeline - vectorPipelineCR, err := r.findVectorPipelineCustomResourceInstance(ctx, req) +func (r *PipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + log := log.FromContext(ctx).WithValues("Pipeline", req.Name) + + log.Info("start Reconcile Pipeline") + pipelineCR, err := r.findPipelineCustomResourceInstance(ctx, req) if err != nil { - log.Error(err, "Failed to get Vector Pipeline") + log.Error(err, "Failed to get Pipeline") return ctrl.Result{}, err } - vectorInstances, err := listVectorCustomResourceInstances(ctx, r.Client) if err != nil { - log.Error(err, "Failed to get Vector Instances") + log.Error(err, "Failed to get Instances") return ctrl.Result{}, nil } @@ -79,9 +64,8 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque return ctrl.Result{}, nil } - if vectorPipelineCR == nil || vectorPipelineCR.DeletionTimestamp != nil { - log.Info("VectorPIpeline CR not found. Ignoring since object must be deleted") - // Start vector reconcilation + if pipelineCR == nil { + log.Info("Pipeline CR not found. Ignoring since object must be deleted") for _, vector := range vectorInstances { VectorAgentReconciliationSourceChannel <- event.GenericEvent{Object: vector} return ctrl.Result{}, nil @@ -89,12 +73,12 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque } // Check Pipeline hash - checkResult, err := pipeline.CheckHash(vectorPipelineCR) + checkResult, err := pipeline.CheckHash(pipelineCR) if err != nil { return ctrl.Result{}, err } if checkResult { - log.Info("VectorPipeline has no changes. Finish Reconcile VectorPipeline") + log.Info("Pipeline has no changes. Finish Reconcile Pipeline") return ctrl.Result{}, nil } @@ -109,35 +93,48 @@ func (r *VectorPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Reque vaCtrl.Vector.Spec.Agent.DataDir = "/vector-data-dir" } - if err := config.ReconcileConfig(ctx, r.Client, vectorPipelineCR, vaCtrl); err != nil { + if err := config.ReconcileConfig(ctx, r.Client, pipelineCR, vaCtrl); err != nil { return ctrl.Result{}, err } + // Start vector reconcilation - if *vectorPipelineCR.Status.ConfigCheckResult { + if *pipelineCR.GetConfigCheckResult() { VectorAgentReconciliationSourceChannel <- event.GenericEvent{Object: vector} } } - log.Info("finish Reconcile VectorPipeline") + log.Info("finish Reconcile Pipeline") return ctrl.Result{}, nil } -func (r *VectorPipelineReconciler) findVectorPipelineCustomResourceInstance(ctx context.Context, req ctrl.Request) (*vectorv1alpha1.VectorPipeline, error) { - // fetch the master instance - vectorPipelineCR := &vectorv1alpha1.VectorPipeline{} - err := r.Get(ctx, req.NamespacedName, vectorPipelineCR) - if err != nil { - if api_errors.IsNotFound(err) { - return nil, nil +func (r *PipelineReconciler) findPipelineCustomResourceInstance(ctx context.Context, req ctrl.Request) (pipeline pipeline.Pipeline, err error) { + if req.Namespace != "" { + vp := &vectorv1alpha1.VectorPipeline{} + err := r.Get(ctx, req.NamespacedName, vp) + if err != nil { + if api_errors.IsNotFound(err) { + return nil, nil + } + return nil, err + } + return vp, nil + } else { + cvp := &vectorv1alpha1.ClusterVectorPipeline{} + err := r.Get(ctx, req.NamespacedName, cvp) + if err != nil { + if api_errors.IsNotFound(err) { + return nil, nil + } + return nil, err } - return nil, err + return cvp, nil } - return vectorPipelineCR, nil } // SetupWithManager sets up the controller with the Manager. -func (r *VectorPipelineReconciler) SetupWithManager(mgr ctrl.Manager) error { +func (r *PipelineReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&vectorv1alpha1.VectorPipeline{}). + Watches(&source.Kind{Type: &vectorv1alpha1.ClusterVectorPipeline{}}, &handler.EnqueueRequestForObject{}). Complete(r) } diff --git a/main.go b/main.go index be7ddce3..aa6255ff 100644 --- a/main.go +++ b/main.go @@ -113,7 +113,7 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "Vector") os.Exit(1) } - if err = (&controllers.VectorPipelineReconciler{ + if err = (&controllers.PipelineReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Clientset: clientset, @@ -121,14 +121,6 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "VectorPipeline") os.Exit(1) } - if err = (&controllers.ClusterVectorPipelineReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Clientset: clientset, - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "ClusterVectorPipeline") - os.Exit(1) - } //+kubebuilder:scaffold:builder if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { From 2fa76e96d48a9a93eddfa3a996e82c95f7427e00 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Thu, 1 Dec 2022 15:58:41 +0200 Subject: [PATCH 141/316] update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35a61b2d..5a142700 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,7 @@ ### Added +- [[65]](https://github.com/kaasops/vector-operator/pull/65) **Refactor**: merge vp and cvp reconcile funcs +- [[64]](https://github.com/kaasops/vector-operator/pull/64) **Fix** Do not reconсile vector if vp check fail +- [[63]](https://github.com/kaasops/vector-operator/pull/63) **Fix** Fix configcheck gc ### v0.0.7 - [[61]](https://github.com/kaasops/vector-operator/pull/61) **Feature** Filter cache and disable time reconcile From da0aa306571b7eb55e5403921f48403531974d00 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Thu, 1 Dec 2022 16:22:09 +0200 Subject: [PATCH 142/316] update docs --- docs/design.md | 2 ++ helm/charts/vector-operator/values.yaml | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/design.md b/docs/design.md index 1c75b8e5..907305a0 100644 --- a/docs/design.md +++ b/docs/design.md @@ -28,6 +28,7 @@ Specification access to [this](https://github.com/kaasops/vector-operator/blob/m # VectorPipeline +The `VectorPipeline` is a namespace-scoped CRD. The `VectorPipeline` CRD defines Sources, Transforms and Sinks rules for Vector. All `VectorPipelines`, with validated configuration file, added to Vector configuration file. @@ -39,6 +40,7 @@ All `VectorPipelines`, with validated configuration file, added to Vector config Specification access to [this](https://github.com/kaasops/vector-operator/blob/main/docs/specification.md#vectorpipelinespec-clustervectorpipelinespec) page # ClusterVectorPipeline +The `ClusterVectorPipeline` is a cluster-scoped CRD. The `ClusterVectorPipeline` CRD defines Sources, Transforms and Sinks rules for Vector. All `ClusterVectorPipelines`, with validated configuration file, added to Vector configuration file. diff --git a/helm/charts/vector-operator/values.yaml b/helm/charts/vector-operator/values.yaml index c58d17a3..b7fc7bb9 100644 --- a/helm/charts/vector-operator/values.yaml +++ b/helm/charts/vector-operator/values.yaml @@ -61,7 +61,8 @@ annotations: {} # Args to path to operator args: -# - "-watch-namespace vector" +# - "-watch-namespace=vector" # Namespace to filter the list of watched objects +# - "-watch-name=vector-operator" # Filter the list of watched objects by checking the app.kubernetes.io/managed-by label vector: enable: false From 161131e8da6cf0b9847aef34312c0f25a290a5f4 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Thu, 1 Dec 2022 16:26:47 +0200 Subject: [PATCH 143/316] prepare release v0.0.8 --- CHANGELOG.md | 2 ++ helm/charts/vector-operator/Chart.yaml | 4 ++-- helm/index.yaml | 21 +++++++++++++++++---- helm/packages/vector-operator-0.0.8.tgz | Bin 0 -> 12162 bytes 4 files changed, 21 insertions(+), 6 deletions(-) create mode 100644 helm/packages/vector-operator-0.0.8.tgz diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a142700..8e5a1c2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ ### Added + +### v0.0.8 - [[65]](https://github.com/kaasops/vector-operator/pull/65) **Refactor**: merge vp and cvp reconcile funcs - [[64]](https://github.com/kaasops/vector-operator/pull/64) **Fix** Do not reconсile vector if vp check fail - [[63]](https://github.com/kaasops/vector-operator/pull/63) **Fix** Fix configcheck gc diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index 657c99a2..e7580e14 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.7 +version: 0.0.8 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.0.7" +appVersion: "v0.0.8" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/index.yaml b/helm/index.yaml index 6f3667c8..4260fe69 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.0.8 + created: "2022-12-01T16:25:50.482786094+02:00" + description: A Helm chart to install Vector Operator + digest: 81852aff874288ce328a9310bc042a7ef5cd96b3e0cb87f9cbcc6867fae0bcf1 + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.8.tgz + version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2022-11-25T12:04:21.024405229+02:00" + created: "2022-12-01T16:25:50.482280389+02:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2022-11-25T12:04:21.023905177+02:00" + created: "2022-12-01T16:25:50.481683486+02:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2022-11-25T12:04:21.023215115+02:00" + created: "2022-12-01T16:25:50.481071126+02:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -40,4 +53,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2022-11-25T12:04:21.01649915+02:00" +generated: "2022-12-01T16:25:50.474415137+02:00" diff --git a/helm/packages/vector-operator-0.0.8.tgz b/helm/packages/vector-operator-0.0.8.tgz new file mode 100644 index 0000000000000000000000000000000000000000..35944774c137557155424e9df2239bcb3d99480c GIT binary patch literal 12162 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 literal 0 HcmV?d00001 From b7515af576a4afb759e6161d5237af113a2404ba Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Fri, 2 Dec 2022 13:52:18 +0200 Subject: [PATCH 144/316] update crd in helm chart --- .../crds/observability.kaasops.io_clustervectorpipelines.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectorpipelines.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectorpipelines.yaml index 715b8145..760cca22 100644 --- a/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectorpipelines.yaml +++ b/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectorpipelines.yaml @@ -15,7 +15,7 @@ spec: shortNames: - cvp singular: clustervectorpipeline - scope: Namespaced + scope: Cluster versions: - additionalPrinterColumns: - jsonPath: .metadata.creationTimestamp From c0e139307d82d354dfd410a4d42fd9cbc4535991 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Fri, 2 Dec 2022 13:53:10 +0200 Subject: [PATCH 145/316] update helm chart --- helm/index.yaml | 12 ++++++------ helm/packages/vector-operator-0.0.8.tgz | Bin 12162 -> 12166 bytes 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/helm/index.yaml b/helm/index.yaml index 4260fe69..17014a1c 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -3,9 +3,9 @@ entries: vector-operator: - apiVersion: v2 appVersion: v0.0.8 - created: "2022-12-01T16:25:50.482786094+02:00" + created: "2022-12-02T13:52:52.499422043+02:00" description: A Helm chart to install Vector Operator - digest: 81852aff874288ce328a9310bc042a7ef5cd96b3e0cb87f9cbcc6867fae0bcf1 + digest: fcac21cc65514b00d13c25fd331c44292b9022ce2c94b0191b08566a19181d5c home: https://github.com/kaasops/vector-operator name: vector-operator sources: @@ -16,7 +16,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2022-12-01T16:25:50.482280389+02:00" + created: "2022-12-02T13:52:52.498610962+02:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -29,7 +29,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2022-12-01T16:25:50.481683486+02:00" + created: "2022-12-02T13:52:52.49775392+02:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -42,7 +42,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2022-12-01T16:25:50.481071126+02:00" + created: "2022-12-02T13:52:52.496881453+02:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -53,4 +53,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2022-12-01T16:25:50.474415137+02:00" +generated: "2022-12-02T13:52:52.490011002+02:00" diff --git a/helm/packages/vector-operator-0.0.8.tgz b/helm/packages/vector-operator-0.0.8.tgz index 35944774c137557155424e9df2239bcb3d99480c..0b5fba62927ba0346c3330a0135d41cd12e994cd 100644 GIT binary patch delta 12131 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 delta 12127 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 From 544f686b6ed2415a85094e98d980730f408550f3 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Fri, 2 Dec 2022 15:33:38 +0200 Subject: [PATCH 146/316] remove cvp namespace from helm chart --- helm/charts/vector-operator/templates/clustervectorpipeline.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/helm/charts/vector-operator/templates/clustervectorpipeline.yaml b/helm/charts/vector-operator/templates/clustervectorpipeline.yaml index cb6ad1f6..f392768a 100644 --- a/helm/charts/vector-operator/templates/clustervectorpipeline.yaml +++ b/helm/charts/vector-operator/templates/clustervectorpipeline.yaml @@ -4,7 +4,6 @@ apiVersion: observability.kaasops.io/v1alpha1 kind: ClusterVectorPipeline metadata: name: {{ $pipeline.name }} - namespace: {{ $namespace }} spec: sources: {{- toYaml $pipeline.sources | nindent 4 }} From 74865f2c5b5b2e3e6360f62fff5c3f8ec9bf00bc Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Fri, 2 Dec 2022 15:34:27 +0200 Subject: [PATCH 147/316] build helm chart --- helm/index.yaml | 12 ++++++------ helm/packages/vector-operator-0.0.8.tgz | Bin 12166 -> 12159 bytes 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/helm/index.yaml b/helm/index.yaml index 17014a1c..bdf82879 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -3,9 +3,9 @@ entries: vector-operator: - apiVersion: v2 appVersion: v0.0.8 - created: "2022-12-02T13:52:52.499422043+02:00" + created: "2022-12-02T15:34:03.411969632+02:00" description: A Helm chart to install Vector Operator - digest: fcac21cc65514b00d13c25fd331c44292b9022ce2c94b0191b08566a19181d5c + digest: 74a141b201d5f24a149281a5adf9409ababea78a024d5f15551a0d155848eacb home: https://github.com/kaasops/vector-operator name: vector-operator sources: @@ -16,7 +16,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2022-12-02T13:52:52.498610962+02:00" + created: "2022-12-02T15:34:03.411450281+02:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -29,7 +29,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2022-12-02T13:52:52.49775392+02:00" + created: "2022-12-02T15:34:03.410832668+02:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -42,7 +42,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2022-12-02T13:52:52.496881453+02:00" + created: "2022-12-02T15:34:03.410287319+02:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -53,4 +53,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2022-12-02T13:52:52.490011002+02:00" +generated: "2022-12-02T15:34:03.4097285+02:00" diff --git a/helm/packages/vector-operator-0.0.8.tgz b/helm/packages/vector-operator-0.0.8.tgz index 0b5fba62927ba0346c3330a0135d41cd12e994cd..fa21a0e041b5b880abfcddb1ac6aff257cd4a2ac 100644 GIT binary patch delta 9031 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 c|2^J)&+5i9m delta 8984 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 h32y-w6_aU Date: Fri, 2 Dec 2022 15:36:03 +0200 Subject: [PATCH 148/316] remove cvp ns from helm chart --- helm/charts/vector-operator/templates/clustervectorpipeline.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/helm/charts/vector-operator/templates/clustervectorpipeline.yaml b/helm/charts/vector-operator/templates/clustervectorpipeline.yaml index f392768a..343547a2 100644 --- a/helm/charts/vector-operator/templates/clustervectorpipeline.yaml +++ b/helm/charts/vector-operator/templates/clustervectorpipeline.yaml @@ -1,4 +1,3 @@ -{{ $namespace := .Release.Namespace }} {{- range $pipeline := .Values.clustervectorpipeline }} apiVersion: observability.kaasops.io/v1alpha1 kind: ClusterVectorPipeline From f47866af9c54f32f3dfd3f95d4c39d05e58513e5 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Fri, 2 Dec 2022 15:36:17 +0200 Subject: [PATCH 149/316] build chart --- helm/index.yaml | 12 ++++++------ helm/packages/vector-operator-0.0.8.tgz | Bin 12159 -> 12149 bytes 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/helm/index.yaml b/helm/index.yaml index bdf82879..6c22f1b0 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -3,9 +3,9 @@ entries: vector-operator: - apiVersion: v2 appVersion: v0.0.8 - created: "2022-12-02T15:34:03.411969632+02:00" + created: "2022-12-02T15:36:12.752976791+02:00" description: A Helm chart to install Vector Operator - digest: 74a141b201d5f24a149281a5adf9409ababea78a024d5f15551a0d155848eacb + digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator name: vector-operator sources: @@ -16,7 +16,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2022-12-02T15:34:03.411450281+02:00" + created: "2022-12-02T15:36:12.752470739+02:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -29,7 +29,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2022-12-02T15:34:03.410832668+02:00" + created: "2022-12-02T15:36:12.751974518+02:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -42,7 +42,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2022-12-02T15:34:03.410287319+02:00" + created: "2022-12-02T15:36:12.751485522+02:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -53,4 +53,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2022-12-02T15:34:03.4097285+02:00" +generated: "2022-12-02T15:36:12.750776861+02:00" diff --git a/helm/packages/vector-operator-0.0.8.tgz b/helm/packages/vector-operator-0.0.8.tgz index fa21a0e041b5b880abfcddb1ac6aff257cd4a2ac..27e35e1a2c673e2e632f1ece61dc40cd53f8b588 100644 GIT binary patch delta 12122 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 Lje}_U1o3|WXN1>z From 4e99a466edf5f32a01cd49b4d762a08e3f409066 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Sat, 3 Dec 2022 14:19:57 +0200 Subject: [PATCH 150/316] Add tolerations to helm-chart Signed-off-by: Zemtsov Vladimir --- helm/charts/vector-operator/templates/deployment.yaml | 4 ++++ helm/charts/vector-operator/values.yaml | 3 +++ 2 files changed, 7 insertions(+) diff --git a/helm/charts/vector-operator/templates/deployment.yaml b/helm/charts/vector-operator/templates/deployment.yaml index 1a9284af..bfec2e76 100644 --- a/helm/charts/vector-operator/templates/deployment.yaml +++ b/helm/charts/vector-operator/templates/deployment.yaml @@ -29,6 +29,10 @@ spec: {{- with .Values.imagePullSecrets }} imagePullSecrets: {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} {{- end }} serviceAccountName: {{ include "chart.serviceAccountName" . }} securityContext: diff --git a/helm/charts/vector-operator/values.yaml b/helm/charts/vector-operator/values.yaml index b7fc7bb9..415f81a6 100644 --- a/helm/charts/vector-operator/values.yaml +++ b/helm/charts/vector-operator/values.yaml @@ -31,6 +31,9 @@ securityContext: capabilities: drop: - ALL + +tolerations: {} + resources: limits: cpu: "1" From 104b6095957c2f5b07fa22d93cb0e995e1054826 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Sat, 3 Dec 2022 14:20:34 +0200 Subject: [PATCH 151/316] add default podSecurityContext to values Signed-off-by: Zemtsov Vladimir --- helm/charts/vector-operator/values.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/helm/charts/vector-operator/values.yaml b/helm/charts/vector-operator/values.yaml index 415f81a6..a87822af 100644 --- a/helm/charts/vector-operator/values.yaml +++ b/helm/charts/vector-operator/values.yaml @@ -32,6 +32,8 @@ securityContext: drop: - ALL +podSecurityContext: {} + tolerations: {} resources: From 955e9ce7702618a59217561753c48b271ac2b553 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Sat, 3 Dec 2022 14:26:29 +0200 Subject: [PATCH 152/316] Update changelog Signed-off-by: Zemtsov Vladimir --- CHANGELOG.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e5a1c2d..7e64ac33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,14 @@ ### Added +- [[65](https://github.com/kaasops/vector-operator/pull/69)] **Helm**: Add toleration-control for vector-operator deployment to helm chart +- [[65](https://github.com/kaasops/vector-operator/pull/69)] **Cleanup**: Add default value for podSecurityContext in chart values ### v0.0.8 -- [[65]](https://github.com/kaasops/vector-operator/pull/65) **Refactor**: merge vp and cvp reconcile funcs -- [[64]](https://github.com/kaasops/vector-operator/pull/64) **Fix** Do not reconсile vector if vp check fail -- [[63]](https://github.com/kaasops/vector-operator/pull/63) **Fix** Fix configcheck gc +- [[65](https://github.com/kaasops/vector-operator/pull/65)] **Refactor**: merge vp and cvp reconcile funcs +- [[64](https://github.com/kaasops/vector-operator/pull/64)] **Fix** Do not reconсile vector if vp check fail +- [[63](https://github.com/kaasops/vector-operator/pull/63)] **Fix** Fix configcheck gc ### v0.0.7 -- [[61]](https://github.com/kaasops/vector-operator/pull/61) **Feature** Filter cache and disable time reconcile +- [[61](https://github.com/kaasops/vector-operator/pull/61)] **Feature** Filter cache and disable time reconcile ### v0.0.6 - [[60](https://github.com/kaasops/vector-operator/pull/60)] **Fix**: Fix Vector agent DaemosSet for collect journald service logs From 4f40b685c3be0e78729f246a69d437a8dc3ce29d Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Tue, 6 Dec 2022 16:43:27 +0200 Subject: [PATCH 153/316] fix nil vector panic --- api/v1alpha1/vector_types.go | 1 - .../observability.kaasops.io_vectors.yaml | 1 - .../vector/vectoragent/vectoragent_default.go | 4 ++++ controllers/pipeline_controller.go | 5 ++--- controllers/vector_controller.go | 19 +++++-------------- 5 files changed, 11 insertions(+), 19 deletions(-) diff --git a/api/v1alpha1/vector_types.go b/api/v1alpha1/vector_types.go index 9992dab2..c522ab58 100644 --- a/api/v1alpha1/vector_types.go +++ b/api/v1alpha1/vector_types.go @@ -43,7 +43,6 @@ type VectorStatus struct { // VectorAgent is the Schema for the Vector Agent type VectorAgent struct { - // +kubebuilder:default:="timberio/vector:0.24.0-distroless-libc" // Image - docker image settings for Vector Agent // if no specified operator uses default config version // +optional diff --git a/config/crd/bases/observability.kaasops.io_vectors.yaml b/config/crd/bases/observability.kaasops.io_vectors.yaml index 1bc130b3..ac1bd5c9 100644 --- a/config/crd/bases/observability.kaasops.io_vectors.yaml +++ b/config/crd/bases/observability.kaasops.io_vectors.yaml @@ -1063,7 +1063,6 @@ spec: node network namespace type: boolean image: - default: timberio/vector:0.24.0-distroless-libc description: Image - docker image settings for Vector Agent if no specified operator uses default config version type: string diff --git a/controllers/factory/vector/vectoragent/vectoragent_default.go b/controllers/factory/vector/vectoragent/vectoragent_default.go index 4d0c80e5..96fd9061 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_default.go +++ b/controllers/factory/vector/vectoragent/vectoragent_default.go @@ -17,11 +17,15 @@ limitations under the License. package vectoragent import ( + "github.com/kaasops/vector-operator/api/v1alpha1" corev1 "k8s.io/api/core/v1" resourcev1 "k8s.io/apimachinery/pkg/api/resource" ) func (ctrl *Controller) SetDefault() { + if ctrl.Vector.Spec.Agent == nil { + ctrl.Vector.Spec.Agent = new(v1alpha1.VectorAgent) + } if ctrl.Vector.Spec.Agent.Image == "" { ctrl.Vector.Spec.Agent.Image = "timberio/vector:0.24.0-distroless-libc" } diff --git a/controllers/pipeline_controller.go b/controllers/pipeline_controller.go index b02b26a1..031f2aec 100644 --- a/controllers/pipeline_controller.go +++ b/controllers/pipeline_controller.go @@ -89,9 +89,8 @@ func (r *PipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c // Init Controller for Vector Agent vaCtrl := vectoragent.NewController(vector, r.Client, r.Clientset) - if vaCtrl.Vector.Spec.Agent.DataDir == "" { - vaCtrl.Vector.Spec.Agent.DataDir = "/vector-data-dir" - } + + vaCtrl.SetDefault() if err := config.ReconcileConfig(ctx, r.Client, pipelineCR, vaCtrl); err != nil { return ctrl.Result{}, err diff --git a/controllers/vector_controller.go b/controllers/vector_controller.go index 6ee4a5aa..4dc2d3c8 100644 --- a/controllers/vector_controller.go +++ b/controllers/vector_controller.go @@ -134,7 +134,6 @@ func (r *VectorReconciler) findVectorCustomResourceInstance(ctx context.Context, } return nil, err } - return vectorCR, nil } @@ -147,13 +146,6 @@ func reconcileVectors(ctx context.Context, client client.Client, clientset *kube if vector.DeletionTimestamp != nil { continue } - - // Init Controller for Vector Agent - vaCtrl := vectoragent.NewController(vector, client, clientset) - if vaCtrl.Vector.Spec.Agent.DataDir == "" { - vaCtrl.Vector.Spec.Agent.DataDir = "/vector-data-dir" - } - if _, err := createOrUpdateVector(ctx, client, clientset, vector, configOnly); err != nil { return ctrl.Result{}, err } @@ -201,10 +193,14 @@ func createOrUpdateVector(ctx context.Context, client client.Client, clientset * if err != nil { return ctrl.Result{}, err } - } vaCtrl.Config = byteConfig + // Start Reconcile Vector Agent + if err := vaCtrl.EnsureVectorAgent(ctx, configOnly); err != nil { + return ctrl.Result{}, err + } + if err := vaCtrl.SetLastAppliedPipelineStatus(ctx, &cfgHash); err != nil { //TODO: Handle err: Operation cannot be fulfilled on vectors.observability.kaasops.io \"vector-sample\": the object has been modified; please apply your changes to the latest version and try again if api_errors.IsConflict(err) { @@ -221,10 +217,5 @@ func createOrUpdateVector(ctx context.Context, client client.Client, clientset * return ctrl.Result{}, err } - // Start Reconcile Vector Agent - if err := vaCtrl.EnsureVectorAgent(ctx, configOnly); err != nil { - return ctrl.Result{}, err - } - return ctrl.Result{}, nil } From 627d4f29d6a7cb23d80eb54b6ac4883b9b20a42b Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Wed, 7 Dec 2022 15:21:13 +0200 Subject: [PATCH 154/316] fix panic on empty sinks and sources --- controllers/factory/config/config_build.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/controllers/factory/config/config_build.go b/controllers/factory/config/config_build.go index be158360..b1ae6274 100644 --- a/controllers/factory/config/config_build.go +++ b/controllers/factory/config/config_build.go @@ -181,6 +181,9 @@ func vectorConfigToByte(config *VectorConfig) ([]byte, error) { } func getSources(pipeline pipeline.Pipeline, filter []string) ([]*Source, error) { + if pipeline.GetSpec().Sources == nil { + return nil, nil + } var sources []*Source sourcesMap, err := decodeRaw(pipeline.GetSpec().Sources.Raw) if err != nil { @@ -229,6 +232,9 @@ func getTransforms(pipeline pipeline.Pipeline) ([]*Transform, error) { } func getSinks(pipeline pipeline.Pipeline) ([]*Sink, error) { + if pipeline.GetSpec().Sinks == nil { + return nil, nil + } sinksMap, err := decodeRaw(pipeline.GetSpec().Sinks.Raw) if err != nil { return nil, err From 49681a469d0b041c3a32227fbe799f48198fb5d2 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Wed, 7 Dec 2022 16:18:09 +0200 Subject: [PATCH 155/316] add vp to all category --- CHANGELOG.md | 2 ++ api/v1alpha1/vectorpipeline_types.go | 2 +- config/crd/bases/observability.kaasops.io_vectorpipelines.yaml | 2 ++ .../crds/observability.kaasops.io_vectorpipelines.yaml | 2 ++ 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e64ac33..5d0f9e51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ ### Added +- [[71]](https://github.com/kaasops/vector-operator/pull/71) **Fix** Fix panic on empty sinks and sources +- [[70]](https://github.com/kaasops/vector-operator/pull/70) **Fix** Fix nil vector panic - [[65](https://github.com/kaasops/vector-operator/pull/69)] **Helm**: Add toleration-control for vector-operator deployment to helm chart - [[65](https://github.com/kaasops/vector-operator/pull/69)] **Cleanup**: Add default value for podSecurityContext in chart values diff --git a/api/v1alpha1/vectorpipeline_types.go b/api/v1alpha1/vectorpipeline_types.go index 8365e525..4c689b4a 100644 --- a/api/v1alpha1/vectorpipeline_types.go +++ b/api/v1alpha1/vectorpipeline_types.go @@ -43,7 +43,7 @@ type VectorPipelineStatus struct { //+kubebuilder:object:root=true //+kubebuilder:subresource:status -//+kubebuilder:resource:shortName=vp +//+kubebuilder:resource:shortName=vp,categories=all //+kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" //+kubebuilder:printcolumn:name="Valid",type="boolean",JSONPath=".status.configCheckResult" diff --git a/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml b/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml index e40dd908..9861cbd1 100644 --- a/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml +++ b/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml @@ -9,6 +9,8 @@ metadata: spec: group: observability.kaasops.io names: + categories: + - all kind: VectorPipeline listKind: VectorPipelineList plural: vectorpipelines diff --git a/helm/charts/vector-operator/crds/observability.kaasops.io_vectorpipelines.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_vectorpipelines.yaml index e40dd908..9861cbd1 100644 --- a/helm/charts/vector-operator/crds/observability.kaasops.io_vectorpipelines.yaml +++ b/helm/charts/vector-operator/crds/observability.kaasops.io_vectorpipelines.yaml @@ -9,6 +9,8 @@ metadata: spec: group: observability.kaasops.io names: + categories: + - all kind: VectorPipeline listKind: VectorPipelineList plural: vectorpipelines From bec1d705ec948d8b2d5e31b26b574f1f2b1faa53 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Wed, 7 Dec 2022 16:21:34 +0200 Subject: [PATCH 156/316] prepare release v0.0.9 --- CHANGELOG.md | 3 +++ helm/charts/vector-operator/Chart.yaml | 4 ++-- helm/index.yaml | 23 ++++++++++++++++++----- helm/packages/vector-operator-0.0.9.tgz | Bin 0 -> 12192 bytes 4 files changed, 23 insertions(+), 7 deletions(-) create mode 100644 helm/packages/vector-operator-0.0.9.tgz diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d0f9e51..4faa79b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,7 @@ ### Added + +### v0.0.9 +- [[72]](https://github.com/kaasops/vector-operator/pull/72) **Feature** Add vp to all category - [[71]](https://github.com/kaasops/vector-operator/pull/71) **Fix** Fix panic on empty sinks and sources - [[70]](https://github.com/kaasops/vector-operator/pull/70) **Fix** Fix nil vector panic - [[65](https://github.com/kaasops/vector-operator/pull/69)] **Helm**: Add toleration-control for vector-operator deployment to helm chart diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index e7580e14..725a878b 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.8 +version: 0.0.9 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.0.8" +appVersion: "v0.0.9" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/index.yaml b/helm/index.yaml index 6c22f1b0..43097794 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.0.9 + created: "2022-12-07T16:21:23.988277182+02:00" + description: A Helm chart to install Vector Operator + digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.9.tgz + version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2022-12-02T15:36:12.752976791+02:00" + created: "2022-12-07T16:21:23.987759513+02:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2022-12-02T15:36:12.752470739+02:00" + created: "2022-12-07T16:21:23.980769185+02:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2022-12-02T15:36:12.751974518+02:00" + created: "2022-12-07T16:21:23.980285426+02:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2022-12-02T15:36:12.751485522+02:00" + created: "2022-12-07T16:21:23.979780268+02:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -53,4 +66,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2022-12-02T15:36:12.750776861+02:00" +generated: "2022-12-07T16:21:23.979201567+02:00" diff --git a/helm/packages/vector-operator-0.0.9.tgz b/helm/packages/vector-operator-0.0.9.tgz new file mode 100644 index 0000000000000000000000000000000000000000..7bf0d9b6c53c6d4a558017f613f50350ae1fb28f GIT binary patch literal 12192 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 PKKLY)g8-r(65{^=)n5#U literal 0 HcmV?d00001 From 6f84bd4ae3834eb2032082358f18fe5649055d65 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Thu, 15 Dec 2022 16:27:05 +0200 Subject: [PATCH 157/316] Add vector tolerations for configCheck pod Signed-off-by: Zemtsov Vladimir --- controllers/factory/config/config.go | 1 + .../factory/config/configcheck/configcheck.go | 27 ++++++++++--------- .../config/configcheck/configcheck_pod.go | 1 + controllers/vector_controller.go | 1 + 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/controllers/factory/config/config.go b/controllers/factory/config/config.go index 507425a1..20228c75 100644 --- a/controllers/factory/config/config.go +++ b/controllers/factory/config/config.go @@ -52,6 +52,7 @@ func ReconcileConfig(ctx context.Context, client client.Client, p pipeline.Pipel vaCtrl.Vector.Namespace, vaCtrl.Vector.Spec.Agent.Image, vaCtrl.Vector.Spec.Agent.Env, + vaCtrl.Vector.Spec.Agent.Tolerations, ) // Start ConfigCheck diff --git a/controllers/factory/config/configcheck/configcheck.go b/controllers/factory/config/configcheck/configcheck.go index d16873fd..96b8cd4c 100644 --- a/controllers/factory/config/configcheck/configcheck.go +++ b/controllers/factory/config/configcheck/configcheck.go @@ -38,11 +38,12 @@ type ConfigCheck struct { Client client.Client ClientSet *kubernetes.Clientset - Name string - Namespace string - Image string - Envs []corev1.EnvVar - Hash string + Name string + Namespace string + Image string + Envs []corev1.EnvVar + Hash string + Tolerations []corev1.Toleration } func New( @@ -51,15 +52,17 @@ func New( cs *kubernetes.Clientset, name, namespace, image string, envs []corev1.EnvVar, + tolerations []corev1.Toleration, ) *ConfigCheck { return &ConfigCheck{ - Config: config, - Client: c, - ClientSet: cs, - Name: name, - Namespace: namespace, - Image: image, - Envs: envs, + Config: config, + Client: c, + ClientSet: cs, + Name: name, + Namespace: namespace, + Image: image, + Envs: envs, + Tolerations: tolerations, } } diff --git a/controllers/factory/config/configcheck/configcheck_pod.go b/controllers/factory/config/configcheck/configcheck_pod.go index 47c74549..00ab95b0 100644 --- a/controllers/factory/config/configcheck/configcheck_pod.go +++ b/controllers/factory/config/configcheck/configcheck_pod.go @@ -35,6 +35,7 @@ func (cc *ConfigCheck) createVectorConfigCheckPod() *corev1.Pod { ServiceAccountName: "vector-configcheck", Volumes: cc.generateVectorConfigCheckVolume(), SecurityContext: &corev1.PodSecurityContext{}, + Tolerations: cc.Tolerations, Containers: []corev1.Container{ { Name: "config-check", diff --git a/controllers/vector_controller.go b/controllers/vector_controller.go index 4dc2d3c8..f90b4efb 100644 --- a/controllers/vector_controller.go +++ b/controllers/vector_controller.go @@ -182,6 +182,7 @@ func createOrUpdateVector(ctx context.Context, client client.Client, clientset * vaCtrl.Vector.Namespace, vaCtrl.Vector.Spec.Agent.Image, vaCtrl.Vector.Spec.Agent.Env, + vaCtrl.Vector.Spec.Agent.Tolerations, ) err := configCheck.Run(ctx) if errors.Is(err, configcheck.ValidationError) { From 61e63e76ba31755607ce917121376ebe4399600f Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Thu, 15 Dec 2022 16:28:50 +0200 Subject: [PATCH 158/316] update ConfigCheck Signed-off-by: Zemtsov Vladimir --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4faa79b8..8ee66003 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ### Added +### v0.0.10 +- [[73]](https://github.com/kaasops/vector-operator/pull/73) **Fix** Add vector tolerations for configCheck pod + ### v0.0.9 - [[72]](https://github.com/kaasops/vector-operator/pull/72) **Feature** Add vp to all category - [[71]](https://github.com/kaasops/vector-operator/pull/71) **Fix** Fix panic on empty sinks and sources From 6445b74104a9f839867d854f0ecff977a15f5065 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Thu, 15 Dec 2022 16:30:01 +0200 Subject: [PATCH 159/316] prepare helm for release Signed-off-by: Zemtsov Vladimir --- helm/charts/vector-operator/Chart.yaml | 4 ++-- helm/index.yaml | 25 +++++++++++++++++------ helm/packages/vector-operator-0.0.10.tgz | Bin 0 -> 12193 bytes 3 files changed, 21 insertions(+), 8 deletions(-) create mode 100644 helm/packages/vector-operator-0.0.10.tgz diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index 725a878b..93d72940 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.9 +version: 0.0.10 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.0.9" +appVersion: "v0.0.10" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/index.yaml b/helm/index.yaml index 43097794..cd1ea87a 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.0.10 + created: "2022-12-15T16:29:49.262088+02:00" + description: A Helm chart to install Vector Operator + digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.10.tgz + version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2022-12-07T16:21:23.988277182+02:00" + created: "2022-12-15T16:29:49.264843+02:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2022-12-07T16:21:23.987759513+02:00" + created: "2022-12-15T16:29:49.264108+02:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2022-12-07T16:21:23.980769185+02:00" + created: "2022-12-15T16:29:49.263485+02:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2022-12-07T16:21:23.980285426+02:00" + created: "2022-12-15T16:29:49.262854+02:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2022-12-07T16:21:23.979780268+02:00" + created: "2022-12-15T16:29:49.261413+02:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -66,4 +79,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2022-12-07T16:21:23.979201567+02:00" +generated: "2022-12-15T16:29:49.260393+02:00" diff --git a/helm/packages/vector-operator-0.0.10.tgz b/helm/packages/vector-operator-0.0.10.tgz new file mode 100644 index 0000000000000000000000000000000000000000..1219d11005dcd49e202aa73e3e2e835e9fb7700f GIT binary patch literal 12193 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 OU_?WVdWd#Ni2n!I2K%T0 literal 0 HcmV?d00001 From 2ae3120e08f983d4c8c73473be7394ebbdc88781 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Wed, 28 Dec 2022 16:00:07 +0200 Subject: [PATCH 160/316] concurrent pipeline checks --- .../observability_v1alpha1_vector.yaml | 2 +- controllers/factory/config/config.go | 59 ----------- .../factory/config/configcheck/configcheck.go | 99 +++++++++++-------- .../factory/config/configcheck/const.go | 6 ++ .../vector/vectoragent/vectoragent_default.go | 2 +- controllers/pipeline_controller.go | 88 +++++++++++++++-- controllers/vector_controller.go | 33 +++++-- main.go | 24 +++-- 8 files changed, 194 insertions(+), 119 deletions(-) create mode 100644 controllers/factory/config/configcheck/const.go diff --git a/config/samples/observability_v1alpha1_vector.yaml b/config/samples/observability_v1alpha1_vector.yaml index 3a7845c3..ae06f0b3 100644 --- a/config/samples/observability_v1alpha1_vector.yaml +++ b/config/samples/observability_v1alpha1_vector.yaml @@ -6,5 +6,5 @@ metadata: spec: agent: service: true - image: "timberio/vector:0.24.0-distroless-libc" + image: "timberio/vector:0.26.0-distroless-libc" diff --git a/controllers/factory/config/config.go b/controllers/factory/config/config.go index 20228c75..fe12b1a4 100644 --- a/controllers/factory/config/config.go +++ b/controllers/factory/config/config.go @@ -17,69 +17,10 @@ limitations under the License. package config import ( - "context" - "errors" - vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" - "github.com/kaasops/vector-operator/controllers/factory/config/configcheck" - "github.com/kaasops/vector-operator/controllers/factory/pipeline" - "github.com/kaasops/vector-operator/controllers/factory/vector/vectoragent" "github.com/mitchellh/mapstructure" - "sigs.k8s.io/controller-runtime/pkg/client" ) -func ReconcileConfig(ctx context.Context, client client.Client, p pipeline.Pipeline, vaCtrl *vectoragent.Controller) error { - // Get Vector Config file - configBuilder := NewBuilder(vaCtrl, p) - - byteConfig, err := configBuilder.GetByteConfigWithValidate() - if err != nil { - if err := pipeline.SetFailedStatus(ctx, client, p, err); err != nil { - return err - } - if err = pipeline.SetLastAppliedPipelineStatus(ctx, client, p); err != nil { - return err - } - return nil - } - - // Init CheckConfig - configCheck := configcheck.New( - byteConfig, - vaCtrl.Client, - vaCtrl.ClientSet, - vaCtrl.Vector.Name, - vaCtrl.Vector.Namespace, - vaCtrl.Vector.Spec.Agent.Image, - vaCtrl.Vector.Spec.Agent.Env, - vaCtrl.Vector.Spec.Agent.Tolerations, - ) - - // Start ConfigCheck - err = configCheck.Run(ctx) - if errors.Is(err, configcheck.ValidationError) { - if err = pipeline.SetFailedStatus(ctx, client, p, err); err != nil { - return err - } - if err = pipeline.SetLastAppliedPipelineStatus(ctx, client, p); err != nil { - return err - } - return nil - } - if err != nil { - return err - } - - if err = pipeline.SetSuccessStatus(ctx, client, p); err != nil { - return err - } - - if err = pipeline.SetLastAppliedPipelineStatus(ctx, client, p); err != nil { - return err - } - return nil -} - func New(vector *vectorv1alpha1.Vector) *VectorConfig { sources := []*Source{} sinks := []*Sink{} diff --git a/controllers/factory/config/configcheck/configcheck.go b/controllers/factory/config/configcheck/configcheck.go index 96b8cd4c..a0ebe2af 100644 --- a/controllers/factory/config/configcheck/configcheck.go +++ b/controllers/factory/config/configcheck/configcheck.go @@ -19,14 +19,15 @@ package configcheck import ( "context" "math/rand" - "time" "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" - apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/kubernetes" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" @@ -40,6 +41,7 @@ type ConfigCheck struct { Name string Namespace string + Initiator string Image string Envs []corev1.EnvVar Hash string @@ -67,9 +69,8 @@ func New( } func (cc *ConfigCheck) Run(ctx context.Context) error { - log := log.FromContext(ctx).WithValues("Vector ConfigCheck", cc.Name) - - log.Info("start ConfigCheck") + log := log.FromContext(ctx).WithValues("Vector ConfigCheck", cc.Initiator) + log.Info("================= Started ConfigCheck =======================") if err := cc.ensureVectorConfigCheckRBAC(ctx); err != nil { return err @@ -154,27 +155,49 @@ func randStringRunes() string { func (cc *ConfigCheck) getCheckResult(ctx context.Context, pod *corev1.Pod) (err error) { log := log.FromContext(ctx).WithValues("Vector ConfigCheck", pod.Name) log.Info("Trying to get configcheck result") + + watcher, err := cc.ClientSet.CoreV1().Pods(cc.Namespace).Watch(ctx, metav1.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(metav1.ObjectNameField, pod.Name).String(), + // LabelSelector: labelsForVectorConfigCheck(), + }) + + if err != nil { + log.Error(err, "cannot create Pod event watcher") + return err + } + + defer watcher.Stop() + for { - pod, err := k8s.FetchPod(ctx, pod, cc.Client) - if err != nil { - if apierrors.IsNotFound(err) { + select { + case e := <-watcher.ResultChan(): + if e.Object == nil { + return nil + } + pod, ok := e.Object.(*corev1.Pod) + if !ok { continue } - return err - } - - switch pod.Status.Phase { - case "Pending": - log.Info("wait Validate Vector Config Result") - time.Sleep(5 * time.Second) - case "Failed": - reason, err := k8s.GetPodLogs(ctx, pod, cc.ClientSet) - if err != nil { - return err + switch e.Type { + case watch.Modified: + if pod.DeletionTimestamp != nil { + continue + } + switch pod.Status.Phase { + case corev1.PodSucceeded: + log.Info("Config Check completed successfully") + return nil + case corev1.PodFailed: + log.Info("Config Check Failed") + reason, err := k8s.GetPodLogs(ctx, pod, cc.ClientSet) + if err != nil { + return err + } + return newValidationError(reason) + } } - return newValidationError(reason) - case "Succeeded": - log.Info("Config Check completed successfully") + case <-ctx.Done(): + watcher.Stop() return nil } } @@ -188,25 +211,23 @@ func (cc *ConfigCheck) cleanup(ctx context.Context, pod *corev1.Pod) error { } return err } - if pod.Status.Phase == "Succeeded" { - for _, v := range pod.Spec.Volumes { - if v.Name == "config" { - nn := types.NamespacedName{ - Name: v.Secret.SecretName, - Namespace: pod.Namespace, - } - secret, err := k8s.GetSecret(ctx, nn, cc.Client) - if err != nil { - return err - } - if err := k8s.DeleteSecret(ctx, secret, cc.Client); err != nil { - return err - } + for _, v := range pod.Spec.Volumes { + if v.Name == "config" { + nn := types.NamespacedName{ + Name: v.Secret.SecretName, + Namespace: pod.Namespace, + } + secret, err := k8s.GetSecret(ctx, nn, cc.Client) + if err != nil { + return err + } + if err := k8s.DeleteSecret(ctx, secret, cc.Client); err != nil { + return err } } - if err := k8s.DeletePod(ctx, pod, cc.Client); err != nil { - return err - } + } + if err := k8s.DeletePod(ctx, pod, cc.Client); err != nil { + return err } return nil } diff --git a/controllers/factory/config/configcheck/const.go b/controllers/factory/config/configcheck/const.go new file mode 100644 index 00000000..b51e6ded --- /dev/null +++ b/controllers/factory/config/configcheck/const.go @@ -0,0 +1,6 @@ +package configcheck + +const ( + ConfigCheckInitiatorVector = "VectorInitiator" + ConfigCheckInitiatorPipieline = "PipelineInitiator" +) diff --git a/controllers/factory/vector/vectoragent/vectoragent_default.go b/controllers/factory/vector/vectoragent/vectoragent_default.go index 96fd9061..56609408 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_default.go +++ b/controllers/factory/vector/vectoragent/vectoragent_default.go @@ -27,7 +27,7 @@ func (ctrl *Controller) SetDefault() { ctrl.Vector.Spec.Agent = new(v1alpha1.VectorAgent) } if ctrl.Vector.Spec.Agent.Image == "" { - ctrl.Vector.Spec.Agent.Image = "timberio/vector:0.24.0-distroless-libc" + ctrl.Vector.Spec.Agent.Image = "timberio/vector:0.26.0-distroless-libc" } if ctrl.Vector.Spec.Agent.Resources.Requests == nil { diff --git a/controllers/pipeline_controller.go b/controllers/pipeline_controller.go index 031f2aec..1f14f434 100644 --- a/controllers/pipeline_controller.go +++ b/controllers/pipeline_controller.go @@ -18,6 +18,9 @@ package controllers import ( "context" + "errors" + "sync" + "time" api_errors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" @@ -27,10 +30,12 @@ import ( "sigs.k8s.io/controller-runtime/pkg/event" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/source" vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" "github.com/kaasops/vector-operator/controllers/factory/config" + "github.com/kaasops/vector-operator/controllers/factory/config/configcheck" "github.com/kaasops/vector-operator/controllers/factory/pipeline" "github.com/kaasops/vector-operator/controllers/factory/vector/vectoragent" ) @@ -40,9 +45,15 @@ type PipelineReconciler struct { Scheme *runtime.Scheme // Temp. Wait this issue - https://github.com/kubernetes-sigs/controller-runtime/issues/452 - Clientset *kubernetes.Clientset + Clientset *kubernetes.Clientset + PipelineCheckWG *sync.WaitGroup + PipelineDeleteEventTimeout time.Duration } +var VectorAgentReconciliationSourceChannel = make(chan event.GenericEvent) + +const PipelineDeleteEventTimeout time.Duration = 3 * time.Second + func (r *PipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log := log.FromContext(ctx).WithValues("Pipeline", req.Name) @@ -67,7 +78,13 @@ func (r *PipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c if pipelineCR == nil { log.Info("Pipeline CR not found. Ignoring since object must be deleted") for _, vector := range vectorInstances { - VectorAgentReconciliationSourceChannel <- event.GenericEvent{Object: vector} + r.PipelineCheckWG.Add(1) + go func() { + VectorAgentReconciliationSourceChannel <- event.GenericEvent{Object: vector} + log.Info("Waiting if other deletions will be done during timeout") + time.Sleep(r.PipelineDeleteEventTimeout) + r.PipelineCheckWG.Done() + }() return ctrl.Result{}, nil } } @@ -91,15 +108,23 @@ func (r *PipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c vaCtrl := vectoragent.NewController(vector, r.Client, r.Clientset) vaCtrl.SetDefault() + // Get Vector Config file + configBuilder := config.NewBuilder(vaCtrl, pipelineCR) - if err := config.ReconcileConfig(ctx, r.Client, pipelineCR, vaCtrl); err != nil { + byteConfig, err := configBuilder.GetByteConfigWithValidate() + if err != nil { + if err := pipeline.SetFailedStatus(ctx, r.Client, pipelineCR, err); err != nil { + return ctrl.Result{}, err + } + if err = pipeline.SetLastAppliedPipelineStatus(ctx, r.Client, pipelineCR); err != nil { + return ctrl.Result{}, err + } return ctrl.Result{}, err } - // Start vector reconcilation - if *pipelineCR.GetConfigCheckResult() { - VectorAgentReconciliationSourceChannel <- event.GenericEvent{Object: vector} - } + vaCtrl.Config = byteConfig + r.PipelineCheckWG.Add(1) + go r.runPipelineCheck(ctx, pipelineCR, vaCtrl) } log.Info("finish Reconcile Pipeline") @@ -135,5 +160,54 @@ func (r *PipelineReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&vectorv1alpha1.VectorPipeline{}). Watches(&source.Kind{Type: &vectorv1alpha1.ClusterVectorPipeline{}}, &handler.EnqueueRequestForObject{}). + WithEventFilter(predicate.GenerationChangedPredicate{}). Complete(r) } + +func (r *PipelineReconciler) runPipelineCheck(ctx context.Context, p pipeline.Pipeline, vaCtrl *vectoragent.Controller) { + log := log.FromContext(ctx).WithValues("Pipeline", p.GetName()) + // Init CheckConfig + configCheck := configcheck.New( + vaCtrl.Config, + vaCtrl.Client, + vaCtrl.ClientSet, + vaCtrl.Vector.Name, + vaCtrl.Vector.Namespace, + vaCtrl.Vector.Spec.Agent.Image, + vaCtrl.Vector.Spec.Agent.Env, + vaCtrl.Vector.Spec.Agent.Tolerations, + ) + configCheck.Initiator = configcheck.ConfigCheckInitiatorPipieline + defer r.PipelineCheckWG.Done() + // Start ConfigCheck + err := configCheck.Run(ctx) + if errors.Is(err, configcheck.ValidationError) { + if err = pipeline.SetFailedStatus(ctx, r.Client, p, err); err != nil { + log.Error(err, "Failed to set pipeline status") + return + } + if err = pipeline.SetLastAppliedPipelineStatus(ctx, r.Client, p); err != nil { + log.Error(err, "Failed to set pipeline status") + return + } + return + } + if err != nil { + log.Error(err, "Configcheck error") + return + } + + if err = pipeline.SetSuccessStatus(ctx, r.Client, p); err != nil { + log.Error(err, "Failed to set pipeline status") + return + } + + if err = pipeline.SetLastAppliedPipelineStatus(ctx, r.Client, p); err != nil { + log.Error(err, "Failed to set pipeline status") + return + } + // Start vector reconcilation + if *p.GetConfigCheckResult() { + VectorAgentReconciliationSourceChannel <- event.GenericEvent{Object: vaCtrl.Vector} + } +} diff --git a/controllers/vector_controller.go b/controllers/vector_controller.go index f90b4efb..98262d73 100644 --- a/controllers/vector_controller.go +++ b/controllers/vector_controller.go @@ -19,6 +19,8 @@ package controllers import ( "context" "errors" + "sync" + "time" "github.com/kaasops/vector-operator/controllers/factory/config" "github.com/kaasops/vector-operator/controllers/factory/config/configcheck" @@ -37,7 +39,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/predicate" @@ -52,7 +54,9 @@ type VectorReconciler struct { Scheme *runtime.Scheme // Temp. Wait this issue - https://github.com/kubernetes-sigs/controller-runtime/issues/452 - Clientset *kubernetes.Clientset + Clientset *kubernetes.Clientset + PipelineCheckWG *sync.WaitGroup + PipelineCheckTimeout time.Duration } //+kubebuilder:rbac:groups=observability.kaasops.io,resources=vectors,verbs=get;list;watch;create;update;patch;delete @@ -69,13 +73,14 @@ type VectorReconciler struct { // For more details, check Reconcile and its Result here: // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.12.2/pkg/reconcile -var VectorAgentReconciliationSourceChannel = make(chan event.GenericEvent) - func (r *VectorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { _ = log.FromContext(ctx) log := log.FromContext(ctx).WithValues("Vector", req.NamespacedName) - log.Info("start Reconcile Vector") - + log.Info("Waiting pipeline checks") + if waitPipelineChecks(r.PipelineCheckWG, r.PipelineCheckTimeout) { + log.Info("Timeout waiting pipeline checks, continue reconcile vector") + } + log.Info("Start Reconcile Vector") if req.Namespace == "" { vectors, err := listVectorCustomResourceInstances(ctx, r.Client) if err != nil { @@ -109,6 +114,7 @@ func (r *VectorReconciler) SetupWithManager(mgr ctrl.Manager) error { Owns(&corev1.ServiceAccount{}). Owns(&rbacv1.ClusterRole{}). Owns(&rbacv1.ClusterRoleBinding{}). + WithOptions(controller.Options{MaxConcurrentReconciles: 10}). Complete(r) } @@ -184,6 +190,7 @@ func createOrUpdateVector(ctx context.Context, client client.Client, clientset * vaCtrl.Vector.Spec.Agent.Env, vaCtrl.Vector.Spec.Agent.Tolerations, ) + configCheck.Initiator = configcheck.ConfigCheckInitiatorVector err := configCheck.Run(ctx) if errors.Is(err, configcheck.ValidationError) { if err := vaCtrl.SetFailedStatus(ctx, err); err != nil { @@ -220,3 +227,17 @@ func createOrUpdateVector(ctx context.Context, client client.Client, clientset * return ctrl.Result{}, nil } + +func waitPipelineChecks(wg *sync.WaitGroup, timeout time.Duration) bool { + c := make(chan struct{}) + go func() { + defer close(c) + wg.Wait() + }() + select { + case <-c: + return false + case <-time.After(timeout): + return true + } +} diff --git a/main.go b/main.go index aa6255ff..ce99ac83 100644 --- a/main.go +++ b/main.go @@ -19,6 +19,8 @@ package main import ( "flag" "os" + "sync" + "time" // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) // to ensure that exec-entrypoint and run can make use of them. @@ -62,6 +64,10 @@ func main() { var probeAddr string var namespace string var watchLabel string + var pipelineCheckWG sync.WaitGroup + var PipelineCheckTimeout time.Duration + var PipelineDeleteEventTimeout time.Duration + flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.") flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") flag.BoolVar(&enableLeaderElection, "leader-elect", false, @@ -69,6 +75,8 @@ func main() { "Enabling this will ensure there is only one active controller manager.") flag.StringVar(&namespace, "watch-namespace", "", "Namespace to filter the list of watched objects") flag.StringVar(&watchLabel, "watch-name", "", "Filter the list of watched objects by checking the app.kubernetes.io/managed-by label") + flag.DurationVar(&PipelineCheckTimeout, "pipeline-check-timeout", 15*time.Second, "wait pipeline checks before force vector reconcile. Default: 15s") + flag.DurationVar(&PipelineDeleteEventTimeout, "pipeline-delete-timeout", 5*time.Second, "collect delete events timeout") opts := zap.Options{ Development: true, } @@ -106,17 +114,21 @@ func main() { } if err = (&controllers.VectorReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Clientset: clientset, + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Clientset: clientset, + PipelineCheckWG: &pipelineCheckWG, + PipelineCheckTimeout: PipelineCheckTimeout, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Vector") os.Exit(1) } if err = (&controllers.PipelineReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Clientset: clientset, + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Clientset: clientset, + PipelineCheckWG: &pipelineCheckWG, + PipelineDeleteEventTimeout: PipelineDeleteEventTimeout, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "VectorPipeline") os.Exit(1) From 0b8654d4e8d2906d90f63a02dda2dc436e976436 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Mon, 9 Jan 2023 13:50:32 +0200 Subject: [PATCH 161/316] add timeout to wait configcheck pod result --- controllers/factory/config/configcheck/configcheck.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/controllers/factory/config/configcheck/configcheck.go b/controllers/factory/config/configcheck/configcheck.go index a0ebe2af..adf01c3f 100644 --- a/controllers/factory/config/configcheck/configcheck.go +++ b/controllers/factory/config/configcheck/configcheck.go @@ -18,11 +18,13 @@ package configcheck import ( "context" + "errors" "math/rand" + "time" "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" + api_errors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/labels" @@ -33,6 +35,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" ) +const waitConfigcheckResultTimeout = 180 * time.Second + type ConfigCheck struct { Config []byte @@ -199,6 +203,9 @@ func (cc *ConfigCheck) getCheckResult(ctx context.Context, pod *corev1.Pod) (err case <-ctx.Done(): watcher.Stop() return nil + case <-time.After(waitConfigcheckResultTimeout): + watcher.Stop() + return errors.New("Timeout waiting configcheck pod result") } } } @@ -206,7 +213,7 @@ func (cc *ConfigCheck) getCheckResult(ctx context.Context, pod *corev1.Pod) (err func (cc *ConfigCheck) cleanup(ctx context.Context, pod *corev1.Pod) error { pod, err := k8s.FetchPod(ctx, pod, cc.Client) if err != nil { - if errors.IsNotFound(err) { + if api_errors.IsNotFound(err) { return nil } return err From f73f322c1f19733a0b11dbde135cf8eaeb8e2b48 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Mon, 9 Jan 2023 15:38:56 +0200 Subject: [PATCH 162/316] cleanup if configcheck timeout --- controllers/factory/config/configcheck/configcheck.go | 6 ++++-- controllers/factory/config/configcheck/configcheck_error.go | 5 ++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/controllers/factory/config/configcheck/configcheck.go b/controllers/factory/config/configcheck/configcheck.go index adf01c3f..8e2077dc 100644 --- a/controllers/factory/config/configcheck/configcheck.go +++ b/controllers/factory/config/configcheck/configcheck.go @@ -18,7 +18,6 @@ package configcheck import ( "context" - "errors" "math/rand" "time" @@ -205,7 +204,10 @@ func (cc *ConfigCheck) getCheckResult(ctx context.Context, pod *corev1.Pod) (err return nil case <-time.After(waitConfigcheckResultTimeout): watcher.Stop() - return errors.New("Timeout waiting configcheck pod result") + if err = cc.cleanup(ctx, pod); err != nil { + log.Error(err, "Failed to clean configcheck pod") + } + return ConfigcheckTimeoutError } } } diff --git a/controllers/factory/config/configcheck/configcheck_error.go b/controllers/factory/config/configcheck/configcheck_error.go index 3a457466..dfe28c76 100644 --- a/controllers/factory/config/configcheck/configcheck_error.go +++ b/controllers/factory/config/configcheck/configcheck_error.go @@ -21,7 +21,10 @@ import ( "fmt" ) -var ValidationError = errors.New("config validation error") +var ( + ValidationError = errors.New("config validation error") + ConfigcheckTimeoutError = errors.New("timeout waiting configcheck pod result") +) func newValidationError(reason string) error { return fmt.Errorf("%w: %s", ValidationError, reason) From ba73260685c5854b4e6cc8cd12483695efbf7af7 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Tue, 10 Jan 2023 13:29:35 +0200 Subject: [PATCH 163/316] remove vector.dev/exclude label --- controllers/factory/config/configcheck/configcheck.go | 7 +++---- .../factory/vector/vectoragent/vectoragent_controller.go | 9 ++++----- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/controllers/factory/config/configcheck/configcheck.go b/controllers/factory/config/configcheck/configcheck.go index 8e2077dc..334cf2a3 100644 --- a/controllers/factory/config/configcheck/configcheck.go +++ b/controllers/factory/config/configcheck/configcheck.go @@ -132,10 +132,9 @@ func (cc *ConfigCheck) checkVectorConfigCheckPod(ctx context.Context) error { func labelsForVectorConfigCheck() map[string]string { return map[string]string{ - k8s.ManagedByLabelKey: "vector-operator", - k8s.NameLabelKey: "vector-configcheck", - k8s.ComponentLabelKey: "ConfigCheck", - k8s.VectorExcludeLabel: "true", + k8s.ManagedByLabelKey: "vector-operator", + k8s.NameLabelKey: "vector-configcheck", + k8s.ComponentLabelKey: "ConfigCheck", } } diff --git a/controllers/factory/vector/vectoragent/vectoragent_controller.go b/controllers/factory/vector/vectoragent/vectoragent_controller.go index 19f82ef9..2790a64c 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_controller.go +++ b/controllers/factory/vector/vectoragent/vectoragent_controller.go @@ -121,11 +121,10 @@ func (ctrl *Controller) ensureVectorAgentDaemonSet(ctx context.Context) error { func (ctrl *Controller) labelsForVectorAgent() map[string]string { return map[string]string{ - k8s.ManagedByLabelKey: "vector-operator", - k8s.NameLabelKey: "vector", - k8s.ComponentLabelKey: "Agent", - k8s.InstanceLabelKey: ctrl.Vector.Name, - k8s.VectorExcludeLabel: "true", + k8s.ManagedByLabelKey: "vector-operator", + k8s.NameLabelKey: "vector", + k8s.ComponentLabelKey: "Agent", + k8s.InstanceLabelKey: ctrl.Vector.Name, } } From 66600ff2259eff436de28a32ae49317a6c08b487 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Tue, 10 Jan 2023 13:45:14 +0200 Subject: [PATCH 164/316] prepare release v0.0.11 --- CHANGELOG.md | 4 ++++ helm/charts/vector-operator/Chart.yaml | 4 ++-- helm/index.yaml | 27 +++++++++++++++++------ helm/packages/vector-operator-0.0.11.tgz | Bin 0 -> 12195 bytes 4 files changed, 26 insertions(+), 9 deletions(-) create mode 100644 helm/packages/vector-operator-0.0.11.tgz diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ee66003..d44b2bd4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ### Added +### v0.0.11 +- [[79]] (https://github.com/kaasops/vector-operator/pull/79) **Fix** Remove vector.dev/exclude label +- [[77]](https://github.com/kaasops/vector-operator/pull/77) **Feature** Concurrent pipeline checks + ### v0.0.10 - [[73]](https://github.com/kaasops/vector-operator/pull/73) **Fix** Add vector tolerations for configCheck pod diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index 93d72940..a3133aae 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.10 +version: 0.0.11 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.0.10" +appVersion: "v0.0.11" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/index.yaml b/helm/index.yaml index cd1ea87a..a939ada4 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.0.11 + created: "2023-01-10T13:44:40.180860293+02:00" + description: A Helm chart to install Vector Operator + digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.11.tgz + version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2022-12-15T16:29:49.262088+02:00" + created: "2023-01-10T13:44:40.179929316+02:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2022-12-15T16:29:49.264843+02:00" + created: "2023-01-10T13:44:40.182875831+02:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2022-12-15T16:29:49.264108+02:00" + created: "2023-01-10T13:44:40.18235856+02:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2022-12-15T16:29:49.263485+02:00" + created: "2023-01-10T13:44:40.181873014+02:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2022-12-15T16:29:49.262854+02:00" + created: "2023-01-10T13:44:40.181372514+02:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2022-12-15T16:29:49.261413+02:00" + created: "2023-01-10T13:44:40.179419447+02:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -79,4 +92,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2022-12-15T16:29:49.260393+02:00" +generated: "2023-01-10T13:44:40.178800454+02:00" diff --git a/helm/packages/vector-operator-0.0.11.tgz b/helm/packages/vector-operator-0.0.11.tgz new file mode 100644 index 0000000000000000000000000000000000000000..50f6621395fa176685b1f10be5d69eba666ec450 GIT binary patch literal 12195 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 literal 0 HcmV?d00001 From cb84f9d309b44dca6c4b182e45e1610197a7ec61 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Tue, 10 Jan 2023 15:30:38 +0200 Subject: [PATCH 165/316] fix changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d44b2bd4..d6ff0431 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ ### Added ### v0.0.11 -- [[79]] (https://github.com/kaasops/vector-operator/pull/79) **Fix** Remove vector.dev/exclude label +- [[79]](https://github.com/kaasops/vector-operator/pull/79) **Fix** Remove vector.dev/exclude label - [[77]](https://github.com/kaasops/vector-operator/pull/77) **Feature** Concurrent pipeline checks ### v0.0.10 From 52a5103773447d2fd63bd82b0ee13207a9d5d5c0 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Thu, 12 Jan 2023 13:59:04 +0200 Subject: [PATCH 166/316] Add control for configcheck params Signed-off-by: Zemtsov Vladimir --- api/v1alpha1/vector_types.go | 26 + api/v1alpha1/zz_generated.deepcopy.go | 42 + .../observability.kaasops.io_vectors.yaml | 982 ++++++++++++++++++ .../factory/config/configcheck/configcheck.go | 31 +- controllers/pipeline_controller.go | 6 +- controllers/vector_controller.go | 6 +- 6 files changed, 1077 insertions(+), 16 deletions(-) diff --git a/api/v1alpha1/vector_types.go b/api/v1alpha1/vector_types.go index c522ab58..1b0edcf7 100644 --- a/api/v1alpha1/vector_types.go +++ b/api/v1alpha1/vector_types.go @@ -96,6 +96,8 @@ type VectorAgent struct { DataDir string `json:"dataDir,omitempty"` Api ApiSpec `json:"api,omitempty"` Service bool `json:"service,omitempty"` + + ConfigCheck ConfigCheck `json:"configCheck,omitempty"` } // ApiSpec is the Schema for the Vector Agent GraphQL API - https://vector.dev/docs/reference/api/ @@ -105,6 +107,30 @@ type ApiSpec struct { Playground bool `json:"playground,omitempty"` } +// ConfigCheck is the Schema for control params for ConfigCheck pods +type ConfigCheck struct { + // Image - docker image settings for Vector Agent + // if no specified operator uses default config version + // +optional + Image *string `json:"image,omitempty"` + // Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + // if not specified - default setting will be used + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Resources",xDescriptors="urn:alm:descriptor:com.tectonic.ui:resourceRequirements" + // +optional + Resources *v1.ResourceRequirements `json:"resources,omitempty"` + // Affinity If specified, the pod's scheduling constraints. + // +optional + Affinity *v1.Affinity `json:"affinity,omitempty"` + // Tolerations If specified, the pod's tolerations. + // +optional + Tolerations *[]v1.Toleration `json:"tolerations,omitempty"` + // SecurityContext holds pod-level security attributes and common container settings. + // This defaults to the default PodSecurityContext. + // +optional + // Tolerations If specified, the pod's tolerations. + // +optional +} + // VectorAggregator is the Schema for the Vector Aggregator type VectorAggregator struct { Enable bool `json:"enable,omitempty"` diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 52a62c40..01b9c888 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -100,6 +100,47 @@ func (in *ClusterVectorPipelineList) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigCheck) DeepCopyInto(out *ConfigCheck) { + *out = *in + if in.Image != nil { + in, out := &in.Image, &out.Image + *out = new(string) + **out = **in + } + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = new(v1.ResourceRequirements) + (*in).DeepCopyInto(*out) + } + if in.Affinity != nil { + in, out := &in.Affinity, &out.Affinity + *out = new(v1.Affinity) + (*in).DeepCopyInto(*out) + } + if in.Tolerations != nil { + in, out := &in.Tolerations, &out.Tolerations + *out = new([]v1.Toleration) + if **in != nil { + in, out := *in, *out + *out = make([]v1.Toleration, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigCheck. +func (in *ConfigCheck) DeepCopy() *ConfigCheck { + if in == nil { + return nil + } + out := new(ConfigCheck) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Vector) DeepCopyInto(out *Vector) { *out = *in @@ -173,6 +214,7 @@ func (in *VectorAgent) DeepCopyInto(out *VectorAgent) { } } out.Api = in.Api + in.ConfigCheck.DeepCopyInto(&out.ConfigCheck) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VectorAgent. diff --git a/config/crd/bases/observability.kaasops.io_vectors.yaml b/config/crd/bases/observability.kaasops.io_vectors.yaml index ac1bd5c9..f59668bb 100644 --- a/config/crd/bases/observability.kaasops.io_vectors.yaml +++ b/config/crd/bases/observability.kaasops.io_vectors.yaml @@ -925,6 +925,988 @@ spec: playground: type: boolean type: object + configCheck: + description: ConfigCheck is the Schema for control params for + ConfigCheck pods + properties: + affinity: + description: Affinity If specified, the pod's scheduling constraints. + properties: + nodeAffinity: + description: Describes node affinity scheduling rules + for the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule + pods to nodes that satisfy the affinity expressions + specified by this field, but it may choose a node + that violates one or more of the expressions. The + node that is most preferred is the one with the + greatest sum of weights, i.e. for each node that + meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, + etc.), compute a sum by iterating through the elements + of this field and adding "weight" to the sum if + the node matches the corresponding matchExpressions; + the node(s) with the highest sum are the most preferred. + items: + description: An empty preferred scheduling term + matches all objects with implicit weight 0 (i.e. + it's a no-op). A null preferred scheduling term + matches no objects (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated + with the corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: The label key that the + selector applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + If the operator is Gt or Lt, the + values array must have a single + element, which will be interpreted + as an integer. This array is replaced + during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: The label key that the + selector applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + If the operator is Gt or Lt, the + values array must have a single + element, which will be interpreted + as an integer. This array is replaced + during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + x-kubernetes-map-type: atomic + weight: + description: Weight associated with matching + the corresponding nodeSelectorTerm, in the + range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified + by this field are not met at scheduling time, the + pod will not be scheduled onto the node. If the + affinity requirements specified by this field cease + to be met at some point during pod execution (e.g. + due to an update), the system may or may not try + to eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector + terms. The terms are ORed. + items: + description: A null or empty node selector term + matches no objects. The requirements of them + are ANDed. The TopologySelectorTerm type implements + a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: The label key that the + selector applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + If the operator is Gt or Lt, the + values array must have a single + element, which will be interpreted + as an integer. This array is replaced + during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: The label key that the + selector applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + If the operator is Gt or Lt, the + values array must have a single + element, which will be interpreted + as an integer. This array is replaced + during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + x-kubernetes-map-type: atomic + type: array + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. + co-locate this pod in the same node, zone, etc. as some + other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule + pods to nodes that satisfy the affinity expressions + specified by this field, but it may choose a node + that violates one or more of the expressions. The + node that is most preferred is the one with the + greatest sum of weights, i.e. for each node that + meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, + etc.), compute a sum by iterating through the elements + of this field and adding "weight" to the sum if + the node has pods which matches the corresponding + podAffinityTerm; the node(s) with the highest sum + are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, + associated with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of + resources, in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The + requirements are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label + key that the selector applies + to. + type: string + operator: + description: operator represents + a key's relationship to a set + of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array + of string values. If the operator + is In or NotIn, the values array + must be non-empty. If the operator + is Exists or DoesNotExist, the + values array must be empty. + This array is replaced during + a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of + {key,value} pairs. A single {key,value} + in the matchLabels map is equivalent + to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are + ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set + of namespaces that the term applies to. + The term is applied to the union of the + namespaces selected by this field and + the ones listed in the namespaces field. + null selector and null or empty namespaces + list means "this pod's namespace". An + empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The + requirements are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label + key that the selector applies + to. + type: string + operator: + description: operator represents + a key's relationship to a set + of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array + of string values. If the operator + is In or NotIn, the values array + must be non-empty. If the operator + is Exists or DoesNotExist, the + values array must be empty. + This array is replaced during + a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of + {key,value} pairs. A single {key,value} + in the matchLabels map is equivalent + to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are + ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static + list of namespace names that the term + applies to. The term is applied to the + union of the namespaces listed in this + field and the ones selected by namespaceSelector. + null or empty namespaces list and null + namespaceSelector means "this pod's namespace". + items: + type: string + type: array + topologyKey: + description: This pod should be co-located + (affinity) or not co-located (anti-affinity) + with the pods matching the labelSelector + in the specified namespaces, where co-located + is defined as running on a node whose + value of the label with key topologyKey + matches that of any node on which any + of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching + the corresponding podAffinityTerm, in the + range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified + by this field are not met at scheduling time, the + pod will not be scheduled onto the node. If the + affinity requirements specified by this field cease + to be met at some point during pod execution (e.g. + due to a pod label update), the system may or may + not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes + corresponding to each podAffinityTerm are intersected, + i.e. all terms must be satisfied. + items: + description: Defines a set of pods (namely those + matching the labelSelector relative to the given + namespace(s)) that this pod should be co-located + (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node + whose value of the label with key + matches that of any node on which a pod of the + set of pods is running + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by + this field and the ones listed in the namespaces + field. null selector and null or empty namespaces + list means "this pod's namespace". An empty + selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. + The term is applied to the union of the namespaces + listed in this field and the ones selected + by namespaceSelector. null or empty namespaces + list and null namespaceSelector means "this + pod's namespace". + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the + pods matching the labelSelector in the specified + namespaces, where co-located is defined as + running on a node whose value of the label + with key topologyKey matches that of any node + on which any of the selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules + (e.g. avoid putting this pod in the same node, zone, + etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule + pods to nodes that satisfy the anti-affinity expressions + specified by this field, but it may choose a node + that violates one or more of the expressions. The + node that is most preferred is the one with the + greatest sum of weights, i.e. for each node that + meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity + expressions, etc.), compute a sum by iterating through + the elements of this field and adding "weight" to + the sum if the node has pods which matches the corresponding + podAffinityTerm; the node(s) with the highest sum + are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, + associated with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of + resources, in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The + requirements are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label + key that the selector applies + to. + type: string + operator: + description: operator represents + a key's relationship to a set + of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array + of string values. If the operator + is In or NotIn, the values array + must be non-empty. If the operator + is Exists or DoesNotExist, the + values array must be empty. + This array is replaced during + a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of + {key,value} pairs. A single {key,value} + in the matchLabels map is equivalent + to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are + ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set + of namespaces that the term applies to. + The term is applied to the union of the + namespaces selected by this field and + the ones listed in the namespaces field. + null selector and null or empty namespaces + list means "this pod's namespace". An + empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The + requirements are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label + key that the selector applies + to. + type: string + operator: + description: operator represents + a key's relationship to a set + of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array + of string values. If the operator + is In or NotIn, the values array + must be non-empty. If the operator + is Exists or DoesNotExist, the + values array must be empty. + This array is replaced during + a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of + {key,value} pairs. A single {key,value} + in the matchLabels map is equivalent + to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are + ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static + list of namespace names that the term + applies to. The term is applied to the + union of the namespaces listed in this + field and the ones selected by namespaceSelector. + null or empty namespaces list and null + namespaceSelector means "this pod's namespace". + items: + type: string + type: array + topologyKey: + description: This pod should be co-located + (affinity) or not co-located (anti-affinity) + with the pods matching the labelSelector + in the specified namespaces, where co-located + is defined as running on a node whose + value of the label with key topologyKey + matches that of any node on which any + of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching + the corresponding podAffinityTerm, in the + range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified + by this field are not met at scheduling time, the + pod will not be scheduled onto the node. If the + anti-affinity requirements specified by this field + cease to be met at some point during pod execution + (e.g. due to a pod label update), the system may + or may not try to eventually evict the pod from + its node. When there are multiple elements, the + lists of nodes corresponding to each podAffinityTerm + are intersected, i.e. all terms must be satisfied. + items: + description: Defines a set of pods (namely those + matching the labelSelector relative to the given + namespace(s)) that this pod should be co-located + (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node + whose value of the label with key + matches that of any node on which a pod of the + set of pods is running + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by + this field and the ones listed in the namespaces + field. null selector and null or empty namespaces + list means "this pod's namespace". An empty + selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. + The term is applied to the union of the namespaces + listed in this field and the ones selected + by namespaceSelector. null or empty namespaces + list and null namespaceSelector means "this + pod's namespace". + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the + pods matching the labelSelector in the specified + namespaces, where co-located is defined as + running on a node whose value of the label + with key topologyKey matches that of any node + on which any of the selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + image: + description: Image - docker image settings for Vector Agent + if no specified operator uses default config version + type: string + resources: + description: Resources container resource request and limits, + https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + if not specified - default setting will be used + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of + compute resources required. If Requests is omitted for + a container, it defaults to Limits if that is explicitly + specified, otherwise to an implementation-defined value. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + tolerations: + description: Tolerations If specified, the pod's tolerations. + items: + description: The pod this Toleration is attached to tolerates + any taint that matches the triple using + the matching operator . + properties: + effect: + description: Effect indicates the taint effect to match. + Empty means match all taint effects. When specified, + allowed values are NoSchedule, PreferNoSchedule and + NoExecute. + type: string + key: + description: Key is the taint key that the toleration + applies to. Empty means match all taint keys. If the + key is empty, operator must be Exists; this combination + means to match all values and all keys. + type: string + operator: + description: Operator represents a key's relationship + to the value. Valid operators are Exists and Equal. + Defaults to Equal. Exists is equivalent to wildcard + for value, so that a pod can tolerate all taints of + a particular category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period + of time the toleration (which must be of effect NoExecute, + otherwise this field is ignored) tolerates the taint. + By default, it is not set, which means tolerate the + taint forever (do not evict). Zero and negative values + will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration + matches to. If the operator is Exists, the value should + be empty, otherwise just a regular string. + type: string + type: object + type: array + type: object dataDir: type: string env: diff --git a/controllers/factory/config/configcheck/configcheck.go b/controllers/factory/config/configcheck/configcheck.go index 334cf2a3..e4a325c0 100644 --- a/controllers/factory/config/configcheck/configcheck.go +++ b/controllers/factory/config/configcheck/configcheck.go @@ -32,6 +32,8 @@ import ( "k8s.io/client-go/kubernetes" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" + + vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" ) const waitConfigcheckResultTimeout = 180 * time.Second @@ -49,25 +51,42 @@ type ConfigCheck struct { Envs []corev1.EnvVar Hash string Tolerations []corev1.Toleration + Resources corev1.ResourceRequirements } func New( config []byte, c client.Client, cs *kubernetes.Clientset, - name, namespace, image string, - envs []corev1.EnvVar, - tolerations []corev1.Toleration, + va *vectorv1alpha1.Vector, ) *ConfigCheck { + image := va.Spec.Agent.Image + if va.Spec.Agent.ConfigCheck.Image != nil { + image = *va.Spec.Agent.ConfigCheck.Image + } + + env := va.Spec.Agent.Env + + tolerations := va.Spec.Agent.Tolerations + if va.Spec.Agent.ConfigCheck.Tolerations != nil { + tolerations = *va.Spec.Agent.ConfigCheck.Tolerations + } + + resources := va.Spec.Agent.Resources + if va.Spec.Agent.ConfigCheck.Resources != nil { + resources = *va.Spec.Agent.ConfigCheck.Resources + } + return &ConfigCheck{ Config: config, Client: c, ClientSet: cs, - Name: name, - Namespace: namespace, + Name: va.Name, + Namespace: va.Namespace, Image: image, - Envs: envs, + Envs: env, Tolerations: tolerations, + Resources: resources, } } diff --git a/controllers/pipeline_controller.go b/controllers/pipeline_controller.go index 1f14f434..f29af5cc 100644 --- a/controllers/pipeline_controller.go +++ b/controllers/pipeline_controller.go @@ -171,11 +171,7 @@ func (r *PipelineReconciler) runPipelineCheck(ctx context.Context, p pipeline.Pi vaCtrl.Config, vaCtrl.Client, vaCtrl.ClientSet, - vaCtrl.Vector.Name, - vaCtrl.Vector.Namespace, - vaCtrl.Vector.Spec.Agent.Image, - vaCtrl.Vector.Spec.Agent.Env, - vaCtrl.Vector.Spec.Agent.Tolerations, + vaCtrl.Vector, ) configCheck.Initiator = configcheck.ConfigCheckInitiatorPipieline defer r.PipelineCheckWG.Done() diff --git a/controllers/vector_controller.go b/controllers/vector_controller.go index 98262d73..42f38931 100644 --- a/controllers/vector_controller.go +++ b/controllers/vector_controller.go @@ -184,11 +184,7 @@ func createOrUpdateVector(ctx context.Context, client client.Client, clientset * byteConfig, vaCtrl.Client, vaCtrl.ClientSet, - vaCtrl.Vector.Name, - vaCtrl.Vector.Namespace, - vaCtrl.Vector.Spec.Agent.Image, - vaCtrl.Vector.Spec.Agent.Env, - vaCtrl.Vector.Spec.Agent.Tolerations, + vaCtrl.Vector, ) configCheck.Initiator = configcheck.ConfigCheckInitiatorVector err := configCheck.Run(ctx) From a0b97a8db5c6815593d24bf20bb7665ec1226258 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Thu, 12 Jan 2023 14:00:10 +0200 Subject: [PATCH 167/316] Update ChangeLog Signed-off-by: Zemtsov Vladimir --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6ff0431..3b26db4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ ### Added +- [[81]](https://github.com/kaasops/vector-operator/pull/81) **Feature** Add control for ConfigCheck params + ### v0.0.11 - [[79]](https://github.com/kaasops/vector-operator/pull/79) **Fix** Remove vector.dev/exclude label From fefa3e910d7091561402b5f74016bccd137c8d00 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Thu, 12 Jan 2023 14:02:53 +0200 Subject: [PATCH 168/316] Prepare helm for 0.0.12 release Signed-off-by: Zemtsov Vladimir --- helm/charts/vector-operator/Chart.yaml | 4 +- .../observability.kaasops.io_vectors.yaml | 983 +++++++++++++++++- helm/index.yaml | 29 +- helm/packages/vector-operator-0.0.12.tgz | Bin 0 -> 15043 bytes 4 files changed, 1005 insertions(+), 11 deletions(-) create mode 100644 helm/packages/vector-operator-0.0.12.tgz diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index a3133aae..3d58c51d 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.11 +version: 0.0.12 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.0.11" +appVersion: "v0.0.12" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml index 1bc130b3..f59668bb 100644 --- a/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml +++ b/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml @@ -925,6 +925,988 @@ spec: playground: type: boolean type: object + configCheck: + description: ConfigCheck is the Schema for control params for + ConfigCheck pods + properties: + affinity: + description: Affinity If specified, the pod's scheduling constraints. + properties: + nodeAffinity: + description: Describes node affinity scheduling rules + for the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule + pods to nodes that satisfy the affinity expressions + specified by this field, but it may choose a node + that violates one or more of the expressions. The + node that is most preferred is the one with the + greatest sum of weights, i.e. for each node that + meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, + etc.), compute a sum by iterating through the elements + of this field and adding "weight" to the sum if + the node matches the corresponding matchExpressions; + the node(s) with the highest sum are the most preferred. + items: + description: An empty preferred scheduling term + matches all objects with implicit weight 0 (i.e. + it's a no-op). A null preferred scheduling term + matches no objects (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated + with the corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: The label key that the + selector applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + If the operator is Gt or Lt, the + values array must have a single + element, which will be interpreted + as an integer. This array is replaced + during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: The label key that the + selector applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + If the operator is Gt or Lt, the + values array must have a single + element, which will be interpreted + as an integer. This array is replaced + during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + x-kubernetes-map-type: atomic + weight: + description: Weight associated with matching + the corresponding nodeSelectorTerm, in the + range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified + by this field are not met at scheduling time, the + pod will not be scheduled onto the node. If the + affinity requirements specified by this field cease + to be met at some point during pod execution (e.g. + due to an update), the system may or may not try + to eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector + terms. The terms are ORed. + items: + description: A null or empty node selector term + matches no objects. The requirements of them + are ANDed. The TopologySelectorTerm type implements + a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: The label key that the + selector applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + If the operator is Gt or Lt, the + values array must have a single + element, which will be interpreted + as an integer. This array is replaced + during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: The label key that the + selector applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + If the operator is Gt or Lt, the + values array must have a single + element, which will be interpreted + as an integer. This array is replaced + during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + x-kubernetes-map-type: atomic + type: array + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. + co-locate this pod in the same node, zone, etc. as some + other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule + pods to nodes that satisfy the affinity expressions + specified by this field, but it may choose a node + that violates one or more of the expressions. The + node that is most preferred is the one with the + greatest sum of weights, i.e. for each node that + meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, + etc.), compute a sum by iterating through the elements + of this field and adding "weight" to the sum if + the node has pods which matches the corresponding + podAffinityTerm; the node(s) with the highest sum + are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, + associated with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of + resources, in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The + requirements are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label + key that the selector applies + to. + type: string + operator: + description: operator represents + a key's relationship to a set + of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array + of string values. If the operator + is In or NotIn, the values array + must be non-empty. If the operator + is Exists or DoesNotExist, the + values array must be empty. + This array is replaced during + a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of + {key,value} pairs. A single {key,value} + in the matchLabels map is equivalent + to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are + ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set + of namespaces that the term applies to. + The term is applied to the union of the + namespaces selected by this field and + the ones listed in the namespaces field. + null selector and null or empty namespaces + list means "this pod's namespace". An + empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The + requirements are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label + key that the selector applies + to. + type: string + operator: + description: operator represents + a key's relationship to a set + of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array + of string values. If the operator + is In or NotIn, the values array + must be non-empty. If the operator + is Exists or DoesNotExist, the + values array must be empty. + This array is replaced during + a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of + {key,value} pairs. A single {key,value} + in the matchLabels map is equivalent + to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are + ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static + list of namespace names that the term + applies to. The term is applied to the + union of the namespaces listed in this + field and the ones selected by namespaceSelector. + null or empty namespaces list and null + namespaceSelector means "this pod's namespace". + items: + type: string + type: array + topologyKey: + description: This pod should be co-located + (affinity) or not co-located (anti-affinity) + with the pods matching the labelSelector + in the specified namespaces, where co-located + is defined as running on a node whose + value of the label with key topologyKey + matches that of any node on which any + of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching + the corresponding podAffinityTerm, in the + range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified + by this field are not met at scheduling time, the + pod will not be scheduled onto the node. If the + affinity requirements specified by this field cease + to be met at some point during pod execution (e.g. + due to a pod label update), the system may or may + not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes + corresponding to each podAffinityTerm are intersected, + i.e. all terms must be satisfied. + items: + description: Defines a set of pods (namely those + matching the labelSelector relative to the given + namespace(s)) that this pod should be co-located + (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node + whose value of the label with key + matches that of any node on which a pod of the + set of pods is running + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by + this field and the ones listed in the namespaces + field. null selector and null or empty namespaces + list means "this pod's namespace". An empty + selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. + The term is applied to the union of the namespaces + listed in this field and the ones selected + by namespaceSelector. null or empty namespaces + list and null namespaceSelector means "this + pod's namespace". + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the + pods matching the labelSelector in the specified + namespaces, where co-located is defined as + running on a node whose value of the label + with key topologyKey matches that of any node + on which any of the selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules + (e.g. avoid putting this pod in the same node, zone, + etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule + pods to nodes that satisfy the anti-affinity expressions + specified by this field, but it may choose a node + that violates one or more of the expressions. The + node that is most preferred is the one with the + greatest sum of weights, i.e. for each node that + meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity + expressions, etc.), compute a sum by iterating through + the elements of this field and adding "weight" to + the sum if the node has pods which matches the corresponding + podAffinityTerm; the node(s) with the highest sum + are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, + associated with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of + resources, in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The + requirements are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label + key that the selector applies + to. + type: string + operator: + description: operator represents + a key's relationship to a set + of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array + of string values. If the operator + is In or NotIn, the values array + must be non-empty. If the operator + is Exists or DoesNotExist, the + values array must be empty. + This array is replaced during + a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of + {key,value} pairs. A single {key,value} + in the matchLabels map is equivalent + to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are + ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set + of namespaces that the term applies to. + The term is applied to the union of the + namespaces selected by this field and + the ones listed in the namespaces field. + null selector and null or empty namespaces + list means "this pod's namespace". An + empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The + requirements are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label + key that the selector applies + to. + type: string + operator: + description: operator represents + a key's relationship to a set + of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array + of string values. If the operator + is In or NotIn, the values array + must be non-empty. If the operator + is Exists or DoesNotExist, the + values array must be empty. + This array is replaced during + a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of + {key,value} pairs. A single {key,value} + in the matchLabels map is equivalent + to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are + ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static + list of namespace names that the term + applies to. The term is applied to the + union of the namespaces listed in this + field and the ones selected by namespaceSelector. + null or empty namespaces list and null + namespaceSelector means "this pod's namespace". + items: + type: string + type: array + topologyKey: + description: This pod should be co-located + (affinity) or not co-located (anti-affinity) + with the pods matching the labelSelector + in the specified namespaces, where co-located + is defined as running on a node whose + value of the label with key topologyKey + matches that of any node on which any + of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching + the corresponding podAffinityTerm, in the + range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified + by this field are not met at scheduling time, the + pod will not be scheduled onto the node. If the + anti-affinity requirements specified by this field + cease to be met at some point during pod execution + (e.g. due to a pod label update), the system may + or may not try to eventually evict the pod from + its node. When there are multiple elements, the + lists of nodes corresponding to each podAffinityTerm + are intersected, i.e. all terms must be satisfied. + items: + description: Defines a set of pods (namely those + matching the labelSelector relative to the given + namespace(s)) that this pod should be co-located + (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node + whose value of the label with key + matches that of any node on which a pod of the + set of pods is running + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by + this field and the ones listed in the namespaces + field. null selector and null or empty namespaces + list means "this pod's namespace". An empty + selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. + The term is applied to the union of the namespaces + listed in this field and the ones selected + by namespaceSelector. null or empty namespaces + list and null namespaceSelector means "this + pod's namespace". + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the + pods matching the labelSelector in the specified + namespaces, where co-located is defined as + running on a node whose value of the label + with key topologyKey matches that of any node + on which any of the selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + image: + description: Image - docker image settings for Vector Agent + if no specified operator uses default config version + type: string + resources: + description: Resources container resource request and limits, + https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + if not specified - default setting will be used + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of + compute resources required. If Requests is omitted for + a container, it defaults to Limits if that is explicitly + specified, otherwise to an implementation-defined value. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + tolerations: + description: Tolerations If specified, the pod's tolerations. + items: + description: The pod this Toleration is attached to tolerates + any taint that matches the triple using + the matching operator . + properties: + effect: + description: Effect indicates the taint effect to match. + Empty means match all taint effects. When specified, + allowed values are NoSchedule, PreferNoSchedule and + NoExecute. + type: string + key: + description: Key is the taint key that the toleration + applies to. Empty means match all taint keys. If the + key is empty, operator must be Exists; this combination + means to match all values and all keys. + type: string + operator: + description: Operator represents a key's relationship + to the value. Valid operators are Exists and Equal. + Defaults to Equal. Exists is equivalent to wildcard + for value, so that a pod can tolerate all taints of + a particular category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period + of time the toleration (which must be of effect NoExecute, + otherwise this field is ignored) tolerates the taint. + By default, it is not set, which means tolerate the + taint forever (do not evict). Zero and negative values + will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration + matches to. If the operator is Exists, the value should + be empty, otherwise just a regular string. + type: string + type: object + type: array + type: object dataDir: type: string env: @@ -1063,7 +2045,6 @@ spec: node network namespace type: boolean image: - default: timberio/vector:0.24.0-distroless-libc description: Image - docker image settings for Vector Agent if no specified operator uses default config version type: string diff --git a/helm/index.yaml b/helm/index.yaml index a939ada4..f0e616be 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.0.12 + created: "2023-01-12T14:02:14.473422+02:00" + description: A Helm chart to install Vector Operator + digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.12.tgz + version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2023-01-10T13:44:40.180860293+02:00" + created: "2023-01-12T14:02:14.472722+02:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2023-01-10T13:44:40.179929316+02:00" + created: "2023-01-12T14:02:14.472047+02:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2023-01-10T13:44:40.182875831+02:00" + created: "2023-01-12T14:02:14.477729+02:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2023-01-10T13:44:40.18235856+02:00" + created: "2023-01-12T14:02:14.476953+02:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2023-01-10T13:44:40.181873014+02:00" + created: "2023-01-12T14:02:14.476104+02:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2023-01-10T13:44:40.181372514+02:00" + created: "2023-01-12T14:02:14.47414+02:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2023-01-10T13:44:40.179419447+02:00" + created: "2023-01-12T14:02:14.471166+02:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -92,4 +105,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2023-01-10T13:44:40.178800454+02:00" +generated: "2023-01-12T14:02:14.470118+02:00" diff --git a/helm/packages/vector-operator-0.0.12.tgz b/helm/packages/vector-operator-0.0.12.tgz new file mode 100644 index 0000000000000000000000000000000000000000..ce3acae4d4e29ed31a22728525c0bfa7ceb66549 GIT binary patch literal 15043 zcmX|I18`u$vW;!qwr$&XHnz=;ZQHi(Y-}eR+qQo8y;uKMO?CIVw`Z#EOi%S0;73BB z0Q`HtDFLVrC6pM9BxKoSJUCf@F{?2cE3;Z?DRZ*PtEsWcs#{wc+8TSPDBAH#m{{8Y zT=;moVYfEheIJ$fB|D`Qs}&uHXE`md%-3fxQpXfpwV!geUdr}k#)+^pr@=8KX_7xT zZU@5}Y{45yjL89g#E%|KlI37~eLwwy*G7U!nbMSEqS0`D@(yIKU)|++Tk0Qw{dpQo z<#)f2kB_ImbNM}#OCPus9L5u%n|yc%EwsMwy&ZboAKcN9@QhhdZ+1!Fki&nWj9LVM2(#=0i6g$j z9U;D{%@nSM3?Bg51WCLOudQW7$oA-f(C~Hkr!^+!vXX7l^xp*^m1Z4k3cL(kUlB=P zC^`PN&k$qjYuIY6wn(yF7`b|qFJW2vMib}U4OImoKK~wDAeYZL|6Y4-*Z+Q2RP#U? zD}KIAF~)=)1Lfk%aPjR^##!XgHUUvvbxI-M1LDn?GLKq0_!Xicru3)WVC@R%v05nz zL{D1q1aN%RMhtXU;*vi#7%PJ?=}6Iemo2-UuK$6I`5!{?loTPt3qUZGq z6p9Jx9Cw#Qgi-D$V~7M1`tg7k6`O=Di5P7C0%Z<$y&lJe#8kVW2WZMQqDT(90l`G1 zlE7KI5P}vGN>T&_GMR-6jrm3K?&C5Nnxr9X(p1M-Bku7E$mhl33BZIV!o1)zqSnV8rprml963y?Kxe~c;QI~2qxkO(C6cIBPO|E zj$*ynG?L&JG$#m0#OkH0SSt&W)m;KO$ADC5t^H6qP7zQ0fxZciJpF9p{k_xu9Ck{V ziOk8-X#db|t7KCmzON+lfSXQS+e=0wfq{c(ay@Fa!9#qxXAT5bBD!(#y~HE}lzl`h zOF@8CHXS|;b4o=oLPp`lO08LAQt(Rj(P&H-B-&WIu;#!L`0%O(AYyd+tWY3JuYh=L z%nm+dxFHtbTZEAIS2D7m*Uj9khmP0X{k>h;#k{pNup=f2L$?_OufPf=mp zVWyyXUpT`@f<5dM@%$`G%U?kan>XrnM)0#H2@Lt`L~e>WA$NYigfsRI1X5;%IuGBk z*+9lb!&fWLX(?%rjR9wRwDN->hr<0$^3MV&Lf5m1sz4NDmPz7qWrUKPGO7gY=HMmb zIHC_=L~cHYty5?n?B{HCIy(-!M{lgUNaFF@z|V+__=Krb@~P48lpg|$5I4q1xzF9p z=Paa&-lI`?(NpNz&k6LWmv7V}ijfS;6ofgnoivi2oLPE@iB>0}U7o#)8;e3bjrbDj zYt2a{zqcdnIEMct&<#SQo9-&Vw@WdJa`B5e24NNT4cf#AXUbGC$w`puk(g3eQUtl6 z*_86!9E@mEYek=!gemI=bz*PaEQ^f&LjZ~IY1x~;*y20gWO}A&JC1@BQ$NgEUfF)} z%j>Pp@SFF2FZ}uYs@VI*{>iGT_!jg6-Vr!e`}x{vjkZ5TCr>~3^JQO#kJamyFZn&x z+?v;bf!6e`ofB#-f(!(=eC9Aw++5F>oEET2!8B*F-267%k+VXK{7vwROwvDx_cyVO2&z;=b}%u-88R`0R~6`%hj<= zM`o>UEbnZ|3wn7~zfWXysVeweX17AU6|}VR`B2{W+vfI^`f26fib$8pGKRO~yo$a# z8j$AocJaMJdp73nzAg53*5ADbeiJ`!@6+S)Tdn%(^LV{YZumVbVpXcXJuIdR%i(23aM}>ft|;w3-(jX_% zHK^nP(N%^0?~F~K;8i6#=3szpG%h*@mH`)I3D%PDM)_VqGslnnY1gLS3U3vQIQyZ%-LD;BinEVzOwkfjx_6R8o)uJN3l`cwiiP{soT!-IHGp5Wv3rSh@-6ksE zE3M^{+I<>dE5|d_nQz~bBl_;2e)ytvWiaGc0es9xQM1F>&`V34&c`hBLx^!Kb86u!(fntohImd@n7Jo@1mcSIHUYGgPj>qN;R z*WlNvP8{Y;u~JLJMrT-pH_b=&Fk$$gfg2&IDII%O{F)zadSa9`u}u~j9l~9VtarxN zSLf<(+}Hq2zhlp_FsVQdM$=9wY!GSIxBrU%W?{=e9gMlMAR9wf)O>6l#Zdh;J&7cwvE#m{ChUgq-28}#Igety#U zEREnaXHwVJRV`^&+dh{IWZza3D2WK5R*|xJP2=LN8COdzs`-9@{^pYEiiKqe`R3uY z+v7i$pZ57U){F3IQ{x|%>>^!rLM!DUJ~@H!6^})W&Be3B=G)_~4D;LX=@lLCyFGPY;oEaob)Caf z6u|m7Uhs``W#u1vOy?0fZc4wrVzVY136qr!QIlrv_TPuO_+Bhmy+0Y<&#abKgPal{ z`1yz8>;2&V=(Tqy_wCV-{t&QZKqyWIw_Fi8)|Q<_a3V{LPeZc%9il3gyPAVx%UmR^+1ymQJ5Y6Pbsv~m%;=9n6d=wSyx{aP6V z^2rq89zt?gR)Z`i5%#Z{Mx(Fk?M0&@ZUmPwEA@T;zzSyBZye5uV1B8s__D75aB~Iw zX2rK&HoN0L%Jt67N)AZjXd6==^*L_Tr+=~GdVl;CZfDx77pCzw#nh4W`Sv}9mv?P1 zra3)ViBA7&9dCM(OM=h{3Jb}n9BX!w+c=y8PD5gri?h0+m~qZOD&)IddenP8ZHdu{ zD7O5QlFwv(Z8zS}bbq_i7oU3UB&DngUEcUkgGO#!=Iw%N&IJv;zMaW!Go1maghh!W zjvZMg2wX8ypeHObwYDBderxftfkRPBZ_TCI|K5K9BK1H)6%(1ExsaV^B-qme9S2Wv z?*o~O*%cUqLXaCDUiqBqQacreC|1nbEvub}I`~@$gTwl{;DlnW{0J+%>=D$MvtkBu z>o4i2ydqc(<*@MR6f}qc1|hDFDI4Vcw~$9J0{@NNYz!4G6S|2(xXs+s@+5H1|65=a0g*??AiwVe^SOy%6Wx`#2k5aB5wonCV-9FGl{kR|OwK{dY~dy3S>i|zZ-u^0cqhT!15u5FvE?@P&8U~E1tx#Wj{eweR20Zzuz7ijbo)qtG+G?AI&Lgy7=06k6+sigFOcPvp4QGWlLAx z&ltZGZrutGrijFjtY9Eq z?|6XEaJz5ySEBq+?fH40Y#&e(j_IkUx-JijJ<&VYpyIp+udsg!8_}bnC`EWi$ zI=eaZ?Bd)yt@0qB>X*cJp6*gU%4WJw@)0SWT*ho#A3=gcHGPX7j6zkt1{T$aPe6&A1(Gp>DCf7 z14ccieE2m!5Q?)XlqT-D2Trp~G^jFtBsVtLoIq+w*p5@B+gZ7bFFmk(SYWdh0r;M@ zINpsEZ=vWmd`VTCLqwfmLgF1i;o+mJvWHr9bsDSRLoP_CweDM&ea$x16mA^hp!%7% zIC=yvoW`mGq8y|lVO=MH13j)DvC??LeWzDJx_UC9h>r;UH&01lGV~pV*6DW{fC-v4 z#y*H4tH4dA_P7BsX8agZfJVr#@_d#Nqhd?wVIqBqVw~ZsW^z_(Mj_LM6#fD`H3Dh&Wfj7*LXjgD~PLTsWq7p1@;bBCACz zpOo)?ta75mF0uBMMMUa2I7I;?7jQVjOwLoeML=<2aFeOnFeg?jz$J^QZK@E`5DcIs zoES9=iI>X7_wP|WX<`vov%x>1AltR#!D%KSG!-6pq9B zP1E#Koq7wfo{RO8O7vqK@7Ewwr#dXOH+0-!we!+H+ROD^dvu<#r_nWy6`>VJ>OyL_ z%^WtLNreh!t6+Bhv&CavR8h;y&Yk)FU|rPW`O*cmwJd=XOAM(IdZC+WXRB*T9aJdv z1Avf<5G6eWIdue9Sb1s2Rx5mH_nk0##Jz2 z)uDRzcAOaA`V|n*P0?Isc#)ZQt;goPGF3MBm|~v;~6)pI`;9z!E=23@2bxS`3ydRUuruWU!p}{0rQ^Iu^j{JdIrZDg5l*jmeu` zmN8dMsVr->4NT5jgZaYP3Pp@a!_{f%&eLUd)pc&q^44*p4;5%TR6?;P9Bt^OFeR}a zy^}PEAW>QpK_jK1G8>dGxi|*JMTB}0@!W$8ZJ1?W5$9%`rFC%o@{$hF^(Oz}hCfZj zIol_i&77lu5Sh_V(rA{UQH}^u$hkK5MIHk?ZoNgUX=DpvzR0;njJYWztR;yYaYVUA zT38T7duy23$l{?;lkxR(pKPU+(KRlVVfS@puSIK*sf$#wU5Gw$+%b-JTkS^n0!B@& zY~I>_R@LGa4F_CS=v5AB(M>UnK&>m#3__O%|dS&M2Tc9;kDO-tCV28)XpG3`((O_sMey~%FhDKCT_ z3=5UPeHf@l*+L6}%m^fBj4IAC%4a62NVaQ2MQEeD1T{lFVLFQQq&(Pk@r%>%!C#;4 z0)BKieAA$|3{{>r(TQ6|0Y(dXiv~)&U(07nJLc?vA2EeEcOR2LeuX;zD))tOaRh^` zJ-fu-oI@c3oQ8)JMk+`$c4Cdk)S4r?FyAaw*vu`Yp5Lv~@7A0aT(&fBYe8g{|I%Ee z80KlOP4x4})>)w&$EBAQfUXPmJ0#g4Y$4S=%co`l2G}RC z=IEDRA=oGJH^TA`D-j4Q<5X$bS`VO{6)_7hi{_UTLz&~#yHYVmWbG_YL(>E`&6=gR zF=&9c%pzz5lye=7Mm`wl?1C3-U)hlr#8GrQVF?ojq+xp556UScPQk@E07;i!Y=FIz zMOY8kxze1$duUKPq*Shi?$3hR%kKodn`L2umj!*8^`$n&stYCi?KO(IBfKEM#{zpI zjO+zG*|w6bop&jfJY~QB#q02QgRQKht0DPU)_C?|Ad90x`PYeQq(O$m>9RROFVP5r zadNIR60MaOXTrU0Byj>nwr7>DkLG!a=%wk#rY(kgN*WL}g}6Aov758jLYOT9uef+^ z0Ep`@s-(nVoo`9Iv14BR*(s8Amu0>lwiRCxjz z6nFw=Y48MZ{sBAzJBUH?dx#*)JOK)nc!Fi9u>>mPfQUgB;EWYttNkhPF5t}Cqd#q9uo;kYjK2JY~?gYr)(yMm( zSjaNGCdH#mO)^w8(wx}xbC)5;%`M7K%=%$5|F)pN{u+T2Jv0UJ${Bq%Pu8DRTB0|7ILb1|T8qI(7jJktV^{Ocj8Sw2<>b2ix zM;#8^FmmrLA8B&j)>p|I@He+0-xY~#FUjS650bo2FylHhcQNCtFyeNkgY~hfiso`y zMI_-=B#4<{6_q4|gT5LF>q3};fH98a^DI0%EEoHKZ;Bf-QfJreuV6NmaIA9Bv1gq& zYT`V_Dkpcw6iO`MV8)5z!sLa{82aYholwOotoFwog5i&a6So=^%8ILMYz!#)pQm7) zd&#zvP)>y!`7+rOSh)2~|D88oESp7SuC!T>lB!DQRXdt2lW2~EVm@+9{(OdCfK8wG z{|!egljis$4Qy(i&j~;`y*BTbOvy_r{Z?qN{W!uJsZs1z=f+WDAuALDMbyyg0C-Il zV}UsMFa-H&!lDOhlZ()}$*uN!`-051pGdWSvWjR_KeoQqQA2wXF3mM9tywZc!jI-B zWQOCI+h(qAU5loeRR35qYA|TX2j*vrd0^+y-=&jANu!bF+B}iO(m#%E#Y! zF7+ZLbR%n0=G z{Vm$eH_fZN)mqDDQ5P|7&BD4?!d=X05R9X?(v~Ii1(|rJi@Sfin}~6A>q^RxG|g^Z z3I37x{Tt_fliXzUH&33XdNY&UN<7T9qDDN-*AlnZ!X54{gfnc$^h1rXu&uL=upa+R z7A>BQI5X@DmHz^ncZmkrcavQ?=_}0lTB!dzxn1vn{muPfQ}uu5O5}4g{BxVx(Iv%y zzOL9y_mI8)=jtYJx)l#@FV(rBCT^wVnVrzr2fWfBGKA44bI9MT-1zB88y!{4lx6$% ze4YcPuh%W))PzWMh;C-4T`VNrzzeO}H{(y2KmzzfqIu6z7~!IJ7?PG{zr!?P_$6T+V|m zt!=G1!sX)NwfW zy)1Zv;MB~_&g$sEd6rr9JPOILP zTS|BZ{N=l&lqX%%_ia&8(jTDZ$}NDcxO%>2E2G*1dt@`OcE@g_*#cV{-pKoBmr38U znRoupgnd&q$Gy~SQcza^Hc6A5=%`(Y(?T|~Bo6ssEzC_Cq=nlp?ZH;4g)565A z^ZXkT4BbA>{BGebAx5`2GTw|%h@K%dZi0^DMtIZ^$1y)mr$?9hn|)Gu`L7OJz3|jl zvc|mQ+EOFQ6;{y^7V6Pa3hQIcA>O)|unjquL8bP=bt<{GYRf~bolRnKwv2ztw*E}E zjVQ(@L$a26^FBe*xKbUumT7cgl^fS!EthRxGr{adSi8Z{eeu)%L6p}*8Ui2E8F9YJ zEk%-ezU6SC!at1;FZk}CtrH=hzdkH35jd)k=MLuGuz;ft%7bB==sm4`}rZ4 zclR+t9V!dHc}JId0Vb1E4X+E>wy(^|(voDjO|qJ+e$&fTlPV0fN$egNaNszVtLv0+ z?9!u@S!7A$;f?Mx>P-JKW7kP}v1!*=<*qN3YlYiVcfW{IclNZ(0ZXfmLt z3mDnXC6N?iH*E}?kxk?T-RF8F`uQa28+Jx>R?&408~{J%lGflcxE*#^q{$baw%TsD z0=ZBD#rCt-7rc67h}iol!X2?C9HEV~DO#e*@N0T_!|p`-&|Csq-t)#G+8jFpRAFP~ z^cKN^ROk5msMh^lWWhD&jN?#cfwbnF>riFEb>{GRS$+flP5MmhlU*~iU1bR5UMU?1 z;_E}W!b>-2jA%_C*yO&ppWG{1WsK@%&)r~%?&KCwHNE?!+hjCJ=xkIk8+vX248%7- zv(Ycn*m^r$jBdsrcx#``Hg^4svOH{YMdGycrw*yrIkE}&?x?OHW0^gSGdx(Nwmx$x zi)7gjcjH-%QJH6%S&wIVUxjCRfdcOwlLqhnRztqpy=ugW7w^0tai>0~UY&P&zaB64 z@dvDE+f*})_^fuhf*4r6bdf_6zY19ii%lHu;oduMUE>VL>3OZ$SL1eTE4>Em0{Th^ zbH=?O<54C{!$4+_rOYhem-Q%F-A+4Ryt#AjzRkSP0$H(L!)_Ctkvy5T@v6>z_)7x6 z-BIewOdcNEUR~U6dqrK;Rx6)DLMw$96OYkM7q zTpJ(z231LD&}SRm0DB!qXGSa6|? zrzpPyn-WL%mUA_&-Q|+hDk@Oc32=~pKvD(HD?NGw%FNOp#X^e*xW61&=HW_Cq6vR#v=T{a7c;t+iQdWKgKcyTq96x2l?>D0xens1?7znO`g#^;Kg5zY47m$A3_i<+EyerTspy56B>C{Cym$&u#~>$F%&54)W-h@K17~k7UT~uQj&bD@HF1?Lr0pDn zdR#=ikrwi>M`xz)!((xBX3qI;za+1{whfV1a`7T?q7B@5OYyo+#3erGq&GOCMK^?O zyjYn|>PrFZz2Wj<)FQG>9H^*UN%t2b~-Jfbu_`}o{<>)HWr-Kx?B5cjbi*f2} zS&jz$QYpDt^PIM3l~Hnjfta@aaR+Hw*9qv}L>+x_p-++7Pszth>bl`7{&^}1BT@GW z^RRgx^N1@$*H6TF)hnNwn(dhyeQY-Q`tQiX)!LZe;2|pHUf!9WrM^_jEj6C^6ed*U z-s3g);rk1CzH5)C${$5N@>uWwuNCjGQ!Q)6u^u)}j@Oh^?b46>gZ}7Z{y(7Cl2fh6 z73)m*QTs|&#PQlQ&wGwpuj)_O*5~Bx)#r%aYRnkiYQ&CsS`PmU+aEO=Aoek;`|N*V zYs9Pe)0S6`&rGA;XUzX9@KgE!suZt+&3av3_mAaT?B-TIm1%;2$a|6Zdx8~Q(#CQe zwuOrJ4q8qBw#I4L-)%5nX-iZ&{gWF}UV(|*%vWxxGi}$H&sM|CS6)3``FpQ-SrNw* z_ud}7!}o{nC^M)k!nhxH zY_T#7IdAs3Pvk3GOFs47P$#-ZVSuW_XtDCKsQ4M7lH|NJkypF^@S34t#qMCGNd$jD;$~}Qedv*#8Nd-; z2f?sV98J5x_w$Qg@2jEj-N^oaZ_dp3`E@Vn*9Oqn%i|cnyf4G|%hcG|?X7{|zb~6oeMY%7GNaj=PV)&+I1RqqM)TO`4Qo{P zmwox`iP1;SACD;eNyo!C^ODbaPt=7bh1nBqFpEWMSBIUoZ+pLJ4ISm3<12Z`tDdF8 zm8@=-@|mr8taNRX@wH4;k+)j4vKQ2G#K`hEv?hlnzUGjv;#IV(82PzATZ zxMvFyV5$N}jUb!73YIX|Z?~thz z{;Zc-VEb+7YHaUhzxyh5>o0NUqjmBu_PA}XV3Ixhcj9C_N zP$@iU`+NlsOJ}UAs;N%NP?Tk3E|UcL39-VnQnVQEu8(?epn_aMoW^*iR}IB*LoQZ) zn9+YH@0)uEcb8eaouLvJ1=nxSQ^44>^A zvy2jF5tgej(_ot{H{h)}dgvtjlkS!+tu~6EwpJ3uZlH`&U|+Mh1Rys>gGRJs=dYloq&Y?)P zS80f~kO+?u0;k&tJR1N0f*(s03LewK4Qh%a#-`Wm>$bL!y1~J%46J8~k(8(>n7b0e zMPLiH8|lb%MEY7&t{gMp7Zga$GTm_lU|wGA6~e=H@iWY*tAPQi#-zISg2j&=XZ`(F z!T2H1naemninZLNRSd2*2lr&jZ_Z9cG6d9{$E)2h!7JHt>04_nNvd7UJ9VJyRs?B^ z(Agwj4zu1dmHPs>;#MNE9AirJ+x!g8427OJ-~ssa$t8fn(-tS9$Dyg78yN?F`b!IV zYp1R2rM_?r`XPEbbI?P5rq}Or?=aJ?w#WPN?sAr-DZ{@Iy6pZ7B8SSBu%%z_VQ@SLX-h*mZLR&JR@Cl8$_wfAw& zY&ISf@?M~tsHzbe?9kWriOC)t#X!#8UwL6^DSoe0Wak;ZPk@2q@uYFwO8n?fjjXby z1%D|6;%Y2vR`K!FaE#t$Q$izKI>HqAX~BduQLgxGSQ7)0ugj+awPO>Z4PTwom)E^$ z^hzpCW07X8uy7b5&!9J~XL5B1HL^L_xjOF)b;5J0U)VOVk%zv-WLh$;Y`TsGY?`97 zvf8bBC*98ejdiV?w2;EMyB?uwAqOM~sQ5{B?~kius*K^p-yJm10-{u|7IWlpr-;w! zOT_FYPmN8Qhc|@jQT7d~OL5y7TTNHt#K(`6h$)?P$Pbub6K3!`erkdEc&1C*a~!Six+u(C8eu$qooUk(^^%ERR|niX{}$G*K_)~4zUM8i7C zZ-H7z7!#Z(e+jy)HQ611nEp)~q&2o;i==^b7=gTec!0-V1vg&GxUffnr)&{TmjI8v zatdV3FQPgYM|4M~*qP4t{Wv{`h%ZHNh<5moT6p}gtMkkdKQit;`F=RW;r6WJ#cX^)T^&t{TK>L28lIDC+wqZ` zp6gTZvE~M2Fjs$^uyrbDghYyx38RomyG;xMU(# zrgQ0oj*ST2wTp?u&*O2NgaxN9{prorgwyi5z(oe)bb_~8K0YU3ywaYPj-c12sdhpl zPs#=Wet0NiuKvv)vFNqj*8njw&7|{|wHlxNG-PNVB-g2*sf(;M*qyZHB^8OMr+^yO zT3WqFFV1Y9Dcn^IJ@F<_tN7%QJ+Z26U`$<2gE18KYNCLrg;&;$2I^3SRNJEDK5&DU za8t?FEop)Z6M&N(gkYrzV9`cM5q73Qr>K4Qq!79t!y}goZrad6-cvS~pQ{1ke$136 z&!-VhLA1E77TT%uTeN&R!soazaou-=LG=~I6;>`FDs1qjLn`HWC;@!mxCO}I1F}PG zRfh5$d1?)Q{|fsq!Q~xTL`O}xbLw{qwKqjMV%7galH zKl3^)m%=grrCW%qLL;J$B{n>S(YXk*?zV=h6eX1jtedYykHWsL+XNfPCT1As0dQu0 z8aY(3Z>4h=r^w=cTiojVsXnWyo#VX zkaG)ymFrAki}?de`Cuq8Vd{74DdL^6(D6G$qNk{;a%*awQ4#|ZmkkL{@-^@5g}5xr za(ET}>!Ng!+)?}`)~rV3nB#Xd*e+ARn4aLr`qwEKHaCtyR6vW@I zG~#C;y1Z}LXQC120~Qiop3hr%FnpB4%S-;ata9-AETtN%gAr^dyR49RQ4yP{k>+*h zBb8?_b}L?I#;7;<6L4zh^{)o{J(Vm_(we|Y zs(+8lBOPZ(U2o1=bja_MWEbmmc?V3-jKy-3+sKb`PDy1VJ2o@h2f`ITp0-Wj+X;h> zzKpfWJL*RB#2tp`F*>ZxZL{f>uhME^K^?d;*qgk+Ul^d#S$j=5hkm7Ve#kttaW!a7 z>FNOk{^8Qn^y!)3X&5LT^?k9fQd*z?&k_3PEDNh3TuVE114@%!9wO}>Qq^r@8WFKn zk8cKv4RQmA$%KetV;zerJ#}3z$IWxUXNjuSwhqt8!K`GQRvWdC%BVwNd%0@lSUOcv zdyqNn?jGW%6sCvWyuG2dSzBaLlWm7&Te!yejA4<`Y(uD%Y>f2}mmU&COrbzfVJbxp zP&K>a8(7)o(oj)C3_AZr{d4MrkR;3^#ZOwnSQL8nPEgCVTD@)x`Fv}Lw>zv3L|!#{ z2cl(X2tEA@WVS2_=d3tw|3MC@2M@Gp-jlP8?cY%FV}H{vv+~F*@F>TAK{Z+2p)b`p z*lqJI`rEj2&)6Q{zj_OtY$I{L-+@d6s*2@zUD2Bo>Rw{;@_A1-RH3nVu~$?>KZF3< z8ql#QWAcNp^AISz9#(VmbQs_;tq|;nlM!|vMt+&MZT911-;39l95X;Bqg%E&;JJ2b zl}(eb?a-QFu0r4aHteimgIVItLxkt8O)(~A-zAMl)*?rS!GwWoX-}AV(B))ffcNi} ztMQgVuBp}^ZNA_}^XW^E+f_Xe z{Zbzf%fuTvpARVxfnh^65GLDr^UT>iVM9LEYqo@;e2YOtw&g;EmP{ z`$*s-Ws><%NC(Gp1H689-|hM87lG^`z$R5uKyZ5CpevN5d(LTod<_Qi8An9TR<3@x zz|W%b@-;})5Yz%;G&#l|kl7U4LNa0xMT^4_Q_}zNU>YIi*Z~4C zhmyXs54D-`3ZBM4DX{mFVOgE^)oz{m3DEd3lLd1?(!#Qx3t#bC5!!r^RNbmKC7tNA zjlY&iDj%U^%KZ=oorte`-4O?E2?%|9HgDsG)I zl-wi^17zE*Cg8M;nX)}Ul4RZX4p-ULSdi1^E(ImnSXyj;^DN(7;1>lwn6@DE8BzjO z2LnaJj#|Ce^|A*eW~7)B+4(GfWY{l#_UL#ZzWA0tqbD1WR%vXT|5yis+IE$+j-@rN zpe>W~`Lt-i@#O;ihfh`is5zT+wV-tQDjpAEDEA{2C1v=yle4e6$=a{{Lv7;qG@wYl20TwJc($abUR+0H7L`YIK zh6W>eWFi?47)>l`E~?$mx%8l_{j20D+R+8iQRP*VIo7#Z9AxlGPP4!Ylp%gCLtQHT z=k9`DW(+>3yF2^(rNmMphSe~FgD^6O)cNvGz+X{KInw%6Ef)@A05Wt~vHLmdA}r(? zTg&LtMF+}Djy#XpcvA(G&K7pF=Un78cE}48}q^z>hl=zIi??HOOc`* z_VQ`vwvi1G6@_5r^-wjlb73GR`PCF?MophJYz-vMH5jq57> zB4?q**h!=%f|Rm$(;yNow%st6>UdR6ou(@ifuJ*UXNRbbT7ExD7L|KDyQ!0B8VLu2 z+bjYb<;@A2A3%NX!_|L?cALlf=S}w14$=%VQULck577LXjDyCV3>s%MfDc+s)E5_s zkKbAD7U8=B0t_#*YOAGhh~f0j8IyvFC_GOBfipc--`3Gz)(5kJQ2#qhm$XfcbJll~ zrYn_#{l|8;UJ_qRxqd7)(tkKvrhv25%D2SqMQ7RfVwxaBBu{MWN)QFuTC+dT9MGAO zW}op`@>=W?4&bniu7xiscNQ$&uf z7G*q}F(kjFvf@`gF|2L^)76v0xTx06AXUt3&A2Hy5u*4h*!d!Dx%oIt5UC(Gm`Ix= zk>qwXX-1bDgJq2WIP&O?NO=u)_Dz?Jh87~tT~wZGkhrE?tqV~{*^x{b?1XAv4V6?g z5pfs4>Lki3m@(!=8&if%mfQk{P{N<$3q(#0^*9>S;9CMG2@EBQ2x2a(_yAi;8zl-- zUzlHH@dmi~er+G`lV`IneyLCeu! z`5P&m1Wj;Oacv(d#Ymz%!jKmcA5U;ZeA*jJ*wF8l=`=A?%snB+tdn8~BH7UG)NRD+ z&~2qTp8i?gByw}u{j*EIk!C*^g2YX+^q3G`k7Mb)3e5%w-O=fAr>$3J*z&B1dug98 z=AjvKNaEBZv0p=}eoZqKc#Iyw=ob}brfZ=CHlF+?VpYN_f6qZ v^U%7&6u;UxfnFN~z%y+&6yL1(ZusB5&qIO3Z}6Y{s^5?4{A2*5Kmh*-zOI_U literal 0 HcmV?d00001 From afd41d5e7e8d6bc5c2d23142e7e9db7d19238927 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Thu, 12 Jan 2023 14:17:25 +0200 Subject: [PATCH 169/316] Fix hardcode for configcheck pod Signed-off-by: Zemtsov Vladimir --- .../factory/config/configcheck/configcheck_pod.go | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/controllers/factory/config/configcheck/configcheck_pod.go b/controllers/factory/config/configcheck/configcheck_pod.go index 00ab95b0..d3a4437e 100644 --- a/controllers/factory/config/configcheck/configcheck_pod.go +++ b/controllers/factory/config/configcheck/configcheck_pod.go @@ -40,15 +40,7 @@ func (cc *ConfigCheck) createVectorConfigCheckPod() *corev1.Pod { { Name: "config-check", Image: cc.Image, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("10m"), - corev1.ResourceMemory: resource.MustParse("10Mi"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100Mi"), - }, + Resources: cc.Resources, }, Args: []string{"validate", "/etc/vector/*.json"}, Env: cc.generateVectorConfigCheckEnvs(), From 0274389f39e6686ec01f39d0cfb7c3d4af5c2ec8 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Thu, 12 Jan 2023 14:19:34 +0200 Subject: [PATCH 170/316] opsy Signed-off-by: Zemtsov Vladimir --- .../factory/config/configcheck/configcheck_pod.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/controllers/factory/config/configcheck/configcheck_pod.go b/controllers/factory/config/configcheck/configcheck_pod.go index d3a4437e..df63eb71 100644 --- a/controllers/factory/config/configcheck/configcheck_pod.go +++ b/controllers/factory/config/configcheck/configcheck_pod.go @@ -18,7 +18,6 @@ package configcheck import ( corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -38,12 +37,11 @@ func (cc *ConfigCheck) createVectorConfigCheckPod() *corev1.Pod { Tolerations: cc.Tolerations, Containers: []corev1.Container{ { - Name: "config-check", - Image: cc.Image, + Name: "config-check", + Image: cc.Image, Resources: cc.Resources, - }, - Args: []string{"validate", "/etc/vector/*.json"}, - Env: cc.generateVectorConfigCheckEnvs(), + Args: []string{"validate", "/etc/vector/*.json"}, + Env: cc.generateVectorConfigCheckEnvs(), Ports: []corev1.ContainerPort{ { Name: "prom-exporter", From ec13ca289c1cb5c4a3d02d9c0831be3aeed48ea2 Mon Sep 17 00:00:00 2001 From: Zemtsov Vladimir Date: Thu, 12 Jan 2023 14:28:01 +0200 Subject: [PATCH 171/316] Update CHANGELOG for release v0.0.12 Signed-off-by: Zemtsov Vladimir --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b26db4c..5f436570 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ### Added -- [[81]](https://github.com/kaasops/vector-operator/pull/81) **Feature** Add control for ConfigCheck params +### v0.0.12 +- [[81]](https://github.com/kaasops/vector-operator/pull/81) **Feature** Add control for ConfigCheck params ### v0.0.11 - [[79]](https://github.com/kaasops/vector-operator/pull/79) **Fix** Remove vector.dev/exclude label From 5d086a8197efd1633073169a32a6acf04faff7b6 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Fri, 13 Jan 2023 16:28:55 +0200 Subject: [PATCH 172/316] fix configcheck cleanup --- .../factory/config/configcheck/configcheck.go | 59 ++++++++----------- .../config/configcheck/configcheck_error.go | 5 -- controllers/factory/pipeline/pipeline.go | 3 +- .../factory/vector/vectoragent/vectoragent.go | 3 +- controllers/pipeline_controller.go | 10 ++-- controllers/vector_controller.go | 4 +- 6 files changed, 35 insertions(+), 49 deletions(-) diff --git a/controllers/factory/config/configcheck/configcheck.go b/controllers/factory/config/configcheck/configcheck.go index e4a325c0..749b0cf5 100644 --- a/controllers/factory/config/configcheck/configcheck.go +++ b/controllers/factory/config/configcheck/configcheck.go @@ -90,25 +90,37 @@ func New( } } -func (cc *ConfigCheck) Run(ctx context.Context) error { +func (cc *ConfigCheck) Run(ctx context.Context) (string, error) { log := log.FromContext(ctx).WithValues("Vector ConfigCheck", cc.Initiator) log.Info("================= Started ConfigCheck =======================") if err := cc.ensureVectorConfigCheckRBAC(ctx); err != nil { - return err + return "", err } cc.Hash = randStringRunes() if err := cc.ensureVectorConfigCheckConfig(ctx); err != nil { - return err + return "", err } - if err := cc.checkVectorConfigCheckPod(ctx); err != nil { - return err + vectorConfigCheckPod := cc.createVectorConfigCheckPod() + + err := k8s.CreatePod(ctx, vectorConfigCheckPod, cc.Client) + if err != nil { + return "", err } - return nil + defer func() { + err = cc.cleanup(ctx, vectorConfigCheckPod) + }() + + reason, err := cc.getCheckResult(ctx, vectorConfigCheckPod) + if err != nil { + return "", err + } + + return reason, err } func (cc *ConfigCheck) ensureVectorConfigCheckRBAC(ctx context.Context) error { @@ -130,22 +142,6 @@ func (cc *ConfigCheck) ensureVectorConfigCheckConfig(ctx context.Context) error } func (cc *ConfigCheck) checkVectorConfigCheckPod(ctx context.Context) error { - vectorConfigCheckPod := cc.createVectorConfigCheckPod() - - err := k8s.CreatePod(ctx, vectorConfigCheckPod, cc.Client) - if err != nil { - return err - } - - err = cc.getCheckResult(ctx, vectorConfigCheckPod) - if err != nil { - return err - } - - err = cc.cleanup(ctx, vectorConfigCheckPod) - if err != nil { - return err - } return nil } @@ -173,7 +169,7 @@ func randStringRunes() string { return string(b) } -func (cc *ConfigCheck) getCheckResult(ctx context.Context, pod *corev1.Pod) (err error) { +func (cc *ConfigCheck) getCheckResult(ctx context.Context, pod *corev1.Pod) (reason string, err error) { log := log.FromContext(ctx).WithValues("Vector ConfigCheck", pod.Name) log.Info("Trying to get configcheck result") @@ -184,7 +180,7 @@ func (cc *ConfigCheck) getCheckResult(ctx context.Context, pod *corev1.Pod) (err if err != nil { log.Error(err, "cannot create Pod event watcher") - return err + return "", err } defer watcher.Stop() @@ -193,7 +189,7 @@ func (cc *ConfigCheck) getCheckResult(ctx context.Context, pod *corev1.Pod) (err select { case e := <-watcher.ResultChan(): if e.Object == nil { - return nil + return "", nil } pod, ok := e.Object.(*corev1.Pod) if !ok { @@ -207,25 +203,22 @@ func (cc *ConfigCheck) getCheckResult(ctx context.Context, pod *corev1.Pod) (err switch pod.Status.Phase { case corev1.PodSucceeded: log.Info("Config Check completed successfully") - return nil + return "", nil case corev1.PodFailed: log.Info("Config Check Failed") reason, err := k8s.GetPodLogs(ctx, pod, cc.ClientSet) if err != nil { - return err + return "", err } - return newValidationError(reason) + return reason, nil } } case <-ctx.Done(): watcher.Stop() - return nil + return "", nil case <-time.After(waitConfigcheckResultTimeout): watcher.Stop() - if err = cc.cleanup(ctx, pod); err != nil { - log.Error(err, "Failed to clean configcheck pod") - } - return ConfigcheckTimeoutError + return "", ConfigcheckTimeoutError } } } diff --git a/controllers/factory/config/configcheck/configcheck_error.go b/controllers/factory/config/configcheck/configcheck_error.go index dfe28c76..63b5ea06 100644 --- a/controllers/factory/config/configcheck/configcheck_error.go +++ b/controllers/factory/config/configcheck/configcheck_error.go @@ -18,14 +18,9 @@ package configcheck import ( "errors" - "fmt" ) var ( ValidationError = errors.New("config validation error") ConfigcheckTimeoutError = errors.New("timeout waiting configcheck pod result") ) - -func newValidationError(reason string) error { - return fmt.Errorf("%w: %s", ValidationError, reason) -} diff --git a/controllers/factory/pipeline/pipeline.go b/controllers/factory/pipeline/pipeline.go index 59c7297d..7e54222a 100644 --- a/controllers/factory/pipeline/pipeline.go +++ b/controllers/factory/pipeline/pipeline.go @@ -72,8 +72,7 @@ func SetSuccessStatus(ctx context.Context, client client.Client, p Pipeline) err return p.UpdateStatus(ctx, client) } -func SetFailedStatus(ctx context.Context, client client.Client, p Pipeline, err error) error { - var reason = err.Error() +func SetFailedStatus(ctx context.Context, client client.Client, p Pipeline, reason string) error { p.SetConfigCheck(false) p.SetReason(&reason) diff --git a/controllers/factory/vector/vectoragent/vectoragent.go b/controllers/factory/vector/vectoragent/vectoragent.go index 68f4616b..81f88b18 100644 --- a/controllers/factory/vector/vectoragent/vectoragent.go +++ b/controllers/factory/vector/vectoragent/vectoragent.go @@ -50,9 +50,8 @@ func (ctrl *Controller) SetSucceesStatus(ctx context.Context) error { return k8s.UpdateStatus(ctx, ctrl.Vector, ctrl.Client) } -func (ctrl *Controller) SetFailedStatus(ctx context.Context, err error) error { +func (ctrl *Controller) SetFailedStatus(ctx context.Context, reason string) error { var status = false - var reason = err.Error() ctrl.Vector.Status.ConfigCheckResult = &status ctrl.Vector.Status.Reason = &reason diff --git a/controllers/pipeline_controller.go b/controllers/pipeline_controller.go index f29af5cc..7a8edaa5 100644 --- a/controllers/pipeline_controller.go +++ b/controllers/pipeline_controller.go @@ -18,7 +18,6 @@ package controllers import ( "context" - "errors" "sync" "time" @@ -113,7 +112,7 @@ func (r *PipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c byteConfig, err := configBuilder.GetByteConfigWithValidate() if err != nil { - if err := pipeline.SetFailedStatus(ctx, r.Client, pipelineCR, err); err != nil { + if err := pipeline.SetFailedStatus(ctx, r.Client, pipelineCR, err.Error()); err != nil { return ctrl.Result{}, err } if err = pipeline.SetLastAppliedPipelineStatus(ctx, r.Client, pipelineCR); err != nil { @@ -176,9 +175,9 @@ func (r *PipelineReconciler) runPipelineCheck(ctx context.Context, p pipeline.Pi configCheck.Initiator = configcheck.ConfigCheckInitiatorPipieline defer r.PipelineCheckWG.Done() // Start ConfigCheck - err := configCheck.Run(ctx) - if errors.Is(err, configcheck.ValidationError) { - if err = pipeline.SetFailedStatus(ctx, r.Client, p, err); err != nil { + reason, err := configCheck.Run(ctx) + if reason != "" { + if err = pipeline.SetFailedStatus(ctx, r.Client, p, reason); err != nil { log.Error(err, "Failed to set pipeline status") return } @@ -188,6 +187,7 @@ func (r *PipelineReconciler) runPipelineCheck(ctx context.Context, p pipeline.Pi } return } + if err != nil { log.Error(err, "Configcheck error") return diff --git a/controllers/vector_controller.go b/controllers/vector_controller.go index 42f38931..bb7d7e47 100644 --- a/controllers/vector_controller.go +++ b/controllers/vector_controller.go @@ -187,9 +187,9 @@ func createOrUpdateVector(ctx context.Context, client client.Client, clientset * vaCtrl.Vector, ) configCheck.Initiator = configcheck.ConfigCheckInitiatorVector - err := configCheck.Run(ctx) + reason, err := configCheck.Run(ctx) if errors.Is(err, configcheck.ValidationError) { - if err := vaCtrl.SetFailedStatus(ctx, err); err != nil { + if err := vaCtrl.SetFailedStatus(ctx, reason); err != nil { return ctrl.Result{}, err } return ctrl.Result{}, err From cfe919d42db845cf196f8d053816511b93c1754a Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Fri, 13 Jan 2023 16:31:53 +0200 Subject: [PATCH 173/316] release v0.0.13 --- CHANGELOG.md | 6 +++-- helm/charts/vector-operator/Chart.yaml | 4 +-- helm/index.yaml | 31 ++++++++++++++++------- helm/packages/vector-operator-0.0.13.tgz | Bin 0 -> 15043 bytes 4 files changed, 28 insertions(+), 13 deletions(-) create mode 100644 helm/packages/vector-operator-0.0.13.tgz diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b26db4c..cf8e5180 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ -### Added -- [[81]](https://github.com/kaasops/vector-operator/pull/81) **Feature** Add control for ConfigCheck params +### v0.0.13 +- [[83]](https://github.com/kaasops/vector-operator/pull/83) **Fix** Fix configcheck pods cleanup +### v0.0.12 +- [[81]](https://github.com/kaasops/vector-operator/pull/81) **Feature** Add control for ConfigCheck params ### v0.0.11 - [[79]](https://github.com/kaasops/vector-operator/pull/79) **Fix** Remove vector.dev/exclude label diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index 3d58c51d..df21ab6f 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.12 +version: 0.0.13 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.0.12" +appVersion: "v0.0.13" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/index.yaml b/helm/index.yaml index f0e616be..236d7bb8 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.0.13 + created: "2023-01-13T16:31:25.066259888+02:00" + description: A Helm chart to install Vector Operator + digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.13.tgz + version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2023-01-12T14:02:14.473422+02:00" + created: "2023-01-13T16:31:25.065670061+02:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2023-01-12T14:02:14.472722+02:00" + created: "2023-01-13T16:31:25.063547713+02:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2023-01-12T14:02:14.472047+02:00" + created: "2023-01-13T16:31:25.063042068+02:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2023-01-12T14:02:14.477729+02:00" + created: "2023-01-13T16:31:25.068234376+02:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2023-01-12T14:02:14.476953+02:00" + created: "2023-01-13T16:31:25.067722577+02:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2023-01-12T14:02:14.476104+02:00" + created: "2023-01-13T16:31:25.06723177+02:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2023-01-12T14:02:14.47414+02:00" + created: "2023-01-13T16:31:25.066747139+02:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2023-01-12T14:02:14.471166+02:00" + created: "2023-01-13T16:31:25.062489433+02:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -105,4 +118,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2023-01-12T14:02:14.470118+02:00" +generated: "2023-01-13T16:31:25.061715304+02:00" diff --git a/helm/packages/vector-operator-0.0.13.tgz b/helm/packages/vector-operator-0.0.13.tgz new file mode 100644 index 0000000000000000000000000000000000000000..850b59eb171641af7fc2e693fd6ee4beef90a406 GIT binary patch literal 15043 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

A=ySB#Y`hlo7Mx|_{h3v46bgLC4jA? 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 Jz6^tY`G4$N#Cre$ From c8d4e5101a5be682100b4c0267b928a8cfbf9183 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Mon, 3 Apr 2023 13:12:04 +0300 Subject: [PATCH 189/316] fix condition type --- controllers/factory/config/config_build.go | 12 ++++++++---- controllers/factory/config/types.go | 7 ++++++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/controllers/factory/config/config_build.go b/controllers/factory/config/config_build.go index 9981c54a..f6a2ae63 100644 --- a/controllers/factory/config/config_build.go +++ b/controllers/factory/config/config_build.go @@ -40,6 +40,7 @@ const ( DefaultSourceName = "defaultSource" PodSelectorType = "pod_labels" NamespaceSelectorType = "ns_labels" + OptimizationConditionType = "vrl" ) var ( @@ -319,10 +320,13 @@ func (b *Builder) optimizeVectorConfig(config *VectorConfig) error { optimizationRequired = true config.Transforms = append(config.Transforms, &Transform{ - Name: source.Name, - Inputs: []string{OptimizedKubernetesSourceName}, - Type: FilterTransformType, - Condition: generateVrlFilter(source.ExtraLabelSelector, PodSelectorType) + "&&" + generateVrlFilter(source.ExtraNamespaceLabelSelector, NamespaceSelectorType), + Name: source.Name, + Inputs: []string{OptimizedKubernetesSourceName}, + Type: FilterTransformType, + Condition: Condition{ + Type: OptimizationConditionType, + Source: generateVrlFilter(source.ExtraLabelSelector, PodSelectorType) + "&&" + generateVrlFilter(source.ExtraNamespaceLabelSelector, NamespaceSelectorType), + }, }) continue } diff --git a/controllers/factory/config/types.go b/controllers/factory/config/types.go index 4b5173d0..d4dfdab1 100644 --- a/controllers/factory/config/types.go +++ b/controllers/factory/config/types.go @@ -37,10 +37,15 @@ type Transform struct { Name string Type string `mapper:"type"` Inputs []string `mapper:"inputs"` - Condition string `mapper:"condition,omitempty"` + Condition Condition `mapper:"condition,omitempty"` Options map[string]interface{} `mapstructure:",remain"` } +type Condition struct { + Type string `mapper:"type,omitempty"` + Source string `mapper:"source,omitempty"` +} + type Sink struct { Name string Type string `mapper:"type"` From 66952b68ff8dc9eb7abeea950dbbf0e36ef61853 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Mon, 3 Apr 2023 13:16:07 +0300 Subject: [PATCH 190/316] release v0.0.17 --- CHANGELOG.md | 6 ++-- helm/charts/vector-operator/Chart.yaml | 4 +-- helm/index.yaml | 39 +++++++++++++++-------- helm/packages/vector-operator-0.0.17.tgz | Bin 0 -> 15147 bytes 4 files changed, 32 insertions(+), 17 deletions(-) create mode 100644 helm/packages/vector-operator-0.0.17.tgz diff --git a/CHANGELOG.md b/CHANGELOG.md index b793a960..44cb7daa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,10 @@ +### v0.0.17 +- [[93](https://github.com/kaasops/vector-operator/pull/93] **Fix** Fix Condition type ### v0.0.16 -- [[85](https://github.com/kaasops/vector-operator/pull/92] **Fix** Added rbac for ServiceMonitros +- [[92](https://github.com/kaasops/vector-operator/pull/92] **Fix** Added rbac for ServiceMonitros ### v0.0.15 -- [[85](https://github.com/kaasops/vector-operator/pull/89] **Feature** Added experemental config optimization option +- [[89](https://github.com/kaasops/vector-operator/pull/89] **Feature** Added experemental config optimization option ### v0.0.14 - [[85](https://github.com/kaasops/vector-operator/pull/85)] **Feature** Add metrics exporter and ServiceMonitor creation diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index df5f77e5..8a8a7c38 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.16 +version: 0.0.17 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.0.16" +appVersion: "v0.0.17" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/index.yaml b/helm/index.yaml index 435db4bb..ab88644d 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.0.17 + created: "2023-04-03T13:15:50.180074433+03:00" + description: A Helm chart to install Vector Operator + digest: c6e78c49a15c4f33c5d434a52931eca9f7f84f58fc5f7119a03a9b2960e6d541 + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.17.tgz + version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2023-03-30T17:24:44.123711193+03:00" + created: "2023-04-03T13:15:50.179475689+03:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2023-03-30T17:24:44.123153464+03:00" + created: "2023-04-03T13:15:50.178750391+03:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2023-03-30T17:24:44.122149227+03:00" + created: "2023-04-03T13:15:50.177969298+03:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2023-03-30T17:24:44.121512327+03:00" + created: "2023-04-03T13:15:50.177184984+03:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2023-03-30T17:24:44.120932497+03:00" + created: "2023-04-03T13:15:50.175242447+03:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2023-03-30T17:24:44.120353616+03:00" + created: "2023-04-03T13:15:50.174198698+03:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2023-03-30T17:24:44.119840946+03:00" + created: "2023-04-03T13:15:50.173446239+03:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2023-03-30T17:24:44.125560479+03:00" + created: "2023-04-03T13:15:50.183417206+03:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2023-03-30T17:24:44.125102851+03:00" + created: "2023-04-03T13:15:50.18207266+03:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2023-03-30T17:24:44.124649556+03:00" + created: "2023-04-03T13:15:50.181469292+03:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2023-03-30T17:24:44.124169718+03:00" + created: "2023-04-03T13:15:50.180677618+03:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -146,7 +159,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2023-03-30T17:24:44.119278546+03:00" + created: "2023-04-03T13:15:50.172681174+03:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -157,4 +170,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2023-03-30T17:24:44.118265221+03:00" +generated: "2023-04-03T13:15:50.171867489+03:00" diff --git a/helm/packages/vector-operator-0.0.17.tgz b/helm/packages/vector-operator-0.0.17.tgz new file mode 100644 index 0000000000000000000000000000000000000000..2d0a38934f94bed23d8d76e292e558a54520da72 GIT binary patch literal 15147 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 literal 0 HcmV?d00001 From b77bd67edcfbd1c841ac569149e617e4e6caf9e2 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Mon, 3 Apr 2023 13:59:10 +0300 Subject: [PATCH 191/316] change tranform condition type to interface{} --- controllers/factory/config/config_build.go | 11 ++++------- controllers/factory/config/types.go | 7 +------ 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/controllers/factory/config/config_build.go b/controllers/factory/config/config_build.go index f6a2ae63..dbf152d8 100644 --- a/controllers/factory/config/config_build.go +++ b/controllers/factory/config/config_build.go @@ -320,13 +320,10 @@ func (b *Builder) optimizeVectorConfig(config *VectorConfig) error { optimizationRequired = true config.Transforms = append(config.Transforms, &Transform{ - Name: source.Name, - Inputs: []string{OptimizedKubernetesSourceName}, - Type: FilterTransformType, - Condition: Condition{ - Type: OptimizationConditionType, - Source: generateVrlFilter(source.ExtraLabelSelector, PodSelectorType) + "&&" + generateVrlFilter(source.ExtraNamespaceLabelSelector, NamespaceSelectorType), - }, + Name: source.Name, + Inputs: []string{OptimizedKubernetesSourceName}, + Type: FilterTransformType, + Condition: generateVrlFilter(source.ExtraLabelSelector, PodSelectorType) + "&&" + generateVrlFilter(source.ExtraNamespaceLabelSelector, NamespaceSelectorType), }) continue } diff --git a/controllers/factory/config/types.go b/controllers/factory/config/types.go index d4dfdab1..aeeba195 100644 --- a/controllers/factory/config/types.go +++ b/controllers/factory/config/types.go @@ -37,15 +37,10 @@ type Transform struct { Name string Type string `mapper:"type"` Inputs []string `mapper:"inputs"` - Condition Condition `mapper:"condition,omitempty"` + Condition interface{} `mapper:"condition,omitempty"` Options map[string]interface{} `mapstructure:",remain"` } -type Condition struct { - Type string `mapper:"type,omitempty"` - Source string `mapper:"source,omitempty"` -} - type Sink struct { Name string Type string `mapper:"type"` From d41fd436fd570901dc591c322c80f0e28fe8482b Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Mon, 3 Apr 2023 14:04:20 +0300 Subject: [PATCH 192/316] re-release v0.0.17 --- helm/index.yaml | 30 +++++++++++------------ helm/packages/vector-operator-0.0.17.tgz | Bin 15147 -> 15146 bytes 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/helm/index.yaml b/helm/index.yaml index ab88644d..dd14dde6 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -3,9 +3,9 @@ entries: vector-operator: - apiVersion: v2 appVersion: v0.0.17 - created: "2023-04-03T13:15:50.180074433+03:00" + created: "2023-04-03T14:03:52.236459284+03:00" description: A Helm chart to install Vector Operator - digest: c6e78c49a15c4f33c5d434a52931eca9f7f84f58fc5f7119a03a9b2960e6d541 + digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator name: vector-operator sources: @@ -16,7 +16,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2023-04-03T13:15:50.179475689+03:00" + created: "2023-04-03T14:03:52.235670764+03:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -29,7 +29,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2023-04-03T13:15:50.178750391+03:00" + created: "2023-04-03T14:03:52.235066441+03:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -42,7 +42,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2023-04-03T13:15:50.177969298+03:00" + created: "2023-04-03T14:03:52.234497075+03:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -55,7 +55,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2023-04-03T13:15:50.177184984+03:00" + created: "2023-04-03T14:03:52.233884577+03:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -68,7 +68,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2023-04-03T13:15:50.175242447+03:00" + created: "2023-04-03T14:03:52.232852345+03:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -81,7 +81,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2023-04-03T13:15:50.174198698+03:00" + created: "2023-04-03T14:03:52.232232223+03:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -94,7 +94,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2023-04-03T13:15:50.173446239+03:00" + created: "2023-04-03T14:03:52.231722664+03:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -107,7 +107,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2023-04-03T13:15:50.183417206+03:00" + created: "2023-04-03T14:03:52.238947556+03:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -120,7 +120,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2023-04-03T13:15:50.18207266+03:00" + created: "2023-04-03T14:03:52.238471086+03:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -133,7 +133,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2023-04-03T13:15:50.181469292+03:00" + created: "2023-04-03T14:03:52.237449353+03:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -146,7 +146,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2023-04-03T13:15:50.180677618+03:00" + created: "2023-04-03T14:03:52.236944496+03:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -159,7 +159,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2023-04-03T13:15:50.172681174+03:00" + created: "2023-04-03T14:03:52.231249434+03:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -170,4 +170,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2023-04-03T13:15:50.171867489+03:00" +generated: "2023-04-03T14:03:52.230678731+03:00" diff --git a/helm/packages/vector-operator-0.0.17.tgz b/helm/packages/vector-operator-0.0.17.tgz index 2d0a38934f94bed23d8d76e292e558a54520da72..8aafdab19df2f45c37f66c16f47a764998d69ab1 100644 GIT binary patch literal 15146 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 From 15897188650d28b3a4e51fbd4f05b9478a5796bf Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Mon, 3 Apr 2023 14:42:29 +0300 Subject: [PATCH 193/316] added imagepullpolicy to agent --- api/v1alpha1/vector_types.go | 2 ++ config/crd/bases/observability.kaasops.io_vectors.yaml | 3 +++ .../factory/vector/vectoragent/vectoragent_daemonset.go | 1 + 3 files changed, 6 insertions(+) diff --git a/api/v1alpha1/vector_types.go b/api/v1alpha1/vector_types.go index 57716bbb..7401a571 100644 --- a/api/v1alpha1/vector_types.go +++ b/api/v1alpha1/vector_types.go @@ -57,6 +57,8 @@ type VectorAgent struct { // see http://kubernetes.io/docs/user-guide/images#specifying-imagepullsecrets-on-a-pod // +optional ImagePullSecrets []v1.LocalObjectReference `json:"imagePullSecrets,omitempty"` + // ImagePullPolicy of pods + ImagePullPolicy v1.PullPolicy `json:"imagePullPolicy,omitempty"` // Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ // if not specified - default setting will be used // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Resources",xDescriptors="urn:alm:descriptor:com.tectonic.ui:resourceRequirements" diff --git a/config/crd/bases/observability.kaasops.io_vectors.yaml b/config/crd/bases/observability.kaasops.io_vectors.yaml index 1c88c6fd..187f4348 100644 --- a/config/crd/bases/observability.kaasops.io_vectors.yaml +++ b/config/crd/bases/observability.kaasops.io_vectors.yaml @@ -2046,6 +2046,9 @@ spec: description: Image - docker image settings for Vector Agent if no specified operator uses default config version type: string + imagePullPolicy: + description: ImagePullPolicy of pods + type: string imagePullSecrets: description: ImagePullSecrets An optional list of references to secrets in the same namespace to use for pulling images from diff --git a/controllers/factory/vector/vectoragent/vectoragent_daemonset.go b/controllers/factory/vector/vectoragent/vectoragent_daemonset.go index de9bd323..5f49b3f5 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_daemonset.go +++ b/controllers/factory/vector/vectoragent/vectoragent_daemonset.go @@ -59,6 +59,7 @@ func (ctrl *Controller) createVectorAgentDaemonSet() *appsv1.DaemonSet { VolumeMounts: ctrl.generateVectorAgentVolumeMounts(), Resources: ctrl.Vector.Spec.Agent.Resources, SecurityContext: &corev1.SecurityContext{}, + ImagePullPolicy: ctrl.Vector.Spec.Agent.ImagePullPolicy, }, }, }, From 417a5ed4b99e805fcebe3a887479871c4c23dcc9 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Mon, 3 Apr 2023 14:44:53 +0300 Subject: [PATCH 194/316] release v0.0.18 --- CHANGELOG.md | 4 +++ helm/charts/vector-operator/Chart.yaml | 4 +-- helm/index.yaml | 41 +++++++++++++++-------- helm/packages/vector-operator-0.0.18.tgz | Bin 0 -> 15147 bytes 4 files changed, 33 insertions(+), 16 deletions(-) create mode 100644 helm/packages/vector-operator-0.0.18.tgz diff --git a/CHANGELOG.md b/CHANGELOG.md index 44cb7daa..7cead5d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ +### v0.0.18 +- [[95](https://github.com/kaasops/vector-operator/pull/95] **Fix** Added ImagePullPolicy to Vector Agent + ### v0.0.17 - [[93](https://github.com/kaasops/vector-operator/pull/93] **Fix** Fix Condition type + ### v0.0.16 - [[92](https://github.com/kaasops/vector-operator/pull/92] **Fix** Added rbac for ServiceMonitros diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index 8a8a7c38..95a0f492 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.17 +version: 0.0.18 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.0.17" +appVersion: "v0.0.18" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/index.yaml b/helm/index.yaml index dd14dde6..63d49eda 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.0.18 + created: "2023-04-03T14:44:38.760068243+03:00" + description: A Helm chart to install Vector Operator + digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.18.tgz + version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2023-04-03T14:03:52.236459284+03:00" + created: "2023-04-03T14:44:38.759446173+03:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2023-04-03T14:03:52.235670764+03:00" + created: "2023-04-03T14:44:38.758738355+03:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2023-04-03T14:03:52.235066441+03:00" + created: "2023-04-03T14:44:38.757969982+03:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2023-04-03T14:03:52.234497075+03:00" + created: "2023-04-03T14:44:38.757263131+03:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2023-04-03T14:03:52.233884577+03:00" + created: "2023-04-03T14:44:38.756536034+03:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2023-04-03T14:03:52.232852345+03:00" + created: "2023-04-03T14:44:38.755345946+03:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2023-04-03T14:03:52.232232223+03:00" + created: "2023-04-03T14:44:38.754592094+03:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2023-04-03T14:03:52.231722664+03:00" + created: "2023-04-03T14:44:38.753977849+03:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2023-04-03T14:03:52.238947556+03:00" + created: "2023-04-03T14:44:38.762778507+03:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2023-04-03T14:03:52.238471086+03:00" + created: "2023-04-03T14:44:38.762192551+03:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2023-04-03T14:03:52.237449353+03:00" + created: "2023-04-03T14:44:38.761634813+03:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -146,7 +159,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2023-04-03T14:03:52.236944496+03:00" + created: "2023-04-03T14:44:38.76103498+03:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -159,7 +172,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2023-04-03T14:03:52.231249434+03:00" + created: "2023-04-03T14:44:38.753348505+03:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -170,4 +183,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2023-04-03T14:03:52.230678731+03:00" +generated: "2023-04-03T14:44:38.752667667+03:00" diff --git a/helm/packages/vector-operator-0.0.18.tgz b/helm/packages/vector-operator-0.0.18.tgz new file mode 100644 index 0000000000000000000000000000000000000000..f4f3defcd2fe5e8fa149daee5aa334280a6a2ab6 GIT binary patch literal 15147 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 literal 0 HcmV?d00001 From aa01000282e5b1238489568a43aaa0dc6293377e Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Wed, 5 Apr 2023 23:18:26 +0300 Subject: [PATCH 195/316] check if ServiceMonitor crd exist in cluster --- .../observability_v1alpha1_vector.yaml | 2 +- controllers/factory/utils/k8s/k8s.go | 20 ++++++++++++++++ .../vectoragent/vectoragent_controller.go | 8 ++++++- controllers/vector_controller.go | 24 ++++++++++++++----- main.go | 11 ++++++++- 5 files changed, 56 insertions(+), 9 deletions(-) diff --git a/config/samples/observability_v1alpha1_vector.yaml b/config/samples/observability_v1alpha1_vector.yaml index 1c388f44..476e2538 100644 --- a/config/samples/observability_v1alpha1_vector.yaml +++ b/config/samples/observability_v1alpha1_vector.yaml @@ -7,6 +7,6 @@ spec: optimizeKubeSourceConfig: true agent: image: "timberio/vector:0.28.1-distroless-libc" - internalMetrics: false + internalMetrics: true api: enabled: false diff --git a/controllers/factory/utils/k8s/k8s.go b/controllers/factory/utils/k8s/k8s.go index 186399b9..a50e758e 100644 --- a/controllers/factory/utils/k8s/k8s.go +++ b/controllers/factory/utils/k8s/k8s.go @@ -31,6 +31,7 @@ import ( api_errors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/discovery" "k8s.io/client-go/kubernetes" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -492,3 +493,22 @@ func UpdateStatus(ctx context.Context, obj client.Object, c client.Client) error func NamespaceNameToLabel(namespace string) string { return "kubernetes.io/metadata.name=" + namespace } + +// ResourceExists returns true if the given resource kind exists +// in the given api groupversion +func ResourceExists(dc discovery.DiscoveryInterface, apiGroupVersion, kind string) (bool, error) { + _, apiLists, err := dc.ServerGroupsAndResources() + if err != nil { + return false, err + } + for _, apiList := range apiLists { + if apiList.GroupVersion == apiGroupVersion { + for _, r := range apiList.APIResources { + if r.Kind == kind { + return true, nil + } + } + } + } + return false, nil +} diff --git a/controllers/factory/vector/vectoragent/vectoragent_controller.go b/controllers/factory/vector/vectoragent/vectoragent_controller.go index 6625565a..6a3a0bd6 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_controller.go +++ b/controllers/factory/vector/vectoragent/vectoragent_controller.go @@ -20,6 +20,7 @@ import ( "context" "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" + monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/utils/pointer" "sigs.k8s.io/controller-runtime/pkg/log" @@ -29,6 +30,11 @@ func (ctrl *Controller) EnsureVectorAgent(ctx context.Context, configOnly bool) log := log.FromContext(ctx).WithValues("vector-agent", ctrl.Vector.Name) log.Info("start Reconcile Vector Agent") + monitoringCRD, err := k8s.ResourceExists(ctrl.ClientSet.Discovery(), monitorv1.SchemeGroupVersion.String(), monitorv1.ServiceMonitorsKind) + if err != nil { + return err + } + if err := ctrl.ensureVectorAgentConfig(ctx); err != nil { return err } @@ -43,7 +49,7 @@ func (ctrl *Controller) EnsureVectorAgent(ctx context.Context, configOnly bool) } } - if ctrl.Vector.Spec.Agent.InternalMetrics { + if ctrl.Vector.Spec.Agent.InternalMetrics && monitoringCRD { if err := ctrl.ensureVectorAgentServiceMonitor(ctx); err != nil { return err } diff --git a/controllers/vector_controller.go b/controllers/vector_controller.go index 1dc43ecd..7fa0aa5c 100644 --- a/controllers/vector_controller.go +++ b/controllers/vector_controller.go @@ -26,6 +26,7 @@ import ( "github.com/kaasops/vector-operator/controllers/factory/config/configcheck" "github.com/kaasops/vector-operator/controllers/factory/pipeline" "github.com/kaasops/vector-operator/controllers/factory/utils/hash" + "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" "github.com/kaasops/vector-operator/controllers/factory/vector/vectoragent" appsv1 "k8s.io/api/apps/v1" @@ -35,11 +36,11 @@ import ( api_errors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/discovery" "k8s.io/client-go/kubernetes" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/predicate" @@ -58,6 +59,7 @@ type VectorReconciler struct { Clientset *kubernetes.Clientset PipelineCheckWG *sync.WaitGroup PipelineCheckTimeout time.Duration + DiscoveryClient *discovery.DiscoveryClient } //+kubebuilder:rbac:groups=observability.kaasops.io,resources=vectors,verbs=get;list;watch;create;update;patch;delete @@ -106,7 +108,11 @@ func (r *VectorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr // SetupWithManager sets up the controller with the Manager. func (r *VectorReconciler) SetupWithManager(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). + monitoringCRD, err := k8s.ResourceExists(r.DiscoveryClient, monitorv1.SchemeGroupVersion.String(), monitorv1.ServiceMonitorsKind) + if err != nil { + return err + } + builder := ctrl.NewControllerManagedBy(mgr). For(&vectorv1alpha1.Vector{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})). Watches(&source.Channel{Source: VectorAgentReconciliationSourceChannel}, &handler.EnqueueRequestForObject{}). Owns(&appsv1.DaemonSet{}). @@ -114,10 +120,16 @@ func (r *VectorReconciler) SetupWithManager(mgr ctrl.Manager) error { Owns(&corev1.Secret{}). Owns(&corev1.ServiceAccount{}). Owns(&rbacv1.ClusterRole{}). - Owns(&rbacv1.ClusterRoleBinding{}). - Owns(&monitorv1.ServiceMonitor{}). - WithOptions(controller.Options{MaxConcurrentReconciles: 10}). - Complete(r) + Owns(&rbacv1.ClusterRoleBinding{}) + + if monitoringCRD { + builder.Owns(&monitorv1.ServiceMonitor{}) + } + + if err = builder.Complete(r); err != nil { + return err + } + return nil } func listVectorCustomResourceInstances(ctx context.Context, client client.Client) (vectors []*vectorv1alpha1.Vector, err error) { diff --git a/main.go b/main.go index 7091baaf..cd3d4301 100644 --- a/main.go +++ b/main.go @@ -25,6 +25,7 @@ import ( // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) // to ensure that exec-entrypoint and run can make use of them. + "k8s.io/client-go/discovery" "k8s.io/client-go/kubernetes" _ "k8s.io/client-go/plugin/pkg/client/auth" @@ -91,7 +92,14 @@ func main() { clientset, err := kubernetes.NewForConfig(config) if err != nil { - panic(err) + setupLog.Error(err, "unable to create clientset") + os.Exit(1) + } + + dc, err := discovery.NewDiscoveryClientForConfig(config) + if err != nil { + setupLog.Error(err, "unable to create discovery client") + os.Exit(1) } mgrOptions := ctrl.Options{ @@ -121,6 +129,7 @@ func main() { Clientset: clientset, PipelineCheckWG: &pipelineCheckWG, PipelineCheckTimeout: PipelineCheckTimeout, + DiscoveryClient: dc, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Vector") os.Exit(1) From 2a0dc9eb5d15cb86fc4dfa3162f28975af347cce Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Wed, 5 Apr 2023 23:24:27 +0300 Subject: [PATCH 196/316] release v0.0.19 --- CHANGELOG.md | 3 ++ helm/charts/vector-operator/Chart.yaml | 4 +-- helm/index.yaml | 43 +++++++++++++++-------- helm/packages/vector-operator-0.0.19.tgz | Bin 0 -> 15148 bytes 4 files changed, 33 insertions(+), 17 deletions(-) create mode 100644 helm/packages/vector-operator-0.0.19.tgz diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cead5d5..0657875c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +### v0.0.19 +- [[97](https://github.com/kaasops/vector-operator/pull/97] **Fix** Check if ServiceMonitor CRD exists + ### v0.0.18 - [[95](https://github.com/kaasops/vector-operator/pull/95] **Fix** Added ImagePullPolicy to Vector Agent diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index 95a0f492..b515e0f4 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.18 +version: 0.0.19 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.0.18" +appVersion: "v0.0.19" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/index.yaml b/helm/index.yaml index 63d49eda..d498f58b 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.0.19 + created: "2023-04-05T23:24:09.866759+03:00" + description: A Helm chart to install Vector Operator + digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.19.tgz + version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2023-04-03T14:44:38.760068243+03:00" + created: "2023-04-05T23:24:09.865844+03:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2023-04-03T14:44:38.759446173+03:00" + created: "2023-04-05T23:24:09.864065+03:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2023-04-03T14:44:38.758738355+03:00" + created: "2023-04-05T23:24:09.862163+03:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2023-04-03T14:44:38.757969982+03:00" + created: "2023-04-05T23:24:09.860555+03:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2023-04-03T14:44:38.757263131+03:00" + created: "2023-04-05T23:24:09.858619+03:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2023-04-03T14:44:38.756536034+03:00" + created: "2023-04-05T23:24:09.857047+03:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2023-04-03T14:44:38.755345946+03:00" + created: "2023-04-05T23:24:09.855368+03:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2023-04-03T14:44:38.754592094+03:00" + created: "2023-04-05T23:24:09.853282+03:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2023-04-03T14:44:38.753977849+03:00" + created: "2023-04-05T23:24:09.851932+03:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2023-04-03T14:44:38.762778507+03:00" + created: "2023-04-05T23:24:09.871334+03:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2023-04-03T14:44:38.762192551+03:00" + created: "2023-04-05T23:24:09.870205+03:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -146,7 +159,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2023-04-03T14:44:38.761634813+03:00" + created: "2023-04-05T23:24:09.869049+03:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -159,7 +172,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2023-04-03T14:44:38.76103498+03:00" + created: "2023-04-05T23:24:09.867919+03:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -172,7 +185,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2023-04-03T14:44:38.753348505+03:00" + created: "2023-04-05T23:24:09.850589+03:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -183,4 +196,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2023-04-03T14:44:38.752667667+03:00" +generated: "2023-04-05T23:24:09.848871+03:00" diff --git a/helm/packages/vector-operator-0.0.19.tgz b/helm/packages/vector-operator-0.0.19.tgz new file mode 100644 index 0000000000000000000000000000000000000000..6bed42e5990deda8f7e87c049bf5c0b04ea2f193 GIT binary patch literal 15148 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 Date: Wed, 19 Apr 2023 15:31:50 +0300 Subject: [PATCH 197/316] added vector csv --- ...vector-operator.clusterserviceversion.yaml | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 config/manifests/bases/vector-operator.clusterserviceversion.yaml diff --git a/config/manifests/bases/vector-operator.clusterserviceversion.yaml b/config/manifests/bases/vector-operator.clusterserviceversion.yaml new file mode 100644 index 00000000..16fb45f6 --- /dev/null +++ b/config/manifests/bases/vector-operator.clusterserviceversion.yaml @@ -0,0 +1,75 @@ +apiVersion: operators.coreos.com/v1alpha1 +kind: ClusterServiceVersion +metadata: + annotations: + alm-examples: '[]' + capabilities: Basic Install + name: vector-operator.v0.0.0 + namespace: placeholder +spec: + apiservicedefinitions: {} + customresourcedefinitions: + owned: + - description: ClusterVectorPipeline is the Schema for the clustervectorpipelines + API + displayName: Cluster Vector Pipeline + kind: ClusterVectorPipeline + name: clustervectorpipelines.observability.kaasops.io + version: v1alpha1 + - description: VectorPipeline is the Schema for the vectorpipelines API + displayName: Vector Pipeline + kind: VectorPipeline + name: vectorpipelines.observability.kaasops.io + version: v1alpha1 + - description: Vector is the Schema for the vectors API + displayName: Vector + kind: Vector + name: vectors.observability.kaasops.io + specDescriptors: + - description: Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + if not specified - default setting will be used + displayName: Resources + path: agent.configCheck.resources + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:resourceRequirements + - description: Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + if not specified - default setting will be used + displayName: Resources + path: agent.resources + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:resourceRequirements + version: v1alpha1 + description: The operator deploys and configures a vector agent daemonset on every + node to collect container and application logs from the node file system + displayName: vector-operator + icon: + - base64data: "" + mediatype: "" + install: + spec: + deployments: null + strategy: "" + installModes: + - supported: false + type: OwnNamespace + - supported: false + type: SingleNamespace + - supported: false + type: MultiNamespace + - supported: true + type: AllNamespaces + keywords: + - vector + - observability + - logging + links: + - name: Vector Operator + url: https://vector-operator.domain + maintainers: + - email: info@kaasops.io + name: kaasops + maturity: alpha + provider: + name: kaasops.io + url: https://kaasops.io/ + version: 0.0.0 From e44a3c479aee3f55622bc45b6ff526a1ef955819 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Wed, 19 Apr 2023 15:43:14 +0300 Subject: [PATCH 198/316] fix vp and cvp rbac markers --- config/rbac/role.yaml | 29 +++++++++++++++++++++++++++++ controllers/pipeline_controller.go | 4 ++++ 2 files changed, 33 insertions(+) diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 187fbbb9..0c611b03 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -5,6 +5,35 @@ metadata: creationTimestamp: null name: manager-role rules: +- apiGroups: + - observability.kaasops.io + resources: + - clustervectorpipelines + - vectorpipelines + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - observability.kaasops.io + resources: + - clustervectorpipelines/finalizers + - vectorpipelines/finalizers + verbs: + - update +- apiGroups: + - observability.kaasops.io + resources: + - clustervectorpipelines/status + - vectorpipelines/status + verbs: + - get + - patch + - update - apiGroups: - observability.kaasops.io resources: diff --git a/controllers/pipeline_controller.go b/controllers/pipeline_controller.go index 12d02a11..5c020218 100644 --- a/controllers/pipeline_controller.go +++ b/controllers/pipeline_controller.go @@ -49,6 +49,10 @@ type PipelineReconciler struct { PipelineDeleteEventTimeout time.Duration } +//+kubebuilder:rbac:groups=observability.kaasops.io,resources=vectorpipelines;clustervectorpipelines,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=observability.kaasops.io,resources=vectorpipelines/status;clustervectorpipelines/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=observability.kaasops.io,resources=vectorpipelines/finalizers;clustervectorpipelines/finalizers,verbs=update + var VectorAgentReconciliationSourceChannel = make(chan event.GenericEvent) const PipelineDeleteEventTimeout time.Duration = 3 * time.Second From e552925137395e939b709255eb0ced38bd76aa86 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Thu, 20 Apr 2023 16:58:27 +0300 Subject: [PATCH 199/316] fix rbac autogen --- config/rbac/role.yaml | 72 +++++++++++++++++++ controllers/vector_controller.go | 15 ++-- .../templates/clusterrole.yaml | 1 + 3 files changed, 79 insertions(+), 9 deletions(-) diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 0c611b03..f716aac6 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -5,6 +5,54 @@ metadata: creationTimestamp: null name: manager-role rules: +- apiGroups: + - "" + resources: + - sercrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - serviceaccounts + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resources: + - daemonsets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - observability.kaasops.io resources: @@ -60,3 +108,27 @@ rules: - get - patch - update +- apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterrolebindings + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterroles + verbs: + - create + - delete + - get + - list + - patch + - update + - watch diff --git a/controllers/vector_controller.go b/controllers/vector_controller.go index 7fa0aa5c..7cd411c8 100644 --- a/controllers/vector_controller.go +++ b/controllers/vector_controller.go @@ -66,15 +66,12 @@ type VectorReconciler struct { //+kubebuilder:rbac:groups=observability.kaasops.io,resources=vectors/status,verbs=get;update;patch //+kubebuilder:rbac:groups=observability.kaasops.io,resources=vectors/finalizers,verbs=update -// Reconcile is part of the main kubernetes reconciliation loop which aims to -// move the current state of the cluster closer to the desired state. -// TODO(user): Modify the Reconcile function to compare the state specified by -// the Vector object against the actual cluster state, and then -// perform operations to make the cluster state reflect the state specified by -// the user. -// -// For more details, check Reconcile and its Result here: -// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.12.2/pkg/reconcile +// +kubebuilder:rbac:groups="",resources=serviceaccounts,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups="",resources=sercrets,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups="",resources=services,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=apps,resources=daemonsets,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups="rbac.authorization.k8s.io",resources=clusterrolebindings,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups="rbac.authorization.k8s.io",resources=clusterroles,verbs=get;list;watch;create;update;patch;delete func (r *VectorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { _ = log.FromContext(ctx) diff --git a/helm/charts/vector-operator/templates/clusterrole.yaml b/helm/charts/vector-operator/templates/clusterrole.yaml index cb9c9d10..2bd00db9 100644 --- a/helm/charts/vector-operator/templates/clusterrole.yaml +++ b/helm/charts/vector-operator/templates/clusterrole.yaml @@ -92,6 +92,7 @@ rules: - clustervectorpipelines/status - vectorpipelines/status - vectors/status + - vectors/finalizers verbs: - get - patch From 03fd72226229a0961e3d60b7ea1297e60f1d9fa5 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Thu, 20 Apr 2023 17:02:21 +0300 Subject: [PATCH 200/316] release v0.0.20 --- CHANGELOG.md | 3 ++ helm/charts/vector-operator/Chart.yaml | 4 +- helm/index.yaml | 45 +++++++++++++++-------- helm/packages/vector-operator-0.0.20.tgz | Bin 0 -> 15150 bytes 4 files changed, 34 insertions(+), 18 deletions(-) create mode 100644 helm/packages/vector-operator-0.0.20.tgz diff --git a/CHANGELOG.md b/CHANGELOG.md index 0657875c..58b98ffa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +### v0.0.20 +- [[100](https://github.com/kaasops/vector-operator/pull/100] **Fix** Create csv and fix rbac auto generation + ### v0.0.19 - [[97](https://github.com/kaasops/vector-operator/pull/97] **Fix** Check if ServiceMonitor CRD exists diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index b515e0f4..3061c742 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.19 +version: 0.0.20 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.0.19" +appVersion: "v0.0.20" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/index.yaml b/helm/index.yaml index d498f58b..4cabb374 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.0.20 + created: "2023-04-20T17:02:02.501524249+03:00" + description: A Helm chart to install Vector Operator + digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.20.tgz + version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2023-04-05T23:24:09.866759+03:00" + created: "2023-04-20T17:02:02.500938496+03:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2023-04-05T23:24:09.865844+03:00" + created: "2023-04-20T17:02:02.500242991+03:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2023-04-05T23:24:09.864065+03:00" + created: "2023-04-20T17:02:02.499500281+03:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2023-04-05T23:24:09.862163+03:00" + created: "2023-04-20T17:02:02.498661254+03:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2023-04-05T23:24:09.860555+03:00" + created: "2023-04-20T17:02:02.497449503+03:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2023-04-05T23:24:09.858619+03:00" + created: "2023-04-20T17:02:02.496703243+03:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2023-04-05T23:24:09.857047+03:00" + created: "2023-04-20T17:02:02.495983354+03:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2023-04-05T23:24:09.855368+03:00" + created: "2023-04-20T17:02:02.495277839+03:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2023-04-05T23:24:09.853282+03:00" + created: "2023-04-20T17:02:02.494569368+03:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2023-04-05T23:24:09.851932+03:00" + created: "2023-04-20T17:02:02.49396054+03:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2023-04-05T23:24:09.871334+03:00" + created: "2023-04-20T17:02:02.504385655+03:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -146,7 +159,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2023-04-05T23:24:09.870205+03:00" + created: "2023-04-20T17:02:02.503795536+03:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -159,7 +172,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2023-04-05T23:24:09.869049+03:00" + created: "2023-04-20T17:02:02.503207677+03:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -172,7 +185,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2023-04-05T23:24:09.867919+03:00" + created: "2023-04-20T17:02:02.502200081+03:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -185,7 +198,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2023-04-05T23:24:09.850589+03:00" + created: "2023-04-20T17:02:02.493250453+03:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -196,4 +209,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2023-04-05T23:24:09.848871+03:00" +generated: "2023-04-20T17:02:02.492281093+03:00" diff --git a/helm/packages/vector-operator-0.0.20.tgz b/helm/packages/vector-operator-0.0.20.tgz new file mode 100644 index 0000000000000000000000000000000000000000..b43890263af9678b6253820ca6fa2e0197767d69 GIT binary patch literal 15150 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 Date: Tue, 25 Apr 2023 11:14:10 +0300 Subject: [PATCH 201/316] add containerSecurityContext, volumes, volumeMounts to Vector resource --- .gitignore | 2 + api/v1alpha1/vector_types.go | 15 +- api/v1alpha1/zz_generated.deepcopy.go | 19 + .../observability.kaasops.io_vectors.yaml | 1873 +++++++++++++++- controllers/factory/config/config.go | 2 +- .../vectoragent/vectoragent_daemonset.go | 52 +- .../vector/vectoragent/vectoragent_default.go | 47 +- docs/specification.md | 21 +- .../observability.kaasops.io_vectors.yaml | 1876 ++++++++++++++++- .../vector-operator/templates/deployment.yaml | 4 +- 10 files changed, 3770 insertions(+), 141 deletions(-) diff --git a/.gitignore b/.gitignore index bf6f53ac..ececd2db 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,5 @@ testbin/* *~ .vscode __debug_bin + +vendor diff --git a/api/v1alpha1/vector_types.go b/api/v1alpha1/vector_types.go index 7401a571..44371aff 100644 --- a/api/v1alpha1/vector_types.go +++ b/api/v1alpha1/vector_types.go @@ -73,9 +73,12 @@ type VectorAgent struct { // SecurityContext holds pod-level security attributes and common container settings. // This defaults to the default PodSecurityContext. // +optional - // Tolerations If specified, the pod's tolerations. + SecurityContext *v1.PodSecurityContext `json:"podSecurityContext,omitempty"` + // SecurityContext holds security configuration that will be applied to a container. + // Some fields are present in both SecurityContext and PodSecurityContext. + // When both are set, the values in SecurityContext take precedence. // +optional - SecurityContext *v1.PodSecurityContext `json:"securityContext,omitempty"` + ContainerSecurityContext *v1.SecurityContext `json:"containerSecurityContext,omitempty"` // SchedulerName - defines kubernetes scheduler name // +optional SchedulerName string `json:"schedulerName,omitempty"` @@ -107,6 +110,14 @@ type VectorAgent struct { // +optional InternalMetrics bool `json:"internalMetrics,omitempty"` + // List of volumes that can be mounted by containers belonging to the pod. + // +optional + Volumes []v1.Volume `json:"volumes,omitempty"` + + // Pod volumes to mount into the container's filesystem. + // +optional + VolumeMounts []v1.VolumeMount `json:"volumeMounts,omitempty"` + ConfigCheck ConfigCheck `json:"configCheck,omitempty"` } diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 01b9c888..15fd8c20 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -194,6 +194,11 @@ func (in *VectorAgent) DeepCopyInto(out *VectorAgent) { *out = new(v1.PodSecurityContext) (*in).DeepCopyInto(*out) } + if in.ContainerSecurityContext != nil { + in, out := &in.ContainerSecurityContext, &out.ContainerSecurityContext + *out = new(v1.SecurityContext) + (*in).DeepCopyInto(*out) + } if in.RuntimeClassName != nil { in, out := &in.RuntimeClassName, &out.RuntimeClassName *out = new(string) @@ -214,6 +219,20 @@ func (in *VectorAgent) DeepCopyInto(out *VectorAgent) { } } out.Api = in.Api + if in.Volumes != nil { + in, out := &in.Volumes, &out.Volumes + *out = make([]v1.Volume, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.VolumeMounts != nil { + in, out := &in.VolumeMounts, &out.VolumeMounts + *out = make([]v1.VolumeMount, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } in.ConfigCheck.DeepCopyInto(&out.ConfigCheck) } diff --git a/config/crd/bases/observability.kaasops.io_vectors.yaml b/config/crd/bases/observability.kaasops.io_vectors.yaml index 187f4348..6677c272 100644 --- a/config/crd/bases/observability.kaasops.io_vectors.yaml +++ b/config/crd/bases/observability.kaasops.io_vectors.yaml @@ -1905,6 +1905,177 @@ spec: type: object type: array type: object + containerSecurityContext: + description: SecurityContext holds security configuration that + will be applied to a container. Some fields are present in both + SecurityContext and PodSecurityContext. When both are set, the + values in SecurityContext take precedence. + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation controls whether a + process can gain more privileges than its parent process. + This bool directly controls if the no_new_privs flag will + be set on the container process. AllowPrivilegeEscalation + is true always when the container is: 1) run as Privileged + 2) has CAP_SYS_ADMIN Note that this field cannot be set + when spec.os.name is windows.' + type: boolean + capabilities: + description: The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the + container runtime. Note that this field cannot be set when + spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. Processes in + privileged containers are essentially equivalent to root + on the host. Defaults to false. Note that this field cannot + be set when spec.os.name is windows. + type: boolean + procMount: + description: procMount denotes the type of proc mount to use + for the containers. The default is DefaultProcMount which + uses the container runtime defaults for readonly paths and + masked paths. This requires the ProcMountType feature flag + to be enabled. Note that this field cannot be set when spec.os.name + is windows. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only root filesystem. + Default is false. Note that this field cannot be set when + spec.os.name is windows. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be set + in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a non-root + user. If true, the Kubelet will validate the image at runtime + to ensure that it does not run as UID 0 (root) and fail + to start the container if it does. If unset or false, no + such validation will be performed. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata if + unspecified. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. Note + that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random + SELinux context for each container. May also be set in + PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by this container. + If seccomp options are provided at both the pod & container + level, the container options override the pod options. Note + that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile must + be preconfigured on the node to work. Must be a descending + path, relative to the kubelet's configured seccomp profile + location. Must only be set if type is "Localhost". + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - a + profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile + should be used. Unconfined - no profile should be applied." + type: string + required: + - type + type: object + windowsOptions: + description: The Windows specific settings applied to all + containers. If unspecified, the options from the PodSecurityContext + will be used. If set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is + linux. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named + by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. This field is + alpha-level and will only be honored by components that + enable the WindowsHostProcessContainers feature flag. + Setting this field without the feature flag will result + in errors when validating the Pod. All of a Pod's containers + must have the same effective HostProcess value (it is + not allowed to have a mix of HostProcess containers + and non-HostProcess containers). In addition, if HostProcess + is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set in + PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. + type: string + type: object + type: object dataDir: type: string env: @@ -2067,52 +2238,10 @@ spec: internalMetrics: description: Enable internal metrics exporter type: boolean - podSecurityPolicyName: - description: PodSecurityPolicyName - defines name for podSecurityPolicy - in case of empty value, prefixedName will be used. - type: string - priorityClassName: - description: PriorityClassName assigned to the Pods - type: string - resources: - description: Resources container resource request and limits, - https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - if not specified - default setting will be used - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute - resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, otherwise - to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - runtimeClassName: - description: RuntimeClassName - defines runtime class for kubernetes - pod. https://kubernetes.io/docs/concepts/containers/runtime-class/ - type: string - schedulerName: - description: SchedulerName - defines kubernetes scheduler name - type: string - securityContext: + podSecurityContext: description: SecurityContext holds pod-level security attributes and common container settings. This defaults to the default - PodSecurityContext. Tolerations If specified, the pod's tolerations. + PodSecurityContext. properties: fsGroup: description: "A special supplemental group that applies to @@ -2281,6 +2410,48 @@ spec: type: string type: object type: object + podSecurityPolicyName: + description: PodSecurityPolicyName - defines name for podSecurityPolicy + in case of empty value, prefixedName will be used. + type: string + priorityClassName: + description: PriorityClassName assigned to the Pods + type: string + resources: + description: Resources container resource request and limits, + https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + if not specified - default setting will be used + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, otherwise + to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + runtimeClassName: + description: RuntimeClassName - defines runtime class for kubernetes + pod. https://kubernetes.io/docs/concepts/containers/runtime-class/ + type: string + schedulerName: + description: SchedulerName - defines kubernetes scheduler name + type: string tolerations: description: Tolerations If specified, the pod's tolerations. items: @@ -2322,6 +2493,1620 @@ spec: type: string type: object type: array + volumeMounts: + description: Pod volumes to mount into the container's filesystem. + items: + description: VolumeMount describes a mounting of a Volume within + a container. + properties: + mountPath: + description: Path within the container at which the volume + should be mounted. Must not contain ':'. + type: string + mountPropagation: + description: mountPropagation determines how mounts are + propagated from the host to container and the other way + around. When not set, MountPropagationNone is used. This + field is beta in 1.10. + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write otherwise + (false or unspecified). Defaults to false. + type: boolean + subPath: + description: Path within the volume from which the container's + volume should be mounted. Defaults to "" (volume's root). + type: string + subPathExpr: + description: Expanded path within the volume from which + the container's volume should be mounted. Behaves similarly + to SubPath but environment variable references $(VAR_NAME) + are expanded using the container's environment. Defaults + to "" (volume's root). SubPathExpr and SubPath are mutually + exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + volumes: + description: List of volumes that can be mounted by containers + belonging to the pod. + items: + description: Volume represents a named volume in a pod that + may be accessed by any container in the pod. + properties: + awsElasticBlockStore: + description: 'awsElasticBlockStore represents an AWS Disk + resource that is attached to a kubelet''s host machine + and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + properties: + fsType: + description: 'fsType is the filesystem type of the volume + that you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + partition: + description: 'partition is the partition in the volume + that you want to mount. If omitted, the default is + to mount by volume name. Examples: For volume /dev/sda1, + you specify the partition as "1". Similarly, the volume + partition for /dev/sda is "0" (or you can leave the + property empty).' + format: int32 + type: integer + readOnly: + description: 'readOnly value true will force the readOnly + setting in VolumeMounts. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: boolean + volumeID: + description: 'volumeID is unique ID of the persistent + disk resource in AWS (Amazon EBS volume). More info: + https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: string + required: + - volumeID + type: object + azureDisk: + description: azureDisk represents an Azure Data Disk mount + on the host and bind mount to the pod. + properties: + cachingMode: + description: 'cachingMode is the Host Caching mode: + None, Read Only, Read Write.' + type: string + diskName: + description: diskName is the Name of the data disk in + the blob storage + type: string + diskURI: + description: diskURI is the URI of data disk in the + blob storage + type: string + fsType: + description: fsType is Filesystem type to mount. Must + be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. + type: string + kind: + description: 'kind expected values are Shared: multiple + blob disks per storage account Dedicated: single + blob disk per storage account Managed: azure managed + data disk (only in managed availability set). defaults + to shared' + type: string + readOnly: + description: readOnly Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts. + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + description: azureFile represents an Azure File Service + mount on the host and bind mount to the pod. + properties: + readOnly: + description: readOnly defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretName: + description: secretName is the name of secret that + contains Azure Storage Account Name and Key + type: string + shareName: + description: shareName is the azure share Name + type: string + required: + - secretName + - shareName + type: object + cephfs: + description: cephFS represents a Ceph FS mount on the host + that shares a pod's lifetime + properties: + monitors: + description: 'monitors is Required: Monitors is a collection + of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + items: + type: string + type: array + path: + description: 'path is Optional: Used as the mounted + root, rather than the full Ceph tree, default is /' + type: string + readOnly: + description: 'readOnly is Optional: Defaults to false + (read/write). ReadOnly here will force the ReadOnly + setting in VolumeMounts. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: boolean + secretFile: + description: 'secretFile is Optional: SecretFile is + the path to key ring for User, default is /etc/ceph/user.secret + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + secretRef: + description: 'secretRef is Optional: SecretRef is reference + to the authentication secret for User, default is + empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + type: object + x-kubernetes-map-type: atomic + user: + description: 'user is optional: User is the rados user + name, default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + required: + - monitors + type: object + cinder: + description: 'cinder represents a cinder volume attached + and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + properties: + fsType: + description: 'fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating + system. Examples: "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + readOnly: + description: 'readOnly defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: boolean + secretRef: + description: 'secretRef is optional: points to a secret + object containing parameters used to connect to OpenStack.' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + description: 'volumeID used to identify the volume in + cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + required: + - volumeID + type: object + configMap: + description: configMap represents a configMap that should + populate this volume + properties: + defaultMode: + description: 'defaultMode is optional: mode bits used + to set permissions on created files by default. Must + be an octal value between 0000 and 0777 or a decimal + value between 0 and 511. YAML accepts both octal and + decimal values, JSON requires decimal values for mode + bits. Defaults to 0644. Directories within the path + are not affected by this setting. This might be in + conflict with other options that affect the file mode, + like fsGroup, and the result can be other mode bits + set.' + format: int32 + type: integer + items: + description: items if unspecified, each key-value pair + in the Data field of the referenced ConfigMap will + be projected into the volume as a file whose name + is the key and content is the value. If specified, + the listed keys will be projected into the specified + paths, and unlisted keys will not be present. If a + key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. + Paths must be relative and may not contain the '..' + path or start with '..'. + items: + description: Maps a string key to a path within a + volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits used + to set permissions on this file. Must be an + octal value between 0000 and 0777 or a decimal + value between 0 and 511. YAML accepts both octal + and decimal values, JSON requires decimal values + for mode bits. If not specified, the volume + defaultMode will be used. This might be in conflict + with other options that affect the file mode, + like fsGroup, and the result can be other mode + bits set.' + format: int32 + type: integer + path: + description: path is the relative path of the + file to map the key to. May not be an absolute + path. May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: optional specify whether the ConfigMap + or its keys must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + description: csi (Container Storage Interface) represents + ephemeral storage that is handled by certain external + CSI drivers (Beta feature). + properties: + driver: + description: driver is the name of the CSI driver that + handles this volume. Consult with your admin for the + correct name as registered in the cluster. + type: string + fsType: + description: fsType to mount. Ex. "ext4", "xfs", "ntfs". + If not provided, the empty value is passed to the + associated CSI driver which will determine the default + filesystem to apply. + type: string + nodePublishSecretRef: + description: nodePublishSecretRef is a reference to + the secret object containing sensitive information + to pass to the CSI driver to complete the CSI NodePublishVolume + and NodeUnpublishVolume calls. This field is optional, + and may be empty if no secret is required. If the + secret object contains more than one secret, all secret + references are passed. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + description: readOnly specifies a read-only configuration + for the volume. Defaults to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: + type: string + description: volumeAttributes stores driver-specific + properties that are passed to the CSI driver. Consult + your driver's documentation for supported values. + type: object + required: + - driver + type: object + downwardAPI: + description: downwardAPI represents downward API about the + pod that should populate this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created + files by default. Must be a Optional: mode bits used + to set permissions on created files by default. Must + be an octal value between 0000 and 0777 or a decimal + value between 0 and 511. YAML accepts both octal and + decimal values, JSON requires decimal values for mode + bits. Defaults to 0644. Directories within the path + are not affected by this setting. This might be in + conflict with other options that affect the file mode, + like fsGroup, and the result can be other mode bits + set.' + format: int32 + type: integer + items: + description: Items is a list of downward API volume + file + items: + description: DownwardAPIVolumeFile represents information + to create the file containing the pod field + properties: + fieldRef: + description: 'Required: Selects a field of the + pod: only annotations, labels, name and namespace + are supported.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in + the specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + description: 'Optional: mode bits used to set + permissions on this file, must be an octal value + between 0000 and 0777 or a decimal value between + 0 and 511. YAML accepts both octal and decimal + values, JSON requires decimal values for mode + bits. If not specified, the volume defaultMode + will be used. This might be in conflict with + other options that affect the file mode, like + fsGroup, and the result can be other mode bits + set.' + format: int32 + type: integer + path: + description: 'Required: Path is the relative + path name of the file to be created. Must not + be absolute or contain the ''..'' path. Must + be utf-8 encoded. The first item of the relative + path must not start with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the container: + only resources limits and requests (limits.cpu, + limits.memory, requests.cpu and requests.memory) + are currently supported.' + properties: + containerName: + description: 'Container name: required for + volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of + the exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + type: object + emptyDir: + description: 'emptyDir represents a temporary directory + that shares a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + properties: + medium: + description: 'medium represents what type of storage + medium should back this directory. The default is + "" which means to use the node''s default medium. + Must be an empty string (default) or Memory. More + info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + description: 'sizeLimit is the total amount of local + storage required for this EmptyDir volume. The size + limit is also applicable for memory medium. The maximum + usage on memory medium EmptyDir would be the minimum + value between the SizeLimit specified here and the + sum of memory limits of all containers in a pod. The + default is nil which means that the limit is undefined. + More info: http://kubernetes.io/docs/user-guide/volumes#emptydir' + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + description: "ephemeral represents a volume that is handled + by a cluster storage driver. The volume's lifecycle is + tied to the pod that defines it - it will be created before + the pod starts, and deleted when the pod is removed. \n + Use this if: a) the volume is only needed while the pod + runs, b) features of normal volumes like restoring from + snapshot or capacity tracking are needed, c) the storage + driver is specified through a storage class, and d) the + storage driver supports dynamic volume provisioning through + a PersistentVolumeClaim (see EphemeralVolumeSource for + more information on the connection between this volume + type and PersistentVolumeClaim). \n Use PersistentVolumeClaim + or one of the vendor-specific APIs for volumes that persist + for longer than the lifecycle of an individual pod. \n + Use CSI for light-weight local ephemeral volumes if the + CSI driver is meant to be used that way - see the documentation + of the driver for more information. \n A pod can use both + types of ephemeral volumes and persistent volumes at the + same time." + properties: + volumeClaimTemplate: + description: "Will be used to create a stand-alone PVC + to provision the volume. The pod in which this EphemeralVolumeSource + is embedded will be the owner of the PVC, i.e. the + PVC will be deleted together with the pod. The name + of the PVC will be `-` where + `` is the name from the `PodSpec.Volumes` + array entry. Pod validation will reject the pod if + the concatenated name is not valid for a PVC (for + example, too long). \n An existing PVC with that name + that is not owned by the pod will *not* be used for + the pod to avoid using an unrelated volume by mistake. + Starting the pod is then blocked until the unrelated + PVC is removed. If such a pre-created PVC is meant + to be used by the pod, the PVC has to updated with + an owner reference to the pod once the pod exists. + Normally this should not be necessary, but it may + be useful when manually reconstructing a broken cluster. + \n This field is read-only and no changes will be + made by Kubernetes to the PVC after it has been created. + \n Required, must not be nil." + properties: + metadata: + description: May contain labels and annotations + that will be copied into the PVC when creating + it. No other fields are allowed and will be rejected + during validation. + type: object + spec: + description: The specification for the PersistentVolumeClaim. + The entire content is copied unchanged into the + PVC that gets created from this template. The + same fields as in a PersistentVolumeClaim are + also valid here. + properties: + accessModes: + description: 'accessModes contains the desired + access modes the volume should have. More + info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' + items: + type: string + type: array + dataSource: + description: 'dataSource field can be used to + specify either: * An existing VolumeSnapshot + object (snapshot.storage.k8s.io/VolumeSnapshot) + * An existing PVC (PersistentVolumeClaim) + If the provisioner or an external controller + can support the specified data source, it + will create a new volume based on the contents + of the specified data source. If the AnyVolumeDataSource + feature gate is enabled, this field will always + have the same contents as the DataSourceRef + field.' + properties: + apiGroup: + description: APIGroup is the group for the + resource being referenced. If APIGroup + is not specified, the specified Kind must + be in the core API group. For any other + third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource + being referenced + type: string + name: + description: Name is the name of resource + being referenced + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + description: 'dataSourceRef specifies the object + from which to populate the volume with data, + if a non-empty volume is desired. This may + be any local object from a non-empty API group + (non core object) or a PersistentVolumeClaim + object. When this field is specified, volume + binding will only succeed if the type of the + specified object matches some installed volume + populator or dynamic provisioner. This field + will replace the functionality of the DataSource + field and as such if both fields are non-empty, + they must have the same value. For backwards + compatibility, both fields (DataSource and + DataSourceRef) will be set to the same value + automatically if one of them is empty and + the other is non-empty. There are two important + differences between DataSource and DataSourceRef: + * While DataSource only allows two specific + types of objects, DataSourceRef allows any + non-core object, as well as PersistentVolumeClaim + objects. * While DataSource ignores disallowed + values (dropping them), DataSourceRef preserves + all values, and generates an error if a disallowed + value is specified. (Beta) Using this field + requires the AnyVolumeDataSource feature gate + to be enabled.' + properties: + apiGroup: + description: APIGroup is the group for the + resource being referenced. If APIGroup + is not specified, the specified Kind must + be in the core API group. For any other + third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource + being referenced + type: string + name: + description: Name is the name of resource + being referenced + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + resources: + description: 'resources represents the minimum + resources the volume should have. If RecoverVolumeExpansionFailure + feature is enabled users are allowed to specify + resource requirements that are lower than + previous value but must still be higher than + capacity recorded in the status field of the + claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum + amount of compute resources allowed. More + info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum + amount of compute resources required. + If Requests is omitted for a container, + it defaults to Limits if that is explicitly + specified, otherwise to an implementation-defined + value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + selector: + description: selector is a label query over + volumes to consider for binding. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + description: 'storageClassName is the name of + the StorageClass required by the claim. More + info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' + type: string + volumeMode: + description: volumeMode defines what type of + volume is required by the claim. Value of + Filesystem is implied when not included in + claim spec. + type: string + volumeName: + description: volumeName is the binding reference + to the PersistentVolume backing this claim. + type: string + type: object + required: + - spec + type: object + type: object + fc: + description: fc represents a Fibre Channel resource that + is attached to a kubelet's host machine and then exposed + to the pod. + properties: + fsType: + description: 'fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. TODO: how do we prevent + errors in the filesystem from compromising the machine' + type: string + lun: + description: 'lun is Optional: FC target lun number' + format: int32 + type: integer + readOnly: + description: 'readOnly is Optional: Defaults to false + (read/write). ReadOnly here will force the ReadOnly + setting in VolumeMounts.' + type: boolean + targetWWNs: + description: 'targetWWNs is Optional: FC target worldwide + names (WWNs)' + items: + type: string + type: array + wwids: + description: 'wwids Optional: FC volume world wide identifiers + (wwids) Either wwids or combination of targetWWNs + and lun must be set, but not both simultaneously.' + items: + type: string + type: array + type: object + flexVolume: + description: flexVolume represents a generic volume resource + that is provisioned/attached using an exec based plugin. + properties: + driver: + description: driver is the name of the driver to use + for this volume. + type: string + fsType: + description: fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". The default filesystem + depends on FlexVolume script. + type: string + options: + additionalProperties: + type: string + description: 'options is Optional: this field holds + extra command options if any.' + type: object + readOnly: + description: 'readOnly is Optional: defaults to false + (read/write). ReadOnly here will force the ReadOnly + setting in VolumeMounts.' + type: boolean + secretRef: + description: 'secretRef is Optional: secretRef is reference + to the secret object containing sensitive information + to pass to the plugin scripts. This may be empty if + no secret object is specified. If the secret object + contains more than one secret, all secrets are passed + to the plugin scripts.' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + description: flocker represents a Flocker volume attached + to a kubelet's host machine. This depends on the Flocker + control service being running + properties: + datasetName: + description: datasetName is Name of the dataset stored + as metadata -> name on the dataset for Flocker should + be considered as deprecated + type: string + datasetUUID: + description: datasetUUID is the UUID of the dataset. + This is unique identifier of a Flocker dataset + type: string + type: object + gcePersistentDisk: + description: 'gcePersistentDisk represents a GCE Disk resource + that is attached to a kubelet''s host machine and then + exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + properties: + fsType: + description: 'fsType is filesystem type of the volume + that you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + partition: + description: 'partition is the partition in the volume + that you want to mount. If omitted, the default is + to mount by volume name. Examples: For volume /dev/sda1, + you specify the partition as "1". Similarly, the volume + partition for /dev/sda is "0" (or you can leave the + property empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + format: int32 + type: integer + pdName: + description: 'pdName is unique name of the PD resource + in GCE. Used to identify the disk in GCE. More info: + https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: string + readOnly: + description: 'readOnly here will force the ReadOnly + setting in VolumeMounts. Defaults to false. More info: + https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: boolean + required: + - pdName + type: object + gitRepo: + description: 'gitRepo represents a git repository at a particular + revision. DEPRECATED: GitRepo is deprecated. To provision + a container with a git repo, mount an EmptyDir into an + InitContainer that clones the repo using git, then mount + the EmptyDir into the Pod''s container.' + properties: + directory: + description: directory is the target directory name. + Must not contain or start with '..'. If '.' is supplied, + the volume directory will be the git repository. Otherwise, + if specified, the volume will contain the git repository + in the subdirectory with the given name. + type: string + repository: + description: repository is the URL + type: string + revision: + description: revision is the commit hash for the specified + revision. + type: string + required: + - repository + type: object + glusterfs: + description: 'glusterfs represents a Glusterfs mount on + the host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md' + properties: + endpoints: + description: 'endpoints is the endpoint name that details + Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + path: + description: 'path is the Glusterfs volume path. More + info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + readOnly: + description: 'readOnly here will force the Glusterfs + volume to be mounted with read-only permissions. Defaults + to false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: boolean + required: + - endpoints + - path + type: object + hostPath: + description: 'hostPath represents a pre-existing file or + directory on the host machine that is directly exposed + to the container. This is generally used for system agents + or other privileged things that are allowed to see the + host machine. Most containers will NOT need this. More + info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + --- TODO(jonesdl) We need to restrict who can use host + directory mounts and who can/can not mount host directories + as read/write.' + properties: + path: + description: 'path of the directory on the host. If + the path is a symlink, it will follow the link to + the real path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + type: + description: 'type for HostPath Volume Defaults to "" + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + required: + - path + type: object + iscsi: + description: 'iscsi represents an ISCSI Disk resource that + is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + properties: + chapAuthDiscovery: + description: chapAuthDiscovery defines whether support + iSCSI Discovery CHAP authentication + type: boolean + chapAuthSession: + description: chapAuthSession defines whether support + iSCSI Session CHAP authentication + type: boolean + fsType: + description: 'fsType is the filesystem type of the volume + that you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + initiatorName: + description: initiatorName is the custom iSCSI Initiator + Name. If initiatorName is specified with iscsiInterface + simultaneously, new iSCSI interface : will be created for the connection. + type: string + iqn: + description: iqn is the target iSCSI Qualified Name. + type: string + iscsiInterface: + description: iscsiInterface is the interface Name that + uses an iSCSI transport. Defaults to 'default' (tcp). + type: string + lun: + description: lun represents iSCSI Target Lun number. + format: int32 + type: integer + portals: + description: portals is the iSCSI Target Portal List. + The portal is either an IP or ip_addr:port if the + port is other than default (typically TCP ports 860 + and 3260). + items: + type: string + type: array + readOnly: + description: readOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. + type: boolean + secretRef: + description: secretRef is the CHAP Secret for iSCSI + target and initiator authentication + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + description: targetPortal is iSCSI Target Portal. The + Portal is either an IP or ip_addr:port if the port + is other than default (typically TCP ports 860 and + 3260). + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + description: 'name of the volume. Must be a DNS_LABEL and + unique within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + nfs: + description: 'nfs represents an NFS mount on the host that + shares a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + properties: + path: + description: 'path that is exported by the NFS server. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + readOnly: + description: 'readOnly here will force the NFS export + to be mounted with read-only permissions. Defaults + to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: boolean + server: + description: 'server is the hostname or IP address of + the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + description: 'persistentVolumeClaimVolumeSource represents + a reference to a PersistentVolumeClaim in the same namespace. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + properties: + claimName: + description: 'claimName is the name of a PersistentVolumeClaim + in the same namespace as the pod using this volume. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: string + readOnly: + description: readOnly Will force the ReadOnly setting + in VolumeMounts. Default false. + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + description: photonPersistentDisk represents a PhotonController + persistent disk attached and mounted on kubelets host + machine + properties: + fsType: + description: fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. + type: string + pdID: + description: pdID is the ID that identifies Photon Controller + persistent disk + type: string + required: + - pdID + type: object + portworxVolume: + description: portworxVolume represents a portworx volume + attached and mounted on kubelets host machine + properties: + fsType: + description: fSType represents the filesystem type to + mount Must be a filesystem type supported by the host + operating system. Ex. "ext4", "xfs". Implicitly inferred + to be "ext4" if unspecified. + type: string + readOnly: + description: readOnly defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts. + type: boolean + volumeID: + description: volumeID uniquely identifies a Portworx + volume + type: string + required: + - volumeID + type: object + projected: + description: projected items for all in one resources secrets, + configmaps, and downward API + properties: + defaultMode: + description: defaultMode are the mode bits used to set + permissions on created files by default. Must be an + octal value between 0000 and 0777 or a decimal value + between 0 and 511. YAML accepts both octal and decimal + values, JSON requires decimal values for mode bits. + Directories within the path are not affected by this + setting. This might be in conflict with other options + that affect the file mode, like fsGroup, and the result + can be other mode bits set. + format: int32 + type: integer + sources: + description: sources is the list of volume projections + items: + description: Projection that may be projected along + with other supported volume types + properties: + configMap: + description: configMap information about the configMap + data to project + properties: + items: + description: items if unspecified, each key-value + pair in the Data field of the referenced + ConfigMap will be projected into the volume + as a file whose name is the key and content + is the value. If specified, the listed keys + will be projected into the specified paths, + and unlisted keys will not be present. If + a key is specified which is not present + in the ConfigMap, the volume setup will + error unless it is marked optional. Paths + must be relative and may not contain the + '..' path or start with '..'. + items: + description: Maps a string key to a path + within a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode + bits used to set permissions on this + file. Must be an octal value between + 0000 and 0777 or a decimal value between + 0 and 511. YAML accepts both octal + and decimal values, JSON requires + decimal values for mode bits. If not + specified, the volume defaultMode + will be used. This might be in conflict + with other options that affect the + file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + path: + description: path is the relative path + of the file to map the key to. May + not be an absolute path. May not contain + the path element '..'. May not start + with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: optional specify whether the + ConfigMap or its keys must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + description: downwardAPI information about the + downwardAPI data to project + properties: + items: + description: Items is a list of DownwardAPIVolume + file + items: + description: DownwardAPIVolumeFile represents + information to create the file containing + the pod field + properties: + fieldRef: + description: 'Required: Selects a field + of the pod: only annotations, labels, + name and namespace are supported.' + properties: + apiVersion: + description: Version of the schema + the FieldPath is written in terms + of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to + select in the specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + description: 'Optional: mode bits used + to set permissions on this file, must + be an octal value between 0000 and + 0777 or a decimal value between 0 + and 511. YAML accepts both octal and + decimal values, JSON requires decimal + values for mode bits. If not specified, + the volume defaultMode will be used. + This might be in conflict with other + options that affect the file mode, + like fsGroup, and the result can be + other mode bits set.' + format: int32 + type: integer + path: + description: 'Required: Path is the + relative path name of the file to + be created. Must not be absolute or + contain the ''..'' path. Must be utf-8 + encoded. The first item of the relative + path must not start with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of + the container: only resources limits + and requests (limits.cpu, limits.memory, + requests.cpu and requests.memory) + are currently supported.' + properties: + containerName: + description: 'Container name: required + for volumes, optional for env + vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output + format of the exposed resources, + defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource + to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + type: object + secret: + description: secret information about the secret + data to project + properties: + items: + description: items if unspecified, each key-value + pair in the Data field of the referenced + Secret will be projected into the volume + as a file whose name is the key and content + is the value. If specified, the listed keys + will be projected into the specified paths, + and unlisted keys will not be present. If + a key is specified which is not present + in the Secret, the volume setup will error + unless it is marked optional. Paths must + be relative and may not contain the '..' + path or start with '..'. + items: + description: Maps a string key to a path + within a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode + bits used to set permissions on this + file. Must be an octal value between + 0000 and 0777 or a decimal value between + 0 and 511. YAML accepts both octal + and decimal values, JSON requires + decimal values for mode bits. If not + specified, the volume defaultMode + will be used. This might be in conflict + with other options that affect the + file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + path: + description: path is the relative path + of the file to map the key to. May + not be an absolute path. May not contain + the path element '..'. May not start + with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: optional field specify whether + the Secret or its key must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + description: serviceAccountToken is information + about the serviceAccountToken data to project + properties: + audience: + description: audience is the intended audience + of the token. A recipient of a token must + identify itself with an identifier specified + in the audience of the token, and otherwise + should reject the token. The audience defaults + to the identifier of the apiserver. + type: string + expirationSeconds: + description: expirationSeconds is the requested + duration of validity of the service account + token. As the token approaches expiration, + the kubelet volume plugin will proactively + rotate the service account token. The kubelet + will start trying to rotate the token if + the token is older than 80 percent of its + time to live or if the token is older than + 24 hours.Defaults to 1 hour and must be + at least 10 minutes. + format: int64 + type: integer + path: + description: path is the path relative to + the mount point of the file to project the + token into. + type: string + required: + - path + type: object + type: object + type: array + type: object + quobyte: + description: quobyte represents a Quobyte mount on the host + that shares a pod's lifetime + properties: + group: + description: group to map volume access to Default is + no group + type: string + readOnly: + description: readOnly here will force the Quobyte volume + to be mounted with read-only permissions. Defaults + to false. + type: boolean + registry: + description: registry represents a single or multiple + Quobyte Registry services specified as a string as + host:port pair (multiple entries are separated with + commas) which acts as the central registry for volumes + type: string + tenant: + description: tenant owning the given Quobyte volume + in the Backend Used with dynamically provisioned Quobyte + volumes, value is set by the plugin + type: string + user: + description: user to map volume access to Defaults to + serivceaccount user + type: string + volume: + description: volume is a string that references an already + created Quobyte volume by name. + type: string + required: + - registry + - volume + type: object + rbd: + description: 'rbd represents a Rados Block Device mount + on the host that shares a pod''s lifetime. More info: + https://examples.k8s.io/volumes/rbd/README.md' + properties: + fsType: + description: 'fsType is the filesystem type of the volume + that you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + image: + description: 'image is the rados image name. More info: + https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + keyring: + description: 'keyring is the path to key ring for RBDUser. + Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + monitors: + description: 'monitors is a collection of Ceph monitors. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + items: + type: string + type: array + pool: + description: 'pool is the rados pool name. Default is + rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + readOnly: + description: 'readOnly here will force the ReadOnly + setting in VolumeMounts. Defaults to false. More info: + https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: boolean + secretRef: + description: 'secretRef is name of the authentication + secret for RBDUser. If provided overrides keyring. + Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + type: object + x-kubernetes-map-type: atomic + user: + description: 'user is the rados user name. Default is + admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + required: + - image + - monitors + type: object + scaleIO: + description: scaleIO represents a ScaleIO persistent volume + attached and mounted on Kubernetes nodes. + properties: + fsType: + description: fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Default is "xfs". + type: string + gateway: + description: gateway is the host address of the ScaleIO + API Gateway. + type: string + protectionDomain: + description: protectionDomain is the name of the ScaleIO + Protection Domain for the configured storage. + type: string + readOnly: + description: readOnly Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: secretRef references to the secret for + ScaleIO user and other sensitive information. If this + is not provided, Login operation will fail. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + description: sslEnabled Flag enable/disable SSL communication + with Gateway, default false + type: boolean + storageMode: + description: storageMode indicates whether the storage + for a volume should be ThickProvisioned or ThinProvisioned. + Default is ThinProvisioned. + type: string + storagePool: + description: storagePool is the ScaleIO Storage Pool + associated with the protection domain. + type: string + system: + description: system is the name of the storage system + as configured in ScaleIO. + type: string + volumeName: + description: volumeName is the name of a volume already + created in the ScaleIO system that is associated with + this volume source. + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + description: 'secret represents a secret that should populate + this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + properties: + defaultMode: + description: 'defaultMode is Optional: mode bits used + to set permissions on created files by default. Must + be an octal value between 0000 and 0777 or a decimal + value between 0 and 511. YAML accepts both octal and + decimal values, JSON requires decimal values for mode + bits. Defaults to 0644. Directories within the path + are not affected by this setting. This might be in + conflict with other options that affect the file mode, + like fsGroup, and the result can be other mode bits + set.' + format: int32 + type: integer + items: + description: items If unspecified, each key-value pair + in the Data field of the referenced Secret will be + projected into the volume as a file whose name is + the key and content is the value. If specified, the + listed keys will be projected into the specified paths, + and unlisted keys will not be present. If a key is + specified which is not present in the Secret, the + volume setup will error unless it is marked optional. + Paths must be relative and may not contain the '..' + path or start with '..'. + items: + description: Maps a string key to a path within a + volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits used + to set permissions on this file. Must be an + octal value between 0000 and 0777 or a decimal + value between 0 and 511. YAML accepts both octal + and decimal values, JSON requires decimal values + for mode bits. If not specified, the volume + defaultMode will be used. This might be in conflict + with other options that affect the file mode, + like fsGroup, and the result can be other mode + bits set.' + format: int32 + type: integer + path: + description: path is the relative path of the + file to map the key to. May not be an absolute + path. May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + optional: + description: optional field specify whether the Secret + or its keys must be defined + type: boolean + secretName: + description: 'secretName is the name of the secret in + the pod''s namespace to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + type: object + storageos: + description: storageOS represents a StorageOS volume attached + and mounted on Kubernetes nodes. + properties: + fsType: + description: fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. + type: string + readOnly: + description: readOnly defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: secretRef specifies the secret to use for + obtaining the StorageOS API credentials. If not specified, + default values will be attempted. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + description: volumeName is the human-readable name of + the StorageOS volume. Volume names are only unique + within a namespace. + type: string + volumeNamespace: + description: volumeNamespace specifies the scope of + the volume within StorageOS. If no namespace is specified + then the Pod's namespace will be used. This allows + the Kubernetes name scoping to be mirrored within + StorageOS for tighter integration. Set VolumeName + to any name to override the default behaviour. Set + to "default" if you are not using namespaces within + StorageOS. Namespaces that do not pre-exist within + StorageOS will be created. + type: string + type: object + vsphereVolume: + description: vsphereVolume represents a vSphere volume attached + and mounted on kubelets host machine + properties: + fsType: + description: fsType is filesystem type to mount. Must + be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. + type: string + storagePolicyID: + description: storagePolicyID is the storage Policy Based + Management (SPBM) profile ID associated with the StoragePolicyName. + type: string + storagePolicyName: + description: storagePolicyName is the storage Policy + Based Management (SPBM) profile name. + type: string + volumePath: + description: volumePath is the path that identifies + vSphere volume vmdk + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array type: object optimizeKubeSourceConfig: description: Enable kubernetes source config optimization diff --git a/controllers/factory/config/config.go b/controllers/factory/config/config.go index a0b9709b..87d19bc3 100644 --- a/controllers/factory/config/config.go +++ b/controllers/factory/config/config.go @@ -41,7 +41,7 @@ func New(vector *vectorv1alpha1.Vector) *VectorConfig { } return &VectorConfig{ - DataDir: vector.Spec.Agent.DataDir, + DataDir: "/vector-data-dir", Api: api, Sources: sources, Sinks: sinks, diff --git a/controllers/factory/vector/vectoragent/vectoragent_daemonset.go b/controllers/factory/vector/vectoragent/vectoragent_daemonset.go index 5f49b3f5..c2eb9714 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_daemonset.go +++ b/controllers/factory/vector/vectoragent/vectoragent_daemonset.go @@ -58,7 +58,7 @@ func (ctrl *Controller) createVectorAgentDaemonSet() *appsv1.DaemonSet { }, VolumeMounts: ctrl.generateVectorAgentVolumeMounts(), Resources: ctrl.Vector.Spec.Agent.Resources, - SecurityContext: &corev1.SecurityContext{}, + SecurityContext: ctrl.Vector.Spec.Agent.ContainerSecurityContext, ImagePullPolicy: ctrl.Vector.Spec.Agent.ImagePullPolicy, }, }, @@ -71,7 +71,9 @@ func (ctrl *Controller) createVectorAgentDaemonSet() *appsv1.DaemonSet { } func (ctrl *Controller) generateVectorAgentVolume() []corev1.Volume { - volume := []corev1.Volume{ + volume := ctrl.Vector.Spec.Agent.Volumes + + volume = append(volume, []corev1.Volume{ { Name: "config", VolumeSource: corev1.VolumeSource{ @@ -84,31 +86,7 @@ func (ctrl *Controller) generateVectorAgentVolume() []corev1.Volume { Name: "data", VolumeSource: corev1.VolumeSource{ HostPath: &corev1.HostPathVolumeSource{ - Path: "/var/lib/vector", - }, - }, - }, - { - Name: "var-log", - VolumeSource: corev1.VolumeSource{ - HostPath: &corev1.HostPathVolumeSource{ - Path: "/var/log/", - }, - }, - }, - { - Name: "journal", - VolumeSource: corev1.VolumeSource{ - HostPath: &corev1.HostPathVolumeSource{ - Path: "/var/log/journal", - }, - }, - }, - { - Name: "var-lib", - VolumeSource: corev1.VolumeSource{ - HostPath: &corev1.HostPathVolumeSource{ - Path: "/var/lib/", + Path: ctrl.Vector.Spec.Agent.DataDir, }, }, }, @@ -128,13 +106,15 @@ func (ctrl *Controller) generateVectorAgentVolume() []corev1.Volume { }, }, }, - } + }...) return volume } func (ctrl *Controller) generateVectorAgentVolumeMounts() []corev1.VolumeMount { - volumeMount := []corev1.VolumeMount{ + volumeMount := ctrl.Vector.Spec.Agent.VolumeMounts + + volumeMount = append(volumeMount, []corev1.VolumeMount{ { Name: "config", MountPath: "/etc/vector/", @@ -143,18 +123,6 @@ func (ctrl *Controller) generateVectorAgentVolumeMounts() []corev1.VolumeMount { Name: "data", MountPath: "/vector-data-dir", }, - { - Name: "var-log", - MountPath: "/var/log/", - }, - { - Name: "journal", - MountPath: "/run/log/journal", - }, - { - Name: "var-lib", - MountPath: "/var/lib/", - }, { Name: "procfs", MountPath: "/host/proc", @@ -163,7 +131,7 @@ func (ctrl *Controller) generateVectorAgentVolumeMounts() []corev1.VolumeMount { Name: "sysfs", MountPath: "/host/sys", }, - } + }...) return volumeMount } diff --git a/controllers/factory/vector/vectoragent/vectoragent_default.go b/controllers/factory/vector/vectoragent/vectoragent_default.go index 87596763..e5e86110 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_default.go +++ b/controllers/factory/vector/vectoragent/vectoragent_default.go @@ -44,7 +44,52 @@ func (ctrl *Controller) SetDefault() { } if ctrl.Vector.Spec.Agent.DataDir == "" { - ctrl.Vector.Spec.Agent.DataDir = "/vector-data-dir" + ctrl.Vector.Spec.Agent.DataDir = "/var/lib/vector" } + if ctrl.Vector.Spec.Agent.Volumes == nil { + ctrl.Vector.Spec.Agent.Volumes = []corev1.Volume{ + { + Name: "var-log", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/var/log/", + }, + }, + }, + { + Name: "journal", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/var/log/journal", + }, + }, + }, + { + Name: "var-lib", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/var/lib/", + }, + }, + }, + } + } + + if ctrl.Vector.Spec.Agent.VolumeMounts == nil { + ctrl.Vector.Spec.Agent.VolumeMounts = []corev1.VolumeMount{ + { + Name: "var-log", + MountPath: "/var/log/", + }, + { + Name: "journal", + MountPath: "/run/log/journal", + }, + { + Name: "var-lib", + MountPath: "/var/lib/", + }, + } + } } diff --git a/docs/specification.md b/docs/specification.md index 0556d071..826e65d3 100644 --- a/docs/specification.md +++ b/docs/specification.md @@ -1,8 +1,9 @@ # Specification -- [Vector](#vector-spec) -- [VectorPipeline](#vectorpipelinespec-clustervectorpipelinespec) -- [ClusterVectorPipeline](#vectorpipelinespec-clustervectorpipelinespec) +- [Specification](#specification) +- [Vector Spec](#vector-spec) + - [Api Spec](#api-spec) +- [VectorPipelineSpec (ClusterVectorPipelineSpec)](#vectorpipelinespec-clustervectorpipelinespec) @@ -45,6 +46,10 @@

>;Db^k;iS%-5$>KWEb|(e|IZ$ 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 literal 0 HcmV?d00001 From 63564e24c04fad95999eadfb5fa8bd44129167a1 Mon Sep 17 00:00:00 2001 From: Vladimir <31961982+zvlb@users.noreply.github.com> Date: Wed, 18 Jan 2023 11:31:54 +0200 Subject: [PATCH 174/316] Update docs about collect journald logs --- docs/journald-logs.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/journald-logs.md b/docs/journald-logs.md index 9ee49ab5..b921a2c3 100644 --- a/docs/journald-logs.md +++ b/docs/journald-logs.md @@ -4,6 +4,8 @@ If you want collect service journald logs from node you can use example. > Type `journald` in source block work only in ClusterVectorPipeline. In VectorPipeline can use only `kubernetes_logs` type +> If you want collect journald logs, needs to use vector-agent container with journalctl. `timberio/vector:0.26.0-debian` - for example + ```yaml apiVersion: observability.kaasops.io/v1alpha1 From bd55466b9554d2e949b06f83a65b90db14fcd946 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Tue, 28 Mar 2023 12:01:14 +0300 Subject: [PATCH 175/316] enable metrics exporter and fix svc creation --- api/v1alpha1/vector_types.go | 10 +++--- .../observability.kaasops.io_vectors.yaml | 7 ++-- .../observability_v1alpha1_vector.yaml | 7 ++-- controllers/factory/config/config.go | 17 +++++++++- controllers/factory/config/config_build.go | 28 +++++++++++++--- controllers/factory/config/types.go | 14 +++----- .../vectoragent/vectoragent_controller.go | 2 +- .../vector/vectoragent/vectoragent_default.go | 6 +--- .../vector/vectoragent/vectoragent_service.go | 33 ++++++++++++++----- 9 files changed, 85 insertions(+), 39 deletions(-) diff --git a/api/v1alpha1/vector_types.go b/api/v1alpha1/vector_types.go index 1b0edcf7..94af4924 100644 --- a/api/v1alpha1/vector_types.go +++ b/api/v1alpha1/vector_types.go @@ -95,16 +95,18 @@ type VectorAgent struct { DataDir string `json:"dataDir,omitempty"` Api ApiSpec `json:"api,omitempty"` - Service bool `json:"service,omitempty"` + + // Enable internal metrics exporter + // +optional + InternalMetrics bool `json:"internalMetrics,omitempty"` ConfigCheck ConfigCheck `json:"configCheck,omitempty"` } // ApiSpec is the Schema for the Vector Agent GraphQL API - https://vector.dev/docs/reference/api/ type ApiSpec struct { - Address string `json:"address,omitempty"` - Enabled bool `json:"enabled,omitempty"` - Playground bool `json:"playground,omitempty"` + Enabled bool `json:"enabled,omitempty"` + Playground bool `json:"playground,omitempty"` } // ConfigCheck is the Schema for control params for ConfigCheck pods diff --git a/config/crd/bases/observability.kaasops.io_vectors.yaml b/config/crd/bases/observability.kaasops.io_vectors.yaml index f59668bb..cd000e2c 100644 --- a/config/crd/bases/observability.kaasops.io_vectors.yaml +++ b/config/crd/bases/observability.kaasops.io_vectors.yaml @@ -918,8 +918,6 @@ spec: description: ApiSpec is the Schema for the Vector Agent GraphQL API - https://vector.dev/docs/reference/api/ properties: - address: - type: string enabled: type: boolean playground: @@ -2063,6 +2061,9 @@ spec: type: object x-kubernetes-map-type: atomic type: array + internalMetrics: + description: Enable internal metrics exporter + type: boolean podSecurityPolicyName: description: PodSecurityPolicyName - defines name for podSecurityPolicy in case of empty value, prefixedName will be used. @@ -2277,8 +2278,6 @@ spec: type: string type: object type: object - service: - type: boolean tolerations: description: Tolerations If specified, the pod's tolerations. items: diff --git a/config/samples/observability_v1alpha1_vector.yaml b/config/samples/observability_v1alpha1_vector.yaml index ae06f0b3..54435af0 100644 --- a/config/samples/observability_v1alpha1_vector.yaml +++ b/config/samples/observability_v1alpha1_vector.yaml @@ -5,6 +5,7 @@ metadata: namespace: vector spec: agent: - service: true - image: "timberio/vector:0.26.0-distroless-libc" - + image: "timberio/vector:0.28.1-distroless-libc" + internalMetrics: true + api: + enabled: true diff --git a/controllers/factory/config/config.go b/controllers/factory/config/config.go index fe12b1a4..a0b9709b 100644 --- a/controllers/factory/config/config.go +++ b/controllers/factory/config/config.go @@ -17,17 +17,32 @@ limitations under the License. package config import ( + "strconv" + vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" + "github.com/kaasops/vector-operator/controllers/factory/vector/vectoragent" "github.com/mitchellh/mapstructure" ) +type ApiSpec struct { + Address string `json:"address,omitempty"` + Enabled bool `json:"enabled,omitempty"` + Playground bool `json:"playground,omitempty"` +} + func New(vector *vectorv1alpha1.Vector) *VectorConfig { sources := []*Source{} sinks := []*Sink{} + api := &ApiSpec{ + Address: "0.0.0.0:" + strconv.Itoa(vectoragent.ApiPort), + Enabled: vector.Spec.Agent.Api.Enabled, + Playground: vector.Spec.Agent.Api.Playground, + } + return &VectorConfig{ DataDir: vector.Spec.Agent.DataDir, - Api: &vector.Spec.Agent.Api, + Api: api, Sources: sources, Sinks: sinks, } diff --git a/controllers/factory/config/config_build.go b/controllers/factory/config/config_build.go index b1ae6274..84eab1de 100644 --- a/controllers/factory/config/config_build.go +++ b/controllers/factory/config/config_build.go @@ -27,21 +27,36 @@ import ( "github.com/mitchellh/mapstructure" ) +const ( + KubernetesSourceType = "kubernetes_logs" + BlackholeSinkType = "blackhole" + InternalMetricsSourceType = "internal_metrics" + InternalMetricsSinkType = "prometheus_exporter" +) + var ( - KubernetesSourceType = "kubernetes_logs" - sourceDefault = &Source{ + sourceDefault = &Source{ Name: "defaultSource", Type: KubernetesSourceType, } + internalMetricSource = &Source{ + Name: "internalMetricsSource", + Type: InternalMetricsSourceType, + } sinkDefault = &Sink{ Name: "defaultSink", - Type: "blackhole", - Inputs: []string{"defaultSource"}, + Type: BlackholeSinkType, + Inputs: []string{sourceDefault.Name}, Options: map[string]interface{}{ "rate": 100, "print_interval_secs": 60, }, } + internalMetricsExporter = &Sink{ + Name: "internalMetricsSink", + Type: InternalMetricsSinkType, + Inputs: []string{internalMetricSource.Name}, + } ) var ( @@ -119,6 +134,11 @@ func (b *Builder) generateVectorConfig() (*VectorConfig, error) { sinks = []*Sink{sinkDefault} } + if b.vaCtrl.Vector.Spec.Agent.InternalMetrics { + sources = append(sources, internalMetricSource) + sinks = append(sinks, internalMetricsExporter) + } + vectorConfig.Sinks = sinks vectorConfig.Sources = sources vectorConfig.Transforms = transforms diff --git a/controllers/factory/config/types.go b/controllers/factory/config/types.go index 834e4ede..2d4cfb62 100644 --- a/controllers/factory/config/types.go +++ b/controllers/factory/config/types.go @@ -16,16 +16,12 @@ limitations under the License. package config -import ( - vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" -) - type VectorConfig struct { - DataDir string `mapstructure:"data_dir"` - Api *vectorv1alpha1.ApiSpec `mapstructure:"api"` - Sources []*Source `mapstructure:"sources"` - Transforms []*Transform `mapstructure:"transforms"` - Sinks []*Sink `mapstructure:"sinks"` + DataDir string `mapstructure:"data_dir"` + Api *ApiSpec `mapstructure:"api"` + Sources []*Source `mapstructure:"sources"` + Transforms []*Transform `mapstructure:"transforms"` + Sinks []*Sink `mapstructure:"sinks"` } type Source struct { diff --git a/controllers/factory/vector/vectoragent/vectoragent_controller.go b/controllers/factory/vector/vectoragent/vectoragent_controller.go index 2790a64c..fc846162 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_controller.go +++ b/controllers/factory/vector/vectoragent/vectoragent_controller.go @@ -37,7 +37,7 @@ func (ctrl *Controller) EnsureVectorAgent(ctx context.Context, configOnly bool) return err } - if ctrl.Vector.Spec.Agent.Service { + if ctrl.Vector.Spec.Agent.InternalMetrics || ctrl.Vector.Spec.Agent.Api.Enabled { if err := ctrl.ensureVectorAgentService(ctx); err != nil { return err } diff --git a/controllers/factory/vector/vectoragent/vectoragent_default.go b/controllers/factory/vector/vectoragent/vectoragent_default.go index 56609408..87596763 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_default.go +++ b/controllers/factory/vector/vectoragent/vectoragent_default.go @@ -27,7 +27,7 @@ func (ctrl *Controller) SetDefault() { ctrl.Vector.Spec.Agent = new(v1alpha1.VectorAgent) } if ctrl.Vector.Spec.Agent.Image == "" { - ctrl.Vector.Spec.Agent.Image = "timberio/vector:0.26.0-distroless-libc" + ctrl.Vector.Spec.Agent.Image = "timberio/vector:0.28.1-distroless-libc" } if ctrl.Vector.Spec.Agent.Resources.Requests == nil { @@ -43,10 +43,6 @@ func (ctrl *Controller) SetDefault() { } } - if ctrl.Vector.Spec.Agent.Api.Address == "" { - ctrl.Vector.Spec.Agent.Api.Address = "0.0.0.0:8686" - } - if ctrl.Vector.Spec.Agent.DataDir == "" { ctrl.Vector.Spec.Agent.DataDir = "/vector-data-dir" } diff --git a/controllers/factory/vector/vectoragent/vectoragent_service.go b/controllers/factory/vector/vectoragent/vectoragent_service.go index 24f33ed7..d359ce9d 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_service.go +++ b/controllers/factory/vector/vectoragent/vectoragent_service.go @@ -22,22 +22,39 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" ) +const ( + ApiPort = 8686 + MetricsExporterPort = 9598 +) + func (ctrl *Controller) createVectorAgentService() *corev1.Service { labels := ctrl.labelsForVectorAgent() + ports := []corev1.ServicePort{ + { + Name: "prom-exporter", + Protocol: corev1.Protocol("TCP"), + Port: MetricsExporterPort, + TargetPort: intstr.FromInt(MetricsExporterPort), + }, + } + + if ctrl.Vector.Spec.Agent.Api.Enabled { + ports = append(ports, corev1.ServicePort{ + Name: "api", + Protocol: corev1.Protocol("TCP"), + Port: ApiPort, + TargetPort: intstr.FromInt(ApiPort), + }) + } + service := &corev1.Service{ ObjectMeta: ctrl.objectMetaVectorAgent(labels), Spec: corev1.ServiceSpec{ - Ports: []corev1.ServicePort{ - { - Name: "prom-exporter", - Protocol: corev1.Protocol("TCP"), - Port: 9090, - TargetPort: intstr.FromInt(9090), - }, - }, + Ports: ports, Selector: labels, }, } + return service } From 5b83e2323ab9ffe6ca465156bb27167ff44e6c03 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Tue, 28 Mar 2023 16:42:41 +0300 Subject: [PATCH 176/316] add serivcemonitor creation --- controllers/factory/utils/k8s/k8s.go | 43 ++ .../vectoragent/vectoragent_controller.go | 16 + .../vector/vectoragent/vectoragent_service.go | 10 +- .../vectoragent/vectoragent_servicemonitor.go | 28 ++ controllers/vector_controller.go | 2 + go.mod | 53 +-- go.sum | 381 +++++------------- main.go | 2 + 8 files changed, 235 insertions(+), 300 deletions(-) create mode 100644 controllers/factory/vector/vectoragent/vectoragent_servicemonitor.go diff --git a/controllers/factory/utils/k8s/k8s.go b/controllers/factory/utils/k8s/k8s.go index 95cda200..186399b9 100644 --- a/controllers/factory/utils/k8s/k8s.go +++ b/controllers/factory/utils/k8s/k8s.go @@ -23,6 +23,7 @@ import ( "fmt" "io" + monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" @@ -60,6 +61,8 @@ func CreateOrUpdateResource(ctx context.Context, obj client.Object, c client.Cli return createOrUpdateClusterRole(ctx, obj, c) case *rbacv1.ClusterRoleBinding: return createOrUpdateClusterRoleBinding(ctx, obj, c) + case *monitorv1.ServiceMonitor: + return createOrUpdateServiceMonitor(ctx, obj, c) default: return NewNotSupportedError(obj) } @@ -369,6 +372,46 @@ func createOrUpdateClusterRoleBinding(ctx context.Context, obj runtime.Object, c return err } +// + +func createOrUpdateServiceMonitor(ctx context.Context, obj runtime.Object, c client.Client) error { + desired := obj.(*monitorv1.ServiceMonitor) + + // Create ServiceMonitor + err := c.Create(ctx, desired) + if api_errors.IsAlreadyExists(err) { + // If alredy exist - compare with existed + existing := &monitorv1.ServiceMonitor{} + err := c.Get(ctx, client.ObjectKeyFromObject(desired), existing) + if err != nil { + return err + } + + // init Interface for compare + desiredFields := []interface{}{ + desired.GetAnnotations(), + desired.GetLabels(), + desired.Spec, + } + existingFields := []interface{}{ + existing.GetAnnotations(), + existing.GetLabels(), + existing.Spec, + } + + // Compare + if !equality.Semantic.DeepDerivative(desiredFields, existingFields) { + // Update if not equal + existing.Labels = desired.Labels + existing.Annotations = desired.Annotations + existing.Spec = desired.Spec + return c.Update(ctx, existing) + } + return nil + } + return err +} + // Something else: func CreatePod(ctx context.Context, pod *corev1.Pod, c client.Client) error { diff --git a/controllers/factory/vector/vectoragent/vectoragent_controller.go b/controllers/factory/vector/vectoragent/vectoragent_controller.go index fc846162..6625565a 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_controller.go +++ b/controllers/factory/vector/vectoragent/vectoragent_controller.go @@ -43,6 +43,12 @@ func (ctrl *Controller) EnsureVectorAgent(ctx context.Context, configOnly bool) } } + if ctrl.Vector.Spec.Agent.InternalMetrics { + if err := ctrl.ensureVectorAgentServiceMonitor(ctx); err != nil { + return err + } + } + if err := ctrl.ensureVectorAgentDaemonSet(ctx); err != nil { return err } @@ -119,6 +125,16 @@ func (ctrl *Controller) ensureVectorAgentDaemonSet(ctx context.Context) error { return k8s.CreateOrUpdateResource(ctx, vectorAgentDaemonSet, ctrl.Client) } +func (ctrl *Controller) ensureVectorAgentServiceMonitor(ctx context.Context) error { + log := log.FromContext(ctx).WithValues("vector-agent-servicemonitor", ctrl.Vector.Name) + + log.Info("start Reconcile Vector Agent ServiceMonitor") + + vectorAgentServiceMonitor := ctrl.createVectorAgentServiceMonitor() + + return k8s.CreateOrUpdateResource(ctx, vectorAgentServiceMonitor, ctrl.Client) +} + func (ctrl *Controller) labelsForVectorAgent() map[string]string { return map[string]string{ k8s.ManagedByLabelKey: "vector-operator", diff --git a/controllers/factory/vector/vectoragent/vectoragent_service.go b/controllers/factory/vector/vectoragent/vectoragent_service.go index d359ce9d..3b25c3d5 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_service.go +++ b/controllers/factory/vector/vectoragent/vectoragent_service.go @@ -30,13 +30,15 @@ const ( func (ctrl *Controller) createVectorAgentService() *corev1.Service { labels := ctrl.labelsForVectorAgent() - ports := []corev1.ServicePort{ - { - Name: "prom-exporter", + ports := []corev1.ServicePort{} + + if ctrl.Vector.Spec.Agent.InternalMetrics { + ports = append(ports, corev1.ServicePort{ + Name: "vectoragent-metrics", Protocol: corev1.Protocol("TCP"), Port: MetricsExporterPort, TargetPort: intstr.FromInt(MetricsExporterPort), - }, + }) } if ctrl.Vector.Spec.Agent.Api.Enabled { diff --git a/controllers/factory/vector/vectoragent/vectoragent_servicemonitor.go b/controllers/factory/vector/vectoragent/vectoragent_servicemonitor.go new file mode 100644 index 00000000..6e7425fe --- /dev/null +++ b/controllers/factory/vector/vectoragent/vectoragent_servicemonitor.go @@ -0,0 +1,28 @@ +package vectoragent + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" +) + +func (ctrl *Controller) createVectorAgentServiceMonitor() *monitorv1.ServiceMonitor { + labels := ctrl.labelsForVectorAgent() + + servicemonitor := &monitorv1.ServiceMonitor{ + ObjectMeta: ctrl.objectMetaVectorAgent(labels), + Spec: monitorv1.ServiceMonitorSpec{ + Endpoints: []monitorv1.Endpoint{ + { + Path: "/metrics", + Port: "vectoragent-metrics", + }, + }, + Selector: metav1.LabelSelector{ + MatchLabels: labels, + }, + }, + } + + return servicemonitor +} diff --git a/controllers/vector_controller.go b/controllers/vector_controller.go index bb7d7e47..1dc43ecd 100644 --- a/controllers/vector_controller.go +++ b/controllers/vector_controller.go @@ -46,6 +46,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/source" vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" + monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" ) // VectorReconciler reconciles a Vector object @@ -114,6 +115,7 @@ func (r *VectorReconciler) SetupWithManager(mgr ctrl.Manager) error { Owns(&corev1.ServiceAccount{}). Owns(&rbacv1.ClusterRole{}). Owns(&rbacv1.ClusterRoleBinding{}). + Owns(&monitorv1.ServiceMonitor{}). WithOptions(controller.Options{MaxConcurrentReconciles: 10}). Complete(r) } diff --git a/go.mod b/go.mod index a2c10637..7da08190 100644 --- a/go.mod +++ b/go.mod @@ -5,20 +5,21 @@ go 1.18 require ( github.com/mitchellh/mapstructure v1.5.0 github.com/onsi/ginkgo v1.16.5 - github.com/onsi/gomega v1.18.1 + github.com/onsi/gomega v1.19.0 + github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.60.1 github.com/stretchr/testify v1.7.0 - k8s.io/api v0.24.2 - k8s.io/apimachinery v0.24.2 - k8s.io/client-go v0.24.2 - k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 - sigs.k8s.io/controller-runtime v0.12.2 + k8s.io/api v0.25.0 + k8s.io/apimachinery v0.25.0 + k8s.io/client-go v0.25.0 + k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73 + sigs.k8s.io/controller-runtime v0.12.3 ) require ( - cloud.google.com/go v0.81.0 // indirect + cloud.google.com/go v0.97.0 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect - github.com/Azure/go-autorest/autorest v0.11.18 // indirect - github.com/Azure/go-autorest/autorest/adal v0.9.13 // indirect + github.com/Azure/go-autorest/autorest v0.11.27 // indirect + github.com/Azure/go-autorest/autorest/adal v0.9.20 // indirect github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect @@ -27,21 +28,21 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/emicklei/go-restful v2.9.5+incompatible // indirect + github.com/emicklei/go-restful/v3 v3.8.0 // indirect github.com/evanphx/json-patch v4.12.0+incompatible // indirect - github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect - github.com/go-logr/logr v1.2.0 // indirect - github.com/go-logr/zapr v1.2.0 // indirect + github.com/go-logr/logr v1.2.3 // indirect + github.com/go-logr/zapr v1.2.3 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.19.5 // indirect github.com/go-openapi/swag v0.19.14 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang-jwt/jwt/v4 v4.2.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect - github.com/google/go-cmp v0.5.5 // indirect - github.com/google/gofuzz v1.1.0 // indirect + github.com/google/go-cmp v0.5.8 // indirect + github.com/google/gofuzz v1.2.0 // indirect github.com/google/uuid v1.1.2 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/josharian/intern v1.0.0 // indirect @@ -62,25 +63,25 @@ require ( go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.19.1 // indirect - golang.org/x/crypto v0.0.0-20220214200702-86341886e292 // indirect - golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect + golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect + golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b // indirect golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect - golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect + golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.27.1 // indirect + google.golang.org/protobuf v1.28.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect - k8s.io/apiextensions-apiserver v0.24.2 // indirect - k8s.io/component-base v0.24.2 // indirect - k8s.io/klog/v2 v2.60.1 // indirect - k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42 // indirect - sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/apiextensions-apiserver v0.25.0 // indirect + k8s.io/component-base v0.25.0 // indirect + k8s.io/klog/v2 v2.80.0 // indirect + k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect + sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index 250a18ed..d113a4ce 100644 --- a/go.sum +++ b/go.sum @@ -17,8 +17,15 @@ cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKP cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0 h1:at8Tk2zUz63cLPR0JPWm5vp77pEZmzxEQBEfRKn1VV8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0 h1:3DXvAyifywvq64LfkKaMOmkWPS1CikIQdMe2lY9vxU8= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -27,7 +34,6 @@ cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4g cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -38,25 +44,24 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.18 h1:90Y4srNYrwOtAgVo3ndrQkTYn6kf1Eg/AjTFJ8Is2aM= -github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= -github.com/Azure/go-autorest/autorest/adal v0.9.13 h1:Mp5hbtOePIzM8pJVRa3YLrWWmZtoxRXqUEzCfJt3+/Q= -github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= +github.com/Azure/go-autorest/autorest v0.11.27 h1:F3R3q42aWytozkV8ihzcgMO4OA4cuqr3bNlsEuF6//A= +github.com/Azure/go-autorest/autorest v0.11.27/go.mod h1:7l8ybrIdUmGqZMTD0sRtAr8NvbHjfofbf8RSP2q7w7U= +github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= +github.com/Azure/go-autorest/autorest/adal v0.9.20 h1:gJ3E98kMpFB1MFqQCvA1yFab8vthOeD4VlFRQULxahg= +github.com/Azure/go-autorest/autorest/adal v0.9.20/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw= +github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= @@ -68,25 +73,13 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210826220005-b48c857c3a0e/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= @@ -99,31 +92,13 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= -github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= -github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= -github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful/v3 v3.8.0 h1:eCZ8ulSerjdAiaNpF7GxXIE7ZCMo1moN1qX+S609eVw= +github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -135,17 +110,10 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c= -github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= -github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= -github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -157,15 +125,15 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v1.2.0 h1:QK40JKJyMdUDz+h+xvCsru/bJhvG0UxvePV0ufL/AcE= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/zapr v1.2.0 h1:n4JnPI1T3Qq1SFEi/F8rwLrZERp2bso19PJZDB9dayk= -github.com/go-logr/zapr v1.2.0/go.mod h1:Qa4Bsj2Vb+FAVeAKsLD8RLQ+YRJB8YDmOAKxaBQf7Ro= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= +github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM= github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= @@ -173,15 +141,13 @@ github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5F github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU= +github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -195,6 +161,7 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -213,11 +180,9 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= -github.com/google/cel-go v0.10.1/go.mod h1:U7ayypeSkw23szu4GaQTPJGx66c20mx8JklMSxrmI1w= -github.com/google/cel-spec v0.6.0/go.mod h1:Nwjgxy5CbjlPrtCWjeDjUyKMl8w41YBYGjsyDdqk0xA= github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -230,14 +195,17 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= -github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -249,51 +217,24 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= @@ -304,16 +245,12 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -321,30 +258,15 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= -github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -352,48 +274,33 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.0.0 h1:CcuG/HvWNkkaqCUpJifQY8z7qEMBJya6aLPx6ftGyjQ= -github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/ginkgo/v2 v2.1.4 h1:GNapqRSid3zijZ9H77KrgVG4/8KqiyRsxcSxe+7ApXY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= -github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= +github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.60.1 h1:A46xpyCEQpMFymrNJOaL5aAu3ZWgEKwJUXZrB5D3IUM= +github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.60.1/go.mod h1:MNl09GdaKb/vE8QdcCWyICDV7XAbGX6gKKQAS43XW1c= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= @@ -404,50 +311,26 @@ github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1: github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= -github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -458,28 +341,11 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= -go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= -go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= -go.etcd.io/etcd/client/v3 v3.5.1/go.mod h1:OnjH4M8OnAotwaB2l9bVgZzRFKru7/ZMoS46OtKyd3Q= -go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE= -go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc= -go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -487,44 +353,27 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= -go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= -go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= -go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= -go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= -go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= -go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= -go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= -go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI= go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE= -golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38= +golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -560,14 +409,10 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -598,19 +443,16 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY= +golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -623,6 +465,9 @@ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -636,12 +481,9 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -655,7 +497,6 @@ golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -671,13 +512,11 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -690,19 +529,20 @@ golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158 h1:rm+CHSpPEEW2IsXUib1ThaHIjuBVZjxNgSKmBLFfD4c= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -719,30 +559,24 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -762,7 +596,6 @@ golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjs golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= @@ -778,13 +611,14 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.10-0.20220218145154-897bd77cd717/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= @@ -809,6 +643,13 @@ google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34q google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -838,7 +679,6 @@ google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= @@ -850,7 +690,6 @@ google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201102152239-715cce707fb0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= @@ -860,9 +699,21 @@ google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -883,8 +734,12 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -897,8 +752,9 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -909,13 +765,8 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -927,10 +778,9 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= -gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -938,40 +788,31 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.24.2 h1:g518dPU/L7VRLxWfcadQn2OnsiGWVOadTLpdnqgY2OI= -k8s.io/api v0.24.2/go.mod h1:AHqbSkTm6YrQ0ObxjO3Pmp/ubFF/KuM7jU+3khoBsOg= -k8s.io/apiextensions-apiserver v0.24.2 h1:/4NEQHKlEz1MlaK/wHT5KMKC9UKYz6NZz6JE6ov4G6k= -k8s.io/apiextensions-apiserver v0.24.2/go.mod h1:e5t2GMFVngUEHUd0wuCJzw8YDwZoqZfJiGOW6mm2hLQ= -k8s.io/apimachinery v0.24.2 h1:5QlH9SL2C8KMcrNJPor+LbXVTaZRReml7svPEh4OKDM= -k8s.io/apimachinery v0.24.2/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= -k8s.io/apiserver v0.24.2/go.mod h1:pSuKzr3zV+L+MWqsEo0kHHYwCo77AT5qXbFXP2jbvFI= -k8s.io/client-go v0.24.2 h1:CoXFSf8if+bLEbinDqN9ePIDGzcLtqhfd6jpfnwGOFA= -k8s.io/client-go v0.24.2/go.mod h1:zg4Xaoo+umDsfCWr4fCnmLEtQXyCNXCvJuSsglNcV30= -k8s.io/code-generator v0.24.2/go.mod h1:dpVhs00hTuTdTY6jvVxvTFCk6gSMrtfRydbhZwHI15w= -k8s.io/component-base v0.24.2 h1:kwpQdoSfbcH+8MPN4tALtajLDfSfYxBDYlXobNWI6OU= -k8s.io/component-base v0.24.2/go.mod h1:ucHwW76dajvQ9B7+zecZAP3BVqvrHoOxm8olHEg0nmM= -k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/gengo v0.0.0-20211129171323-c02415ce4185/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/api v0.25.0 h1:H+Q4ma2U/ww0iGB78ijZx6DRByPz6/733jIuFpX70e0= +k8s.io/api v0.25.0/go.mod h1:ttceV1GyV1i1rnmvzT3BST08N6nGt+dudGrquzVQWPk= +k8s.io/apiextensions-apiserver v0.25.0 h1:CJ9zlyXAbq0FIW8CD7HHyozCMBpDSiH7EdrSTCZcZFY= +k8s.io/apiextensions-apiserver v0.25.0/go.mod h1:3pAjZiN4zw7R8aZC5gR0y3/vCkGlAjCazcg1me8iB/E= +k8s.io/apimachinery v0.25.0 h1:MlP0r6+3XbkUG2itd6vp3oxbtdQLQI94fD5gCS+gnoU= +k8s.io/apimachinery v0.25.0/go.mod h1:qMx9eAk0sZQGsXGu86fab8tZdffHbwUfsvzqKn4mfB0= +k8s.io/client-go v0.25.0 h1:CVWIaCETLMBNiTUta3d5nzRbXvY5Hy9Dpl+VvREpu5E= +k8s.io/client-go v0.25.0/go.mod h1:lxykvypVfKilxhTklov0wz1FoaUZ8X4EwbhS6rpRfN8= +k8s.io/component-base v0.25.0 h1:haVKlLkPCFZhkcqB6WCvpVxftrg6+FK5x1ZuaIDaQ5Y= +k8s.io/component-base v0.25.0/go.mod h1:F2Sumv9CnbBlqrpdf7rKZTmmd2meJq0HizeyY/yAFxk= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.60.1 h1:VW25q3bZx9uE3vvdL6M8ezOX79vA2Aq1nEWLqNQclHc= -k8s.io/klog/v2 v2.60.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42 h1:Gii5eqf+GmIEwGNKQYQClCayuJCe2/4fZUvF7VG99sU= -k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk= -k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 h1:HNSDgDCrr/6Ly3WEGKZftiE7IY19Vz2GdbOCyI4qqhc= -k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/klog/v2 v2.80.0 h1:lyJt0TWMPaGoODa8B8bUuxgHS3W/m/bNr2cca3brA/g= +k8s.io/klog/v2 v2.80.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 h1:MQ8BAZPZlWk3S9K4a9NCkIFQtZShWqoha7snGixVgEA= +k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU= +k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73 h1:H9TCJUUx+2VA0ZiD9lvtaX8fthFsMoD+Izn93E/hm8U= +k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.30/go.mod h1:fEO7lRTdivWO2qYVCVG7dEADOMo/MLDCVr8So2g88Uw= -sigs.k8s.io/controller-runtime v0.12.2 h1:nqV02cvhbAj7tbt21bpPpTByrXGn2INHRsi39lXy9sE= -sigs.k8s.io/controller-runtime v0.12.2/go.mod h1:qKsk4WE6zW2Hfj0G4v10EnNB2jMG1C+NTb8h+DwCoU0= -sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 h1:kDi4JBNAsJWfz1aEXhO8Jg87JJaPNLh5tIzYHgStQ9Y= -sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY= -sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y= -sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sigs.k8s.io/controller-runtime v0.12.3 h1:FCM8xeY/FI8hoAfh/V4XbbYMY20gElh9yh+A98usMio= +sigs.k8s.io/controller-runtime v0.12.3/go.mod h1:qKsk4WE6zW2Hfj0G4v10EnNB2jMG1C+NTb8h+DwCoU0= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/main.go b/main.go index ce99ac83..7091baaf 100644 --- a/main.go +++ b/main.go @@ -43,6 +43,7 @@ import ( observabilityv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" "github.com/kaasops/vector-operator/controllers" "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" + monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" //+kubebuilder:scaffold:imports ) @@ -55,6 +56,7 @@ func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) utilruntime.Must(observabilityv1alpha1.AddToScheme(scheme)) + utilruntime.Must(monitorv1.AddToScheme(scheme)) //+kubebuilder:scaffold:scheme } From c8275749445fb4b98a944853d15156193b2d79f2 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Tue, 28 Mar 2023 16:49:08 +0300 Subject: [PATCH 177/316] update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf8e5180..923dfe29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +### Added +- [[85]](https://github.com/kaasops/vector-operator/pull/85) **Feature** Add metrics exporter and ServiceMonitor creation + ### v0.0.13 - [[83]](https://github.com/kaasops/vector-operator/pull/83) **Fix** Fix configcheck pods cleanup From b5fdf0e2ec2013be84217e05ce567f1e06ce52af Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Tue, 28 Mar 2023 16:58:09 +0300 Subject: [PATCH 178/316] release v0.0.14 --- CHANGELOG.md | 2 +- helm/charts/vector-operator/Chart.yaml | 4 +-- helm/index.yaml | 33 ++++++++++++++++------- helm/packages/vector-operator-0.0.14.tgz | Bin 0 -> 15041 bytes 4 files changed, 26 insertions(+), 13 deletions(-) create mode 100644 helm/packages/vector-operator-0.0.14.tgz diff --git a/CHANGELOG.md b/CHANGELOG.md index 923dfe29..dc13abdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -### Added +### v0.0.14 - [[85]](https://github.com/kaasops/vector-operator/pull/85) **Feature** Add metrics exporter and ServiceMonitor creation ### v0.0.13 diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index df21ab6f..2b914d9c 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.13 +version: 0.0.14 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.0.13" +appVersion: "v0.0.14" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/index.yaml b/helm/index.yaml index 236d7bb8..583d14ef 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.0.14 + created: "2023-03-28T16:57:26.273005421+03:00" + description: A Helm chart to install Vector Operator + digest: 7e9ec1ed91a9715c90f21ccb67b54aadfed56312d96f7fbee575e89fec013457 + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.14.tgz + version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2023-01-13T16:31:25.066259888+02:00" + created: "2023-03-28T16:57:26.272425648+03:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2023-01-13T16:31:25.065670061+02:00" + created: "2023-03-28T16:57:26.271229019+03:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2023-01-13T16:31:25.063547713+02:00" + created: "2023-03-28T16:57:26.270488116+03:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2023-01-13T16:31:25.063042068+02:00" + created: "2023-03-28T16:57:26.269900611+03:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2023-01-13T16:31:25.068234376+02:00" + created: "2023-03-28T16:57:26.275296725+03:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2023-01-13T16:31:25.067722577+02:00" + created: "2023-03-28T16:57:26.274704835+03:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2023-01-13T16:31:25.06723177+02:00" + created: "2023-03-28T16:57:26.274150233+03:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2023-01-13T16:31:25.066747139+02:00" + created: "2023-03-28T16:57:26.273587144+03:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2023-01-13T16:31:25.062489433+02:00" + created: "2023-03-28T16:57:26.26928276+03:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -118,4 +131,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2023-01-13T16:31:25.061715304+02:00" +generated: "2023-03-28T16:57:26.262627995+03:00" diff --git a/helm/packages/vector-operator-0.0.14.tgz b/helm/packages/vector-operator-0.0.14.tgz new file mode 100644 index 0000000000000000000000000000000000000000..275aea5ce334d61de8c4174974c1f85120b97492 GIT binary patch literal 15041 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 Date: Wed, 29 Mar 2023 13:04:08 +0300 Subject: [PATCH 179/316] fix pipeline validate --- controllers/factory/config/config_build.go | 40 ++++++---------------- controllers/pipeline_controller.go | 2 +- 2 files changed, 11 insertions(+), 31 deletions(-) diff --git a/controllers/factory/config/config_build.go b/controllers/factory/config/config_build.go index 84eab1de..db636526 100644 --- a/controllers/factory/config/config_build.go +++ b/controllers/factory/config/config_build.go @@ -82,36 +82,6 @@ func (b *Builder) GetByteConfig() ([]byte, error) { if err != nil { return nil, err } - - data, err := vectorConfigToByte(config) - if err != nil { - return nil, err - } - - return data, nil -} - -func (b *Builder) GetByteConfigWithValidate() ([]byte, error) { - config, err := b.generateVectorConfig() - if err != nil { - return nil, err - } - if len(b.Pipelines) != 0 { - for _, pipeline := range b.Pipelines { - for _, source := range config.Sources { - if pipeline.Type() != vectorv1alpha1.ClusterPipelineKind { - if source.Type != KubernetesSourceType { - return nil, PipelineTypeError - } - if source.ExtraNamespaceLabelSelector != "" { - if source.ExtraNamespaceLabelSelector != k8s.NamespaceNameToLabel(pipeline.GetNamespace()) { - return nil, PipelineScopeError - } - } - } - } - } - } data, err := vectorConfigToByte(config) if err != nil { return nil, err @@ -162,6 +132,16 @@ func (b *Builder) getComponents() (sources []*Source, transforms []*Transform, s source.ExtraNamespaceLabelSelector = k8s.NamespaceNameToLabel(pipeline.GetNamespace()) } } + if pipeline.Type() != vectorv1alpha1.ClusterPipelineKind { + if source.Type != KubernetesSourceType { + return nil, nil, nil, PipelineTypeError + } + if source.ExtraNamespaceLabelSelector != "" { + if source.ExtraNamespaceLabelSelector != k8s.NamespaceNameToLabel(pipeline.GetNamespace()) { + return nil, nil, nil, PipelineScopeError + } + } + } sources = append(sources, source) } pipelineTransforms, err := getTransforms(pipeline) diff --git a/controllers/pipeline_controller.go b/controllers/pipeline_controller.go index 7a8edaa5..12d02a11 100644 --- a/controllers/pipeline_controller.go +++ b/controllers/pipeline_controller.go @@ -110,7 +110,7 @@ func (r *PipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c // Get Vector Config file configBuilder := config.NewBuilder(vaCtrl, pipelineCR) - byteConfig, err := configBuilder.GetByteConfigWithValidate() + byteConfig, err := configBuilder.GetByteConfig() if err != nil { if err := pipeline.SetFailedStatus(ctx, r.Client, pipelineCR, err.Error()); err != nil { return ctrl.Result{}, err From d7d2d11d9e49aca51075c78f917ccdf78e110db5 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Wed, 29 Mar 2023 13:12:11 +0300 Subject: [PATCH 180/316] fix-release-v0.0.14 --- helm/index.yaml | 24 +++++++++++------------ helm/packages/vector-operator-0.0.14.tgz | Bin 15041 -> 15041 bytes 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/helm/index.yaml b/helm/index.yaml index 583d14ef..311780eb 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -3,9 +3,9 @@ entries: vector-operator: - apiVersion: v2 appVersion: v0.0.14 - created: "2023-03-28T16:57:26.273005421+03:00" + created: "2023-03-29T13:11:19.357491121+03:00" description: A Helm chart to install Vector Operator - digest: 7e9ec1ed91a9715c90f21ccb67b54aadfed56312d96f7fbee575e89fec013457 + digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator name: vector-operator sources: @@ -16,7 +16,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2023-03-28T16:57:26.272425648+03:00" + created: "2023-03-29T13:11:19.356901628+03:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -29,7 +29,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2023-03-28T16:57:26.271229019+03:00" + created: "2023-03-29T13:11:19.356190001+03:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -42,7 +42,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2023-03-28T16:57:26.270488116+03:00" + created: "2023-03-29T13:11:19.35549268+03:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -55,7 +55,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2023-03-28T16:57:26.269900611+03:00" + created: "2023-03-29T13:11:19.354532976+03:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -68,7 +68,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2023-03-28T16:57:26.275296725+03:00" + created: "2023-03-29T13:11:19.361226331+03:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -81,7 +81,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2023-03-28T16:57:26.274704835+03:00" + created: "2023-03-29T13:11:19.360596391+03:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -94,7 +94,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2023-03-28T16:57:26.274150233+03:00" + created: "2023-03-29T13:11:19.359987496+03:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -107,7 +107,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2023-03-28T16:57:26.273587144+03:00" + created: "2023-03-29T13:11:19.358085935+03:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -120,7 +120,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2023-03-28T16:57:26.26928276+03:00" + created: "2023-03-29T13:11:19.35393054+03:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -131,4 +131,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2023-03-28T16:57:26.262627995+03:00" +generated: "2023-03-29T13:11:19.353265017+03:00" diff --git a/helm/packages/vector-operator-0.0.14.tgz b/helm/packages/vector-operator-0.0.14.tgz index 275aea5ce334d61de8c4174974c1f85120b97492..4ce95c9a8b79a132daed7313fc2da04b147afbcd 100644 GIT binary patch delta 13847 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 CB~y<8 delta 13847 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 Date: Wed, 29 Mar 2023 17:16:06 +0300 Subject: [PATCH 181/316] poc optomized config --- api/v1alpha1/vector_types.go | 5 ++ .../observability.kaasops.io_vectors.yaml | 3 + .../observability_v1alpha1_vector.yaml | 5 +- controllers/factory/config/config_build.go | 74 ++++++++++++++++++- controllers/factory/config/types.go | 10 ++- 5 files changed, 87 insertions(+), 10 deletions(-) diff --git a/api/v1alpha1/vector_types.go b/api/v1alpha1/vector_types.go index 94af4924..57716bbb 100644 --- a/api/v1alpha1/vector_types.go +++ b/api/v1alpha1/vector_types.go @@ -30,6 +30,11 @@ type VectorSpec struct { // DisableAggregation bool `json:"disableAggregation,omitempty"` // Vector Agent Agent *VectorAgent `json:"agent,omitempty"` + + // Enable kubernetes source config optimization + // +optional + OptimizeKubeSourceConfig bool `json:"optimizeKubeSourceConfig,omitempty"` + // Vector Aggregator // Aggregator *VectorAggregator `json:"aggregator,omitempty"` } diff --git a/config/crd/bases/observability.kaasops.io_vectors.yaml b/config/crd/bases/observability.kaasops.io_vectors.yaml index cd000e2c..1c88c6fd 100644 --- a/config/crd/bases/observability.kaasops.io_vectors.yaml +++ b/config/crd/bases/observability.kaasops.io_vectors.yaml @@ -2320,6 +2320,9 @@ spec: type: object type: array type: object + optimizeKubeSourceConfig: + description: Enable kubernetes source config optimization + type: boolean type: object status: description: VectorStatus defines the observed state of Vector diff --git a/config/samples/observability_v1alpha1_vector.yaml b/config/samples/observability_v1alpha1_vector.yaml index 54435af0..1c388f44 100644 --- a/config/samples/observability_v1alpha1_vector.yaml +++ b/config/samples/observability_v1alpha1_vector.yaml @@ -4,8 +4,9 @@ metadata: name: vector-sample namespace: vector spec: + optimizeKubeSourceConfig: true agent: image: "timberio/vector:0.28.1-distroless-libc" - internalMetrics: true + internalMetrics: false api: - enabled: true + enabled: false diff --git a/controllers/factory/config/config_build.go b/controllers/factory/config/config_build.go index db636526..effc794a 100644 --- a/controllers/factory/config/config_build.go +++ b/controllers/factory/config/config_build.go @@ -17,21 +17,28 @@ limitations under the License. package config import ( + "bytes" "encoding/json" "errors" + "fmt" + "strings" vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" "github.com/kaasops/vector-operator/controllers/factory/pipeline" "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" "github.com/kaasops/vector-operator/controllers/factory/vector/vectoragent" "github.com/mitchellh/mapstructure" + "k8s.io/apimachinery/pkg/labels" ) const ( - KubernetesSourceType = "kubernetes_logs" - BlackholeSinkType = "blackhole" - InternalMetricsSourceType = "internal_metrics" - InternalMetricsSinkType = "prometheus_exporter" + KubernetesSourceType = "kubernetes_logs" + BlackholeSinkType = "blackhole" + InternalMetricsSourceType = "internal_metrics" + InternalMetricsSinkType = "prometheus_exporter" + OptimizedKubernetesSourceName = "optimizedKubernetesSource" + FilterTransformType = "filter" + DefaultSourceName = "defaultSource" ) var ( @@ -113,6 +120,12 @@ func (b *Builder) generateVectorConfig() (*VectorConfig, error) { vectorConfig.Sources = sources vectorConfig.Transforms = transforms + if b.vaCtrl.Vector.Spec.OptimizeKubeSourceConfig { + if err := b.optimizeVectorConfig(vectorConfig); err != nil { + return nil, err + } + } + return vectorConfig, nil } @@ -291,3 +304,56 @@ func cfgToMap(config *VectorConfig) (cfgMap map[string]interface{}, err error) { return cfgMap, nil } + +// Experemental +func (b *Builder) optimizeVectorConfig(config *VectorConfig) error { + var optimizedSource []*Source + for _, source := range config.Sources { + if source.Type == KubernetesSourceType && source.ExtraLabelSelector != "" { + optimizedSource = append(optimizedSource, &Source{ + Name: OptimizedKubernetesSourceName, + Type: KubernetesSourceType, + }) + + podLabelsMap, err := labels.ConvertSelectorToLabelsMap(source.ExtraLabelSelector) + if err != nil { + return err + } + + nsLabelsMap, err := labels.ConvertSelectorToLabelsMap(source.ExtraNamespaceLabelSelector) + if err != nil { + return err + } + config.Transforms = append(config.Transforms, &Transform{ + Name: source.Name, + Inputs: []string{OptimizedKubernetesSourceName}, + Type: FilterTransformType, + Condition: generatePodFilterVrl(podLabelsMap) + "&&" + generateNsFilterVrl(nsLabelsMap), + }) + continue + } + optimizedSource = append(optimizedSource, source) + } + config.Sources = optimizedSource + return nil +} + +func generatePodFilterVrl(selector map[string]string) string { + buffer := new(bytes.Buffer) + + for key, value := range selector { + fmt.Fprintf(buffer, ".kubernetes.pod_labels.\"%s\"==\"%s\"&&", key, value) + } + vrlstring := buffer.String() + return strings.TrimSuffix(vrlstring, "&&") +} + +func generateNsFilterVrl(selector map[string]string) string { + buffer := new(bytes.Buffer) + + for key, value := range selector { + fmt.Fprintf(buffer, ".kubernetes.namespace_labels.\"%s\"==\"%s\"&&", key, value) + } + vrlstring := buffer.String() + return strings.TrimSuffix(vrlstring, "&&") +} diff --git a/controllers/factory/config/types.go b/controllers/factory/config/types.go index 2d4cfb62..f48c95b9 100644 --- a/controllers/factory/config/types.go +++ b/controllers/factory/config/types.go @@ -28,14 +28,16 @@ type Source struct { Name string Type string `mapper:"type"` ExtraNamespaceLabelSelector string `mapstructure:"extra_namespace_label_selector" mapper:"extra_namespace_label_selector,omitempty"` + ExtraLabelSelector string `mapstructure:"extra_label_selector" mapper:"extra_label_selector,omitempty"` Options map[string]interface{} `mapstructure:",remain"` } type Transform struct { - Name string - Type string `mapper:"type"` - Inputs []string `mapper:"inputs"` - Options map[string]interface{} `mapstructure:",remain"` + Name string + Type string `mapper:"type"` + Inputs []string `mapper:"inputs"` + Condition string `mapper:"condition,omitempty"` + Options map[string]interface{} `mapstructure:",remain"` } type Sink struct { From a56748d63d736ca3a26eef4d602975d21a0c0585 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Thu, 30 Mar 2023 16:00:10 +0300 Subject: [PATCH 182/316] fix vrl for filter when not equals selector --- controllers/factory/config/config_build.go | 62 +++++++++++++--------- controllers/factory/config/types.go | 1 + 2 files changed, 39 insertions(+), 24 deletions(-) diff --git a/controllers/factory/config/config_build.go b/controllers/factory/config/config_build.go index effc794a..a01906b9 100644 --- a/controllers/factory/config/config_build.go +++ b/controllers/factory/config/config_build.go @@ -28,7 +28,6 @@ import ( "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" "github.com/kaasops/vector-operator/controllers/factory/vector/vectoragent" "github.com/mitchellh/mapstructure" - "k8s.io/apimachinery/pkg/labels" ) const ( @@ -39,6 +38,8 @@ const ( OptimizedKubernetesSourceName = "optimizedKubernetesSource" FilterTransformType = "filter" DefaultSourceName = "defaultSource" + PodSelectorType = "pod_labels" + NamespaceSelectorType = "ns_labels" ) var ( @@ -308,52 +309,65 @@ func cfgToMap(config *VectorConfig) (cfgMap map[string]interface{}, err error) { // Experemental func (b *Builder) optimizeVectorConfig(config *VectorConfig) error { var optimizedSource []*Source + var optimizationRequired bool for _, source := range config.Sources { if source.Type == KubernetesSourceType && source.ExtraLabelSelector != "" { - optimizedSource = append(optimizedSource, &Source{ - Name: OptimizedKubernetesSourceName, - Type: KubernetesSourceType, - }) - - podLabelsMap, err := labels.ConvertSelectorToLabelsMap(source.ExtraLabelSelector) - if err != nil { - return err + if source.ExtraFieldSelector != "" { + optimizedSource = append(optimizedSource, source) + continue } + optimizationRequired = true - nsLabelsMap, err := labels.ConvertSelectorToLabelsMap(source.ExtraNamespaceLabelSelector) - if err != nil { - return err - } config.Transforms = append(config.Transforms, &Transform{ Name: source.Name, Inputs: []string{OptimizedKubernetesSourceName}, Type: FilterTransformType, - Condition: generatePodFilterVrl(podLabelsMap) + "&&" + generateNsFilterVrl(nsLabelsMap), + Condition: generateVrlFilter(source.ExtraLabelSelector, PodSelectorType) + "&&" + generateVrlFilter(source.ExtraNamespaceLabelSelector, NamespaceSelectorType), }) continue } optimizedSource = append(optimizedSource, source) } - config.Sources = optimizedSource + + if optimizationRequired { + optimizedSource = append(optimizedSource, &Source{ + Name: OptimizedKubernetesSourceName, + Type: KubernetesSourceType, + }) + config.Sources = optimizedSource + } + return nil } -func generatePodFilterVrl(selector map[string]string) string { +func generateVrlFilter(selector string, selectorType string) string { buffer := new(bytes.Buffer) - for key, value := range selector { - fmt.Fprintf(buffer, ".kubernetes.pod_labels.\"%s\"==\"%s\"&&", key, value) + labels := strings.Split(selector, ",") + + for _, label := range labels { + label = formatVrlSelector(label) + switch selectorType { + case PodSelectorType: + fmt.Fprintf(buffer, ".kubernetes.pod_labels.%s&&", label) + case NamespaceSelectorType: + fmt.Fprintf(buffer, ".kubernetes.namespace_labels.%s&&", label) + } } + vrlstring := buffer.String() return strings.TrimSuffix(vrlstring, "&&") } -func generateNsFilterVrl(selector map[string]string) string { - buffer := new(bytes.Buffer) +func formatVrlSelector(label string) string { + l := strings.Split(label, "!=") + if len(l) == 2 { + return fmt.Sprintf("\"%s\" != \"%s\"", l[0], l[1]) + } - for key, value := range selector { - fmt.Fprintf(buffer, ".kubernetes.namespace_labels.\"%s\"==\"%s\"&&", key, value) + l = strings.Split(label, "=") + if len(l) != 2 { + return label } - vrlstring := buffer.String() - return strings.TrimSuffix(vrlstring, "&&") + return fmt.Sprintf("\"%s\" == \"%s\"", l[0], l[1]) } diff --git a/controllers/factory/config/types.go b/controllers/factory/config/types.go index f48c95b9..4b5173d0 100644 --- a/controllers/factory/config/types.go +++ b/controllers/factory/config/types.go @@ -29,6 +29,7 @@ type Source struct { Type string `mapper:"type"` ExtraNamespaceLabelSelector string `mapstructure:"extra_namespace_label_selector" mapper:"extra_namespace_label_selector,omitempty"` ExtraLabelSelector string `mapstructure:"extra_label_selector" mapper:"extra_label_selector,omitempty"` + ExtraFieldSelector string `mapstructure:"extra_field_selector" mapper:"extra_field_selector,omitempty"` Options map[string]interface{} `mapstructure:",remain"` } From bd826a249f6eceb9fa156df5beaa86c2639ae0b3 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Thu, 30 Mar 2023 16:06:38 +0300 Subject: [PATCH 183/316] exclude cvp from config optimization --- controllers/factory/config/config_build.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controllers/factory/config/config_build.go b/controllers/factory/config/config_build.go index a01906b9..9981c54a 100644 --- a/controllers/factory/config/config_build.go +++ b/controllers/factory/config/config_build.go @@ -311,7 +311,7 @@ func (b *Builder) optimizeVectorConfig(config *VectorConfig) error { var optimizedSource []*Source var optimizationRequired bool for _, source := range config.Sources { - if source.Type == KubernetesSourceType && source.ExtraLabelSelector != "" { + if source.ExtraNamespaceLabelSelector != "" && source.Type == KubernetesSourceType && source.ExtraLabelSelector != "" { if source.ExtraFieldSelector != "" { optimizedSource = append(optimizedSource, source) continue From aa10edc2643aef86e037ef846456d40e4bcc1c1a Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Thu, 30 Mar 2023 16:14:27 +0300 Subject: [PATCH 184/316] release v0.0.15 --- CHANGELOG.md | 21 ++++++++------ helm/charts/vector-operator/Chart.yaml | 4 +-- helm/index.yaml | 35 ++++++++++++++++------- helm/packages/vector-operator-0.0.15.tgz | Bin 0 -> 15042 bytes 4 files changed, 38 insertions(+), 22 deletions(-) create mode 100644 helm/packages/vector-operator-0.0.15.tgz diff --git a/CHANGELOG.md b/CHANGELOG.md index dc13abdd..e6e22b4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,23 +1,26 @@ +### v0.0.15 +- [[85](https://github.com/kaasops/vector-operator/pull/89] **Feature** Added experemental config optimization option + ### v0.0.14 -- [[85]](https://github.com/kaasops/vector-operator/pull/85) **Feature** Add metrics exporter and ServiceMonitor creation +- [[85](https://github.com/kaasops/vector-operator/pull/85)] **Feature** Add metrics exporter and ServiceMonitor creation ### v0.0.13 -- [[83]](https://github.com/kaasops/vector-operator/pull/83) **Fix** Fix configcheck pods cleanup +- [[83](https://github.com/kaasops/vector-operator/pull/83)] **Fix** Fix configcheck pods cleanup ### v0.0.12 -- [[81]](https://github.com/kaasops/vector-operator/pull/81) **Feature** Add control for ConfigCheck params +- [[81](https://github.com/kaasops/vector-operator/pull/81)] **Feature** Add control for ConfigCheck params ### v0.0.11 -- [[79]](https://github.com/kaasops/vector-operator/pull/79) **Fix** Remove vector.dev/exclude label -- [[77]](https://github.com/kaasops/vector-operator/pull/77) **Feature** Concurrent pipeline checks +- [[79](https://github.com/kaasops/vector-operator/pull/79)] **Fix** Remove vector.dev/exclude label +- [[77](https://github.com/kaasops/vector-operator/pull/77)] **Feature** Concurrent pipeline checks ### v0.0.10 -- [[73]](https://github.com/kaasops/vector-operator/pull/73) **Fix** Add vector tolerations for configCheck pod +- [[73](https://github.com/kaasops/vector-operator/pull/73)] **Fix** Add vector tolerations for configCheck pod ### v0.0.9 -- [[72]](https://github.com/kaasops/vector-operator/pull/72) **Feature** Add vp to all category -- [[71]](https://github.com/kaasops/vector-operator/pull/71) **Fix** Fix panic on empty sinks and sources -- [[70]](https://github.com/kaasops/vector-operator/pull/70) **Fix** Fix nil vector panic +- [[72](https://github.com/kaasops/vector-operator/pull/72)] **Feature** Add vp to all category +- [[71](https://github.com/kaasops/vector-operator/pull/71)] **Fix** Fix panic on empty sinks and sources +- [[70](https://github.com/kaasops/vector-operator/pull/70)] **Fix** Fix nil vector panic - [[65](https://github.com/kaasops/vector-operator/pull/69)] **Helm**: Add toleration-control for vector-operator deployment to helm chart - [[65](https://github.com/kaasops/vector-operator/pull/69)] **Cleanup**: Add default value for podSecurityContext in chart values diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index 2b914d9c..aa6209df 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.14 +version: 0.0.15 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.0.14" +appVersion: "v0.0.15" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/index.yaml b/helm/index.yaml index 311780eb..783f958e 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.0.15 + created: "2023-03-30T16:14:09.532742299+03:00" + description: A Helm chart to install Vector Operator + digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.15.tgz + version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2023-03-29T13:11:19.357491121+03:00" + created: "2023-03-30T16:14:09.531647751+03:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2023-03-29T13:11:19.356901628+03:00" + created: "2023-03-30T16:14:09.530770933+03:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2023-03-29T13:11:19.356190001+03:00" + created: "2023-03-30T16:14:09.529935997+03:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2023-03-29T13:11:19.35549268+03:00" + created: "2023-03-30T16:14:09.52914344+03:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2023-03-29T13:11:19.354532976+03:00" + created: "2023-03-30T16:14:09.519890148+03:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2023-03-29T13:11:19.361226331+03:00" + created: "2023-03-30T16:14:09.53841056+03:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2023-03-29T13:11:19.360596391+03:00" + created: "2023-03-30T16:14:09.53756537+03:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2023-03-29T13:11:19.359987496+03:00" + created: "2023-03-30T16:14:09.536520545+03:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2023-03-29T13:11:19.358085935+03:00" + created: "2023-03-30T16:14:09.53366963+03:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2023-03-29T13:11:19.35393054+03:00" + created: "2023-03-30T16:14:09.516041075+03:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -131,4 +144,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2023-03-29T13:11:19.353265017+03:00" +generated: "2023-03-30T16:14:09.510805402+03:00" diff --git a/helm/packages/vector-operator-0.0.15.tgz b/helm/packages/vector-operator-0.0.15.tgz new file mode 100644 index 0000000000000000000000000000000000000000..32187816518121d7c336100c40eafa0fdea0c7d7 GIT binary patch literal 15042 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 literal 0 HcmV?d00001 From 101be87a879af849df0bf5a7b08bb04faaa7cda0 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Thu, 30 Mar 2023 16:23:49 +0300 Subject: [PATCH 185/316] update helm chart for optimizeKubeSourceConfig --- helm/charts/vector-operator/templates/vector.yaml | 1 + helm/charts/vector-operator/values.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/helm/charts/vector-operator/templates/vector.yaml b/helm/charts/vector-operator/templates/vector.yaml index 98d4e8f5..49d9311f 100644 --- a/helm/charts/vector-operator/templates/vector.yaml +++ b/helm/charts/vector-operator/templates/vector.yaml @@ -5,6 +5,7 @@ metadata: name: {{ .Values.vector.name }} namespace: {{ .Release.Namespace }} spec: + optimizeKubeSourceConfig: {{ .Values.vector.optimizeKubeSourceConfig}} {{- with .Values.vector.agent }} agent: {{ toYaml . | indent 4 }} diff --git a/helm/charts/vector-operator/values.yaml b/helm/charts/vector-operator/values.yaml index a87822af..192dd01d 100644 --- a/helm/charts/vector-operator/values.yaml +++ b/helm/charts/vector-operator/values.yaml @@ -72,6 +72,7 @@ args: vector: enable: false name: "vector" + optimizeKubeSourceConfig: false # agent: # image: timberio/vector:0.24.0-distroless-libc # env: From 07bf710611415028278f30c08304a5439109447e Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Thu, 30 Mar 2023 16:55:02 +0300 Subject: [PATCH 186/316] fix servicemonitors rbac --- helm/charts/vector-operator/Chart.yaml | 4 +- .../templates/clusterrole.yaml | 6 +++ helm/index.yaml | 37 ++++++++++++------ helm/packages/vector-operator-0.0.16.tgz | Bin 0 -> 15101 bytes 4 files changed, 33 insertions(+), 14 deletions(-) create mode 100644 helm/packages/vector-operator-0.0.16.tgz diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index aa6209df..df5f77e5 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.15 +version: 0.0.16 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.0.15" +appVersion: "v0.0.16" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/charts/vector-operator/templates/clusterrole.yaml b/helm/charts/vector-operator/templates/clusterrole.yaml index d3ee4070..cb9c9d10 100644 --- a/helm/charts/vector-operator/templates/clusterrole.yaml +++ b/helm/charts/vector-operator/templates/clusterrole.yaml @@ -96,4 +96,10 @@ rules: - get - patch - update +- apiGroups: + - monitoring.coreos.com + resources: + - servicemonitors + verbs: + - '*' {{- end -}} diff --git a/helm/index.yaml b/helm/index.yaml index 783f958e..265cdc11 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.0.16 + created: "2023-03-30T16:54:33.487943791+03:00" + description: A Helm chart to install Vector Operator + digest: 394b81ccfd7dc24e92f77808da765b7196b9e7dc7b54a0e0f86e839998f0d57c + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.16.tgz + version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2023-03-30T16:14:09.532742299+03:00" + created: "2023-03-30T16:54:33.486945585+03:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2023-03-30T16:14:09.531647751+03:00" + created: "2023-03-30T16:54:33.486359166+03:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2023-03-30T16:14:09.530770933+03:00" + created: "2023-03-30T16:54:33.485662519+03:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2023-03-30T16:14:09.529935997+03:00" + created: "2023-03-30T16:54:33.484965744+03:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2023-03-30T16:14:09.52914344+03:00" + created: "2023-03-30T16:54:33.484235155+03:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2023-03-30T16:14:09.519890148+03:00" + created: "2023-03-30T16:54:33.482243083+03:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2023-03-30T16:14:09.53841056+03:00" + created: "2023-03-30T16:54:33.490338872+03:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2023-03-30T16:14:09.53756537+03:00" + created: "2023-03-30T16:54:33.48978027+03:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2023-03-30T16:14:09.536520545+03:00" + created: "2023-03-30T16:54:33.489209908+03:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2023-03-30T16:14:09.53366963+03:00" + created: "2023-03-30T16:54:33.488661253+03:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2023-03-30T16:14:09.516041075+03:00" + created: "2023-03-30T16:54:33.481628985+03:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -144,4 +157,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2023-03-30T16:14:09.510805402+03:00" +generated: "2023-03-30T16:54:33.474978555+03:00" diff --git a/helm/packages/vector-operator-0.0.16.tgz b/helm/packages/vector-operator-0.0.16.tgz new file mode 100644 index 0000000000000000000000000000000000000000..43a9b159a1f26d822aad5095a8d77e181859d5da GIT binary patch literal 15101 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 g|I1d?dEJYk?S8S(z553K$#nfDp6aCm7y| Date: Thu, 30 Mar 2023 16:56:29 +0300 Subject: [PATCH 187/316] update changelog v0.0.16 --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6e22b4f..b793a960 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +### v0.0.16 +- [[85](https://github.com/kaasops/vector-operator/pull/92] **Fix** Added rbac for ServiceMonitros + ### v0.0.15 - [[85](https://github.com/kaasops/vector-operator/pull/89] **Feature** Added experemental config optimization option From d877f5f178020de19a5dcffa922eed3088b4ffe1 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Thu, 30 Mar 2023 17:25:17 +0300 Subject: [PATCH 188/316] update crds in helm --- .../observability.kaasops.io_vectors.yaml | 10 ++++--- helm/index.yaml | 28 +++++++++--------- helm/packages/vector-operator-0.0.16.tgz | Bin 15101 -> 15147 bytes 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml index f59668bb..1c88c6fd 100644 --- a/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml +++ b/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml @@ -918,8 +918,6 @@ spec: description: ApiSpec is the Schema for the Vector Agent GraphQL API - https://vector.dev/docs/reference/api/ properties: - address: - type: string enabled: type: boolean playground: @@ -2063,6 +2061,9 @@ spec: type: object x-kubernetes-map-type: atomic type: array + internalMetrics: + description: Enable internal metrics exporter + type: boolean podSecurityPolicyName: description: PodSecurityPolicyName - defines name for podSecurityPolicy in case of empty value, prefixedName will be used. @@ -2277,8 +2278,6 @@ spec: type: string type: object type: object - service: - type: boolean tolerations: description: Tolerations If specified, the pod's tolerations. items: @@ -2321,6 +2320,9 @@ spec: type: object type: array type: object + optimizeKubeSourceConfig: + description: Enable kubernetes source config optimization + type: boolean type: object status: description: VectorStatus defines the observed state of Vector diff --git a/helm/index.yaml b/helm/index.yaml index 265cdc11..435db4bb 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -3,9 +3,9 @@ entries: vector-operator: - apiVersion: v2 appVersion: v0.0.16 - created: "2023-03-30T16:54:33.487943791+03:00" + created: "2023-03-30T17:24:44.123711193+03:00" description: A Helm chart to install Vector Operator - digest: 394b81ccfd7dc24e92f77808da765b7196b9e7dc7b54a0e0f86e839998f0d57c + digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator name: vector-operator sources: @@ -16,7 +16,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2023-03-30T16:54:33.486945585+03:00" + created: "2023-03-30T17:24:44.123153464+03:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -29,7 +29,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2023-03-30T16:54:33.486359166+03:00" + created: "2023-03-30T17:24:44.122149227+03:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -42,7 +42,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2023-03-30T16:54:33.485662519+03:00" + created: "2023-03-30T17:24:44.121512327+03:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -55,7 +55,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2023-03-30T16:54:33.484965744+03:00" + created: "2023-03-30T17:24:44.120932497+03:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -68,7 +68,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2023-03-30T16:54:33.484235155+03:00" + created: "2023-03-30T17:24:44.120353616+03:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -81,7 +81,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2023-03-30T16:54:33.482243083+03:00" + created: "2023-03-30T17:24:44.119840946+03:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -94,7 +94,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2023-03-30T16:54:33.490338872+03:00" + created: "2023-03-30T17:24:44.125560479+03:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -107,7 +107,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2023-03-30T16:54:33.48978027+03:00" + created: "2023-03-30T17:24:44.125102851+03:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -120,7 +120,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2023-03-30T16:54:33.489209908+03:00" + created: "2023-03-30T17:24:44.124649556+03:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -133,7 +133,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2023-03-30T16:54:33.488661253+03:00" + created: "2023-03-30T17:24:44.124169718+03:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -146,7 +146,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2023-03-30T16:54:33.481628985+03:00" + created: "2023-03-30T17:24:44.119278546+03:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -157,4 +157,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2023-03-30T16:54:33.474978555+03:00" +generated: "2023-03-30T17:24:44.118265221+03:00" diff --git a/helm/packages/vector-operator-0.0.16.tgz b/helm/packages/vector-operator-0.0.16.tgz index 43a9b159a1f26d822aad5095a8d77e181859d5da..dde051f3730ca7b4807fb98c9e7650460a182d05 100644 GIT binary patch delta 14704 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 R1_OJ4C-C)p1RDhh`+sDP%+UY< delta 14658 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

+ + + + @@ -61,6 +66,14 @@ + + + + + + + + @@ -109,4 +122,4 @@ -
api
securityContext SecurityContext holds pod-level security attributes and common container settings. By default - not set
containerSecurityContextsecurityContext holds security configuration that will be applied to a container.
schedulerName SchedulerName - defines kubernetes scheduler name. By default - not setpodSecurityPolicyName PodSecurityPolicyName - defines name for podSecurityPolicy in case of empty value, prefixedName will be used.
volumesList of volumes that can be mounted by containers belonging to the pod.
volumeMountsPod volumes to mount into the container's filesystem.
priorityClassName PriorityClassName assigned to the Pods.sinks List of Sinks
\ No newline at end of file + diff --git a/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml index 1c88c6fd..6677c272 100644 --- a/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml +++ b/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml @@ -1905,6 +1905,177 @@ spec: type: object type: array type: object + containerSecurityContext: + description: SecurityContext holds security configuration that + will be applied to a container. Some fields are present in both + SecurityContext and PodSecurityContext. When both are set, the + values in SecurityContext take precedence. + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation controls whether a + process can gain more privileges than its parent process. + This bool directly controls if the no_new_privs flag will + be set on the container process. AllowPrivilegeEscalation + is true always when the container is: 1) run as Privileged + 2) has CAP_SYS_ADMIN Note that this field cannot be set + when spec.os.name is windows.' + type: boolean + capabilities: + description: The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the + container runtime. Note that this field cannot be set when + spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. Processes in + privileged containers are essentially equivalent to root + on the host. Defaults to false. Note that this field cannot + be set when spec.os.name is windows. + type: boolean + procMount: + description: procMount denotes the type of proc mount to use + for the containers. The default is DefaultProcMount which + uses the container runtime defaults for readonly paths and + masked paths. This requires the ProcMountType feature flag + to be enabled. Note that this field cannot be set when spec.os.name + is windows. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only root filesystem. + Default is false. Note that this field cannot be set when + spec.os.name is windows. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be set + in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a non-root + user. If true, the Kubelet will validate the image at runtime + to ensure that it does not run as UID 0 (root) and fail + to start the container if it does. If unset or false, no + such validation will be performed. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata if + unspecified. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. Note + that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random + SELinux context for each container. May also be set in + PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by this container. + If seccomp options are provided at both the pod & container + level, the container options override the pod options. Note + that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile must + be preconfigured on the node to work. Must be a descending + path, relative to the kubelet's configured seccomp profile + location. Must only be set if type is "Localhost". + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - a + profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile + should be used. Unconfined - no profile should be applied." + type: string + required: + - type + type: object + windowsOptions: + description: The Windows specific settings applied to all + containers. If unspecified, the options from the PodSecurityContext + will be used. If set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is + linux. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named + by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. This field is + alpha-level and will only be honored by components that + enable the WindowsHostProcessContainers feature flag. + Setting this field without the feature flag will result + in errors when validating the Pod. All of a Pod's containers + must have the same effective HostProcess value (it is + not allowed to have a mix of HostProcess containers + and non-HostProcess containers). In addition, if HostProcess + is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set in + PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. + type: string + type: object + type: object dataDir: type: string env: @@ -2046,6 +2217,9 @@ spec: description: Image - docker image settings for Vector Agent if no specified operator uses default config version type: string + imagePullPolicy: + description: ImagePullPolicy of pods + type: string imagePullSecrets: description: ImagePullSecrets An optional list of references to secrets in the same namespace to use for pulling images from @@ -2064,52 +2238,10 @@ spec: internalMetrics: description: Enable internal metrics exporter type: boolean - podSecurityPolicyName: - description: PodSecurityPolicyName - defines name for podSecurityPolicy - in case of empty value, prefixedName will be used. - type: string - priorityClassName: - description: PriorityClassName assigned to the Pods - type: string - resources: - description: Resources container resource request and limits, - https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - if not specified - default setting will be used - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute - resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, otherwise - to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - runtimeClassName: - description: RuntimeClassName - defines runtime class for kubernetes - pod. https://kubernetes.io/docs/concepts/containers/runtime-class/ - type: string - schedulerName: - description: SchedulerName - defines kubernetes scheduler name - type: string - securityContext: + podSecurityContext: description: SecurityContext holds pod-level security attributes and common container settings. This defaults to the default - PodSecurityContext. Tolerations If specified, the pod's tolerations. + PodSecurityContext. properties: fsGroup: description: "A special supplemental group that applies to @@ -2278,6 +2410,48 @@ spec: type: string type: object type: object + podSecurityPolicyName: + description: PodSecurityPolicyName - defines name for podSecurityPolicy + in case of empty value, prefixedName will be used. + type: string + priorityClassName: + description: PriorityClassName assigned to the Pods + type: string + resources: + description: Resources container resource request and limits, + https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + if not specified - default setting will be used + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, otherwise + to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + runtimeClassName: + description: RuntimeClassName - defines runtime class for kubernetes + pod. https://kubernetes.io/docs/concepts/containers/runtime-class/ + type: string + schedulerName: + description: SchedulerName - defines kubernetes scheduler name + type: string tolerations: description: Tolerations If specified, the pod's tolerations. items: @@ -2319,6 +2493,1620 @@ spec: type: string type: object type: array + volumeMounts: + description: Pod volumes to mount into the container's filesystem. + items: + description: VolumeMount describes a mounting of a Volume within + a container. + properties: + mountPath: + description: Path within the container at which the volume + should be mounted. Must not contain ':'. + type: string + mountPropagation: + description: mountPropagation determines how mounts are + propagated from the host to container and the other way + around. When not set, MountPropagationNone is used. This + field is beta in 1.10. + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write otherwise + (false or unspecified). Defaults to false. + type: boolean + subPath: + description: Path within the volume from which the container's + volume should be mounted. Defaults to "" (volume's root). + type: string + subPathExpr: + description: Expanded path within the volume from which + the container's volume should be mounted. Behaves similarly + to SubPath but environment variable references $(VAR_NAME) + are expanded using the container's environment. Defaults + to "" (volume's root). SubPathExpr and SubPath are mutually + exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + volumes: + description: List of volumes that can be mounted by containers + belonging to the pod. + items: + description: Volume represents a named volume in a pod that + may be accessed by any container in the pod. + properties: + awsElasticBlockStore: + description: 'awsElasticBlockStore represents an AWS Disk + resource that is attached to a kubelet''s host machine + and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + properties: + fsType: + description: 'fsType is the filesystem type of the volume + that you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + partition: + description: 'partition is the partition in the volume + that you want to mount. If omitted, the default is + to mount by volume name. Examples: For volume /dev/sda1, + you specify the partition as "1". Similarly, the volume + partition for /dev/sda is "0" (or you can leave the + property empty).' + format: int32 + type: integer + readOnly: + description: 'readOnly value true will force the readOnly + setting in VolumeMounts. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: boolean + volumeID: + description: 'volumeID is unique ID of the persistent + disk resource in AWS (Amazon EBS volume). More info: + https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: string + required: + - volumeID + type: object + azureDisk: + description: azureDisk represents an Azure Data Disk mount + on the host and bind mount to the pod. + properties: + cachingMode: + description: 'cachingMode is the Host Caching mode: + None, Read Only, Read Write.' + type: string + diskName: + description: diskName is the Name of the data disk in + the blob storage + type: string + diskURI: + description: diskURI is the URI of data disk in the + blob storage + type: string + fsType: + description: fsType is Filesystem type to mount. Must + be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. + type: string + kind: + description: 'kind expected values are Shared: multiple + blob disks per storage account Dedicated: single + blob disk per storage account Managed: azure managed + data disk (only in managed availability set). defaults + to shared' + type: string + readOnly: + description: readOnly Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts. + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + description: azureFile represents an Azure File Service + mount on the host and bind mount to the pod. + properties: + readOnly: + description: readOnly defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretName: + description: secretName is the name of secret that + contains Azure Storage Account Name and Key + type: string + shareName: + description: shareName is the azure share Name + type: string + required: + - secretName + - shareName + type: object + cephfs: + description: cephFS represents a Ceph FS mount on the host + that shares a pod's lifetime + properties: + monitors: + description: 'monitors is Required: Monitors is a collection + of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + items: + type: string + type: array + path: + description: 'path is Optional: Used as the mounted + root, rather than the full Ceph tree, default is /' + type: string + readOnly: + description: 'readOnly is Optional: Defaults to false + (read/write). ReadOnly here will force the ReadOnly + setting in VolumeMounts. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: boolean + secretFile: + description: 'secretFile is Optional: SecretFile is + the path to key ring for User, default is /etc/ceph/user.secret + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + secretRef: + description: 'secretRef is Optional: SecretRef is reference + to the authentication secret for User, default is + empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + type: object + x-kubernetes-map-type: atomic + user: + description: 'user is optional: User is the rados user + name, default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + required: + - monitors + type: object + cinder: + description: 'cinder represents a cinder volume attached + and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + properties: + fsType: + description: 'fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating + system. Examples: "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + readOnly: + description: 'readOnly defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: boolean + secretRef: + description: 'secretRef is optional: points to a secret + object containing parameters used to connect to OpenStack.' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + description: 'volumeID used to identify the volume in + cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + required: + - volumeID + type: object + configMap: + description: configMap represents a configMap that should + populate this volume + properties: + defaultMode: + description: 'defaultMode is optional: mode bits used + to set permissions on created files by default. Must + be an octal value between 0000 and 0777 or a decimal + value between 0 and 511. YAML accepts both octal and + decimal values, JSON requires decimal values for mode + bits. Defaults to 0644. Directories within the path + are not affected by this setting. This might be in + conflict with other options that affect the file mode, + like fsGroup, and the result can be other mode bits + set.' + format: int32 + type: integer + items: + description: items if unspecified, each key-value pair + in the Data field of the referenced ConfigMap will + be projected into the volume as a file whose name + is the key and content is the value. If specified, + the listed keys will be projected into the specified + paths, and unlisted keys will not be present. If a + key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. + Paths must be relative and may not contain the '..' + path or start with '..'. + items: + description: Maps a string key to a path within a + volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits used + to set permissions on this file. Must be an + octal value between 0000 and 0777 or a decimal + value between 0 and 511. YAML accepts both octal + and decimal values, JSON requires decimal values + for mode bits. If not specified, the volume + defaultMode will be used. This might be in conflict + with other options that affect the file mode, + like fsGroup, and the result can be other mode + bits set.' + format: int32 + type: integer + path: + description: path is the relative path of the + file to map the key to. May not be an absolute + path. May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: optional specify whether the ConfigMap + or its keys must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + description: csi (Container Storage Interface) represents + ephemeral storage that is handled by certain external + CSI drivers (Beta feature). + properties: + driver: + description: driver is the name of the CSI driver that + handles this volume. Consult with your admin for the + correct name as registered in the cluster. + type: string + fsType: + description: fsType to mount. Ex. "ext4", "xfs", "ntfs". + If not provided, the empty value is passed to the + associated CSI driver which will determine the default + filesystem to apply. + type: string + nodePublishSecretRef: + description: nodePublishSecretRef is a reference to + the secret object containing sensitive information + to pass to the CSI driver to complete the CSI NodePublishVolume + and NodeUnpublishVolume calls. This field is optional, + and may be empty if no secret is required. If the + secret object contains more than one secret, all secret + references are passed. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + description: readOnly specifies a read-only configuration + for the volume. Defaults to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: + type: string + description: volumeAttributes stores driver-specific + properties that are passed to the CSI driver. Consult + your driver's documentation for supported values. + type: object + required: + - driver + type: object + downwardAPI: + description: downwardAPI represents downward API about the + pod that should populate this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created + files by default. Must be a Optional: mode bits used + to set permissions on created files by default. Must + be an octal value between 0000 and 0777 or a decimal + value between 0 and 511. YAML accepts both octal and + decimal values, JSON requires decimal values for mode + bits. Defaults to 0644. Directories within the path + are not affected by this setting. This might be in + conflict with other options that affect the file mode, + like fsGroup, and the result can be other mode bits + set.' + format: int32 + type: integer + items: + description: Items is a list of downward API volume + file + items: + description: DownwardAPIVolumeFile represents information + to create the file containing the pod field + properties: + fieldRef: + description: 'Required: Selects a field of the + pod: only annotations, labels, name and namespace + are supported.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in + the specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + description: 'Optional: mode bits used to set + permissions on this file, must be an octal value + between 0000 and 0777 or a decimal value between + 0 and 511. YAML accepts both octal and decimal + values, JSON requires decimal values for mode + bits. If not specified, the volume defaultMode + will be used. This might be in conflict with + other options that affect the file mode, like + fsGroup, and the result can be other mode bits + set.' + format: int32 + type: integer + path: + description: 'Required: Path is the relative + path name of the file to be created. Must not + be absolute or contain the ''..'' path. Must + be utf-8 encoded. The first item of the relative + path must not start with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the container: + only resources limits and requests (limits.cpu, + limits.memory, requests.cpu and requests.memory) + are currently supported.' + properties: + containerName: + description: 'Container name: required for + volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of + the exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + type: object + emptyDir: + description: 'emptyDir represents a temporary directory + that shares a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + properties: + medium: + description: 'medium represents what type of storage + medium should back this directory. The default is + "" which means to use the node''s default medium. + Must be an empty string (default) or Memory. More + info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + description: 'sizeLimit is the total amount of local + storage required for this EmptyDir volume. The size + limit is also applicable for memory medium. The maximum + usage on memory medium EmptyDir would be the minimum + value between the SizeLimit specified here and the + sum of memory limits of all containers in a pod. The + default is nil which means that the limit is undefined. + More info: http://kubernetes.io/docs/user-guide/volumes#emptydir' + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + description: "ephemeral represents a volume that is handled + by a cluster storage driver. The volume's lifecycle is + tied to the pod that defines it - it will be created before + the pod starts, and deleted when the pod is removed. \n + Use this if: a) the volume is only needed while the pod + runs, b) features of normal volumes like restoring from + snapshot or capacity tracking are needed, c) the storage + driver is specified through a storage class, and d) the + storage driver supports dynamic volume provisioning through + a PersistentVolumeClaim (see EphemeralVolumeSource for + more information on the connection between this volume + type and PersistentVolumeClaim). \n Use PersistentVolumeClaim + or one of the vendor-specific APIs for volumes that persist + for longer than the lifecycle of an individual pod. \n + Use CSI for light-weight local ephemeral volumes if the + CSI driver is meant to be used that way - see the documentation + of the driver for more information. \n A pod can use both + types of ephemeral volumes and persistent volumes at the + same time." + properties: + volumeClaimTemplate: + description: "Will be used to create a stand-alone PVC + to provision the volume. The pod in which this EphemeralVolumeSource + is embedded will be the owner of the PVC, i.e. the + PVC will be deleted together with the pod. The name + of the PVC will be `-` where + `` is the name from the `PodSpec.Volumes` + array entry. Pod validation will reject the pod if + the concatenated name is not valid for a PVC (for + example, too long). \n An existing PVC with that name + that is not owned by the pod will *not* be used for + the pod to avoid using an unrelated volume by mistake. + Starting the pod is then blocked until the unrelated + PVC is removed. If such a pre-created PVC is meant + to be used by the pod, the PVC has to updated with + an owner reference to the pod once the pod exists. + Normally this should not be necessary, but it may + be useful when manually reconstructing a broken cluster. + \n This field is read-only and no changes will be + made by Kubernetes to the PVC after it has been created. + \n Required, must not be nil." + properties: + metadata: + description: May contain labels and annotations + that will be copied into the PVC when creating + it. No other fields are allowed and will be rejected + during validation. + type: object + spec: + description: The specification for the PersistentVolumeClaim. + The entire content is copied unchanged into the + PVC that gets created from this template. The + same fields as in a PersistentVolumeClaim are + also valid here. + properties: + accessModes: + description: 'accessModes contains the desired + access modes the volume should have. More + info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' + items: + type: string + type: array + dataSource: + description: 'dataSource field can be used to + specify either: * An existing VolumeSnapshot + object (snapshot.storage.k8s.io/VolumeSnapshot) + * An existing PVC (PersistentVolumeClaim) + If the provisioner or an external controller + can support the specified data source, it + will create a new volume based on the contents + of the specified data source. If the AnyVolumeDataSource + feature gate is enabled, this field will always + have the same contents as the DataSourceRef + field.' + properties: + apiGroup: + description: APIGroup is the group for the + resource being referenced. If APIGroup + is not specified, the specified Kind must + be in the core API group. For any other + third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource + being referenced + type: string + name: + description: Name is the name of resource + being referenced + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + description: 'dataSourceRef specifies the object + from which to populate the volume with data, + if a non-empty volume is desired. This may + be any local object from a non-empty API group + (non core object) or a PersistentVolumeClaim + object. When this field is specified, volume + binding will only succeed if the type of the + specified object matches some installed volume + populator or dynamic provisioner. This field + will replace the functionality of the DataSource + field and as such if both fields are non-empty, + they must have the same value. For backwards + compatibility, both fields (DataSource and + DataSourceRef) will be set to the same value + automatically if one of them is empty and + the other is non-empty. There are two important + differences between DataSource and DataSourceRef: + * While DataSource only allows two specific + types of objects, DataSourceRef allows any + non-core object, as well as PersistentVolumeClaim + objects. * While DataSource ignores disallowed + values (dropping them), DataSourceRef preserves + all values, and generates an error if a disallowed + value is specified. (Beta) Using this field + requires the AnyVolumeDataSource feature gate + to be enabled.' + properties: + apiGroup: + description: APIGroup is the group for the + resource being referenced. If APIGroup + is not specified, the specified Kind must + be in the core API group. For any other + third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource + being referenced + type: string + name: + description: Name is the name of resource + being referenced + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + resources: + description: 'resources represents the minimum + resources the volume should have. If RecoverVolumeExpansionFailure + feature is enabled users are allowed to specify + resource requirements that are lower than + previous value but must still be higher than + capacity recorded in the status field of the + claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum + amount of compute resources allowed. More + info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum + amount of compute resources required. + If Requests is omitted for a container, + it defaults to Limits if that is explicitly + specified, otherwise to an implementation-defined + value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + selector: + description: selector is a label query over + volumes to consider for binding. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + description: 'storageClassName is the name of + the StorageClass required by the claim. More + info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' + type: string + volumeMode: + description: volumeMode defines what type of + volume is required by the claim. Value of + Filesystem is implied when not included in + claim spec. + type: string + volumeName: + description: volumeName is the binding reference + to the PersistentVolume backing this claim. + type: string + type: object + required: + - spec + type: object + type: object + fc: + description: fc represents a Fibre Channel resource that + is attached to a kubelet's host machine and then exposed + to the pod. + properties: + fsType: + description: 'fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. TODO: how do we prevent + errors in the filesystem from compromising the machine' + type: string + lun: + description: 'lun is Optional: FC target lun number' + format: int32 + type: integer + readOnly: + description: 'readOnly is Optional: Defaults to false + (read/write). ReadOnly here will force the ReadOnly + setting in VolumeMounts.' + type: boolean + targetWWNs: + description: 'targetWWNs is Optional: FC target worldwide + names (WWNs)' + items: + type: string + type: array + wwids: + description: 'wwids Optional: FC volume world wide identifiers + (wwids) Either wwids or combination of targetWWNs + and lun must be set, but not both simultaneously.' + items: + type: string + type: array + type: object + flexVolume: + description: flexVolume represents a generic volume resource + that is provisioned/attached using an exec based plugin. + properties: + driver: + description: driver is the name of the driver to use + for this volume. + type: string + fsType: + description: fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". The default filesystem + depends on FlexVolume script. + type: string + options: + additionalProperties: + type: string + description: 'options is Optional: this field holds + extra command options if any.' + type: object + readOnly: + description: 'readOnly is Optional: defaults to false + (read/write). ReadOnly here will force the ReadOnly + setting in VolumeMounts.' + type: boolean + secretRef: + description: 'secretRef is Optional: secretRef is reference + to the secret object containing sensitive information + to pass to the plugin scripts. This may be empty if + no secret object is specified. If the secret object + contains more than one secret, all secrets are passed + to the plugin scripts.' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + description: flocker represents a Flocker volume attached + to a kubelet's host machine. This depends on the Flocker + control service being running + properties: + datasetName: + description: datasetName is Name of the dataset stored + as metadata -> name on the dataset for Flocker should + be considered as deprecated + type: string + datasetUUID: + description: datasetUUID is the UUID of the dataset. + This is unique identifier of a Flocker dataset + type: string + type: object + gcePersistentDisk: + description: 'gcePersistentDisk represents a GCE Disk resource + that is attached to a kubelet''s host machine and then + exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + properties: + fsType: + description: 'fsType is filesystem type of the volume + that you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + partition: + description: 'partition is the partition in the volume + that you want to mount. If omitted, the default is + to mount by volume name. Examples: For volume /dev/sda1, + you specify the partition as "1". Similarly, the volume + partition for /dev/sda is "0" (or you can leave the + property empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + format: int32 + type: integer + pdName: + description: 'pdName is unique name of the PD resource + in GCE. Used to identify the disk in GCE. More info: + https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: string + readOnly: + description: 'readOnly here will force the ReadOnly + setting in VolumeMounts. Defaults to false. More info: + https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: boolean + required: + - pdName + type: object + gitRepo: + description: 'gitRepo represents a git repository at a particular + revision. DEPRECATED: GitRepo is deprecated. To provision + a container with a git repo, mount an EmptyDir into an + InitContainer that clones the repo using git, then mount + the EmptyDir into the Pod''s container.' + properties: + directory: + description: directory is the target directory name. + Must not contain or start with '..'. If '.' is supplied, + the volume directory will be the git repository. Otherwise, + if specified, the volume will contain the git repository + in the subdirectory with the given name. + type: string + repository: + description: repository is the URL + type: string + revision: + description: revision is the commit hash for the specified + revision. + type: string + required: + - repository + type: object + glusterfs: + description: 'glusterfs represents a Glusterfs mount on + the host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md' + properties: + endpoints: + description: 'endpoints is the endpoint name that details + Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + path: + description: 'path is the Glusterfs volume path. More + info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + readOnly: + description: 'readOnly here will force the Glusterfs + volume to be mounted with read-only permissions. Defaults + to false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: boolean + required: + - endpoints + - path + type: object + hostPath: + description: 'hostPath represents a pre-existing file or + directory on the host machine that is directly exposed + to the container. This is generally used for system agents + or other privileged things that are allowed to see the + host machine. Most containers will NOT need this. More + info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + --- TODO(jonesdl) We need to restrict who can use host + directory mounts and who can/can not mount host directories + as read/write.' + properties: + path: + description: 'path of the directory on the host. If + the path is a symlink, it will follow the link to + the real path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + type: + description: 'type for HostPath Volume Defaults to "" + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + required: + - path + type: object + iscsi: + description: 'iscsi represents an ISCSI Disk resource that + is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + properties: + chapAuthDiscovery: + description: chapAuthDiscovery defines whether support + iSCSI Discovery CHAP authentication + type: boolean + chapAuthSession: + description: chapAuthSession defines whether support + iSCSI Session CHAP authentication + type: boolean + fsType: + description: 'fsType is the filesystem type of the volume + that you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + initiatorName: + description: initiatorName is the custom iSCSI Initiator + Name. If initiatorName is specified with iscsiInterface + simultaneously, new iSCSI interface : will be created for the connection. + type: string + iqn: + description: iqn is the target iSCSI Qualified Name. + type: string + iscsiInterface: + description: iscsiInterface is the interface Name that + uses an iSCSI transport. Defaults to 'default' (tcp). + type: string + lun: + description: lun represents iSCSI Target Lun number. + format: int32 + type: integer + portals: + description: portals is the iSCSI Target Portal List. + The portal is either an IP or ip_addr:port if the + port is other than default (typically TCP ports 860 + and 3260). + items: + type: string + type: array + readOnly: + description: readOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. + type: boolean + secretRef: + description: secretRef is the CHAP Secret for iSCSI + target and initiator authentication + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + description: targetPortal is iSCSI Target Portal. The + Portal is either an IP or ip_addr:port if the port + is other than default (typically TCP ports 860 and + 3260). + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + description: 'name of the volume. Must be a DNS_LABEL and + unique within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + nfs: + description: 'nfs represents an NFS mount on the host that + shares a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + properties: + path: + description: 'path that is exported by the NFS server. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + readOnly: + description: 'readOnly here will force the NFS export + to be mounted with read-only permissions. Defaults + to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: boolean + server: + description: 'server is the hostname or IP address of + the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + description: 'persistentVolumeClaimVolumeSource represents + a reference to a PersistentVolumeClaim in the same namespace. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + properties: + claimName: + description: 'claimName is the name of a PersistentVolumeClaim + in the same namespace as the pod using this volume. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: string + readOnly: + description: readOnly Will force the ReadOnly setting + in VolumeMounts. Default false. + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + description: photonPersistentDisk represents a PhotonController + persistent disk attached and mounted on kubelets host + machine + properties: + fsType: + description: fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. + type: string + pdID: + description: pdID is the ID that identifies Photon Controller + persistent disk + type: string + required: + - pdID + type: object + portworxVolume: + description: portworxVolume represents a portworx volume + attached and mounted on kubelets host machine + properties: + fsType: + description: fSType represents the filesystem type to + mount Must be a filesystem type supported by the host + operating system. Ex. "ext4", "xfs". Implicitly inferred + to be "ext4" if unspecified. + type: string + readOnly: + description: readOnly defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts. + type: boolean + volumeID: + description: volumeID uniquely identifies a Portworx + volume + type: string + required: + - volumeID + type: object + projected: + description: projected items for all in one resources secrets, + configmaps, and downward API + properties: + defaultMode: + description: defaultMode are the mode bits used to set + permissions on created files by default. Must be an + octal value between 0000 and 0777 or a decimal value + between 0 and 511. YAML accepts both octal and decimal + values, JSON requires decimal values for mode bits. + Directories within the path are not affected by this + setting. This might be in conflict with other options + that affect the file mode, like fsGroup, and the result + can be other mode bits set. + format: int32 + type: integer + sources: + description: sources is the list of volume projections + items: + description: Projection that may be projected along + with other supported volume types + properties: + configMap: + description: configMap information about the configMap + data to project + properties: + items: + description: items if unspecified, each key-value + pair in the Data field of the referenced + ConfigMap will be projected into the volume + as a file whose name is the key and content + is the value. If specified, the listed keys + will be projected into the specified paths, + and unlisted keys will not be present. If + a key is specified which is not present + in the ConfigMap, the volume setup will + error unless it is marked optional. Paths + must be relative and may not contain the + '..' path or start with '..'. + items: + description: Maps a string key to a path + within a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode + bits used to set permissions on this + file. Must be an octal value between + 0000 and 0777 or a decimal value between + 0 and 511. YAML accepts both octal + and decimal values, JSON requires + decimal values for mode bits. If not + specified, the volume defaultMode + will be used. This might be in conflict + with other options that affect the + file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + path: + description: path is the relative path + of the file to map the key to. May + not be an absolute path. May not contain + the path element '..'. May not start + with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: optional specify whether the + ConfigMap or its keys must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + description: downwardAPI information about the + downwardAPI data to project + properties: + items: + description: Items is a list of DownwardAPIVolume + file + items: + description: DownwardAPIVolumeFile represents + information to create the file containing + the pod field + properties: + fieldRef: + description: 'Required: Selects a field + of the pod: only annotations, labels, + name and namespace are supported.' + properties: + apiVersion: + description: Version of the schema + the FieldPath is written in terms + of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to + select in the specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + description: 'Optional: mode bits used + to set permissions on this file, must + be an octal value between 0000 and + 0777 or a decimal value between 0 + and 511. YAML accepts both octal and + decimal values, JSON requires decimal + values for mode bits. If not specified, + the volume defaultMode will be used. + This might be in conflict with other + options that affect the file mode, + like fsGroup, and the result can be + other mode bits set.' + format: int32 + type: integer + path: + description: 'Required: Path is the + relative path name of the file to + be created. Must not be absolute or + contain the ''..'' path. Must be utf-8 + encoded. The first item of the relative + path must not start with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of + the container: only resources limits + and requests (limits.cpu, limits.memory, + requests.cpu and requests.memory) + are currently supported.' + properties: + containerName: + description: 'Container name: required + for volumes, optional for env + vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output + format of the exposed resources, + defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource + to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + type: object + secret: + description: secret information about the secret + data to project + properties: + items: + description: items if unspecified, each key-value + pair in the Data field of the referenced + Secret will be projected into the volume + as a file whose name is the key and content + is the value. If specified, the listed keys + will be projected into the specified paths, + and unlisted keys will not be present. If + a key is specified which is not present + in the Secret, the volume setup will error + unless it is marked optional. Paths must + be relative and may not contain the '..' + path or start with '..'. + items: + description: Maps a string key to a path + within a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode + bits used to set permissions on this + file. Must be an octal value between + 0000 and 0777 or a decimal value between + 0 and 511. YAML accepts both octal + and decimal values, JSON requires + decimal values for mode bits. If not + specified, the volume defaultMode + will be used. This might be in conflict + with other options that affect the + file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + path: + description: path is the relative path + of the file to map the key to. May + not be an absolute path. May not contain + the path element '..'. May not start + with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: optional field specify whether + the Secret or its key must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + description: serviceAccountToken is information + about the serviceAccountToken data to project + properties: + audience: + description: audience is the intended audience + of the token. A recipient of a token must + identify itself with an identifier specified + in the audience of the token, and otherwise + should reject the token. The audience defaults + to the identifier of the apiserver. + type: string + expirationSeconds: + description: expirationSeconds is the requested + duration of validity of the service account + token. As the token approaches expiration, + the kubelet volume plugin will proactively + rotate the service account token. The kubelet + will start trying to rotate the token if + the token is older than 80 percent of its + time to live or if the token is older than + 24 hours.Defaults to 1 hour and must be + at least 10 minutes. + format: int64 + type: integer + path: + description: path is the path relative to + the mount point of the file to project the + token into. + type: string + required: + - path + type: object + type: object + type: array + type: object + quobyte: + description: quobyte represents a Quobyte mount on the host + that shares a pod's lifetime + properties: + group: + description: group to map volume access to Default is + no group + type: string + readOnly: + description: readOnly here will force the Quobyte volume + to be mounted with read-only permissions. Defaults + to false. + type: boolean + registry: + description: registry represents a single or multiple + Quobyte Registry services specified as a string as + host:port pair (multiple entries are separated with + commas) which acts as the central registry for volumes + type: string + tenant: + description: tenant owning the given Quobyte volume + in the Backend Used with dynamically provisioned Quobyte + volumes, value is set by the plugin + type: string + user: + description: user to map volume access to Defaults to + serivceaccount user + type: string + volume: + description: volume is a string that references an already + created Quobyte volume by name. + type: string + required: + - registry + - volume + type: object + rbd: + description: 'rbd represents a Rados Block Device mount + on the host that shares a pod''s lifetime. More info: + https://examples.k8s.io/volumes/rbd/README.md' + properties: + fsType: + description: 'fsType is the filesystem type of the volume + that you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + image: + description: 'image is the rados image name. More info: + https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + keyring: + description: 'keyring is the path to key ring for RBDUser. + Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + monitors: + description: 'monitors is a collection of Ceph monitors. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + items: + type: string + type: array + pool: + description: 'pool is the rados pool name. Default is + rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + readOnly: + description: 'readOnly here will force the ReadOnly + setting in VolumeMounts. Defaults to false. More info: + https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: boolean + secretRef: + description: 'secretRef is name of the authentication + secret for RBDUser. If provided overrides keyring. + Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + type: object + x-kubernetes-map-type: atomic + user: + description: 'user is the rados user name. Default is + admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + required: + - image + - monitors + type: object + scaleIO: + description: scaleIO represents a ScaleIO persistent volume + attached and mounted on Kubernetes nodes. + properties: + fsType: + description: fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Default is "xfs". + type: string + gateway: + description: gateway is the host address of the ScaleIO + API Gateway. + type: string + protectionDomain: + description: protectionDomain is the name of the ScaleIO + Protection Domain for the configured storage. + type: string + readOnly: + description: readOnly Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: secretRef references to the secret for + ScaleIO user and other sensitive information. If this + is not provided, Login operation will fail. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + description: sslEnabled Flag enable/disable SSL communication + with Gateway, default false + type: boolean + storageMode: + description: storageMode indicates whether the storage + for a volume should be ThickProvisioned or ThinProvisioned. + Default is ThinProvisioned. + type: string + storagePool: + description: storagePool is the ScaleIO Storage Pool + associated with the protection domain. + type: string + system: + description: system is the name of the storage system + as configured in ScaleIO. + type: string + volumeName: + description: volumeName is the name of a volume already + created in the ScaleIO system that is associated with + this volume source. + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + description: 'secret represents a secret that should populate + this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + properties: + defaultMode: + description: 'defaultMode is Optional: mode bits used + to set permissions on created files by default. Must + be an octal value between 0000 and 0777 or a decimal + value between 0 and 511. YAML accepts both octal and + decimal values, JSON requires decimal values for mode + bits. Defaults to 0644. Directories within the path + are not affected by this setting. This might be in + conflict with other options that affect the file mode, + like fsGroup, and the result can be other mode bits + set.' + format: int32 + type: integer + items: + description: items If unspecified, each key-value pair + in the Data field of the referenced Secret will be + projected into the volume as a file whose name is + the key and content is the value. If specified, the + listed keys will be projected into the specified paths, + and unlisted keys will not be present. If a key is + specified which is not present in the Secret, the + volume setup will error unless it is marked optional. + Paths must be relative and may not contain the '..' + path or start with '..'. + items: + description: Maps a string key to a path within a + volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits used + to set permissions on this file. Must be an + octal value between 0000 and 0777 or a decimal + value between 0 and 511. YAML accepts both octal + and decimal values, JSON requires decimal values + for mode bits. If not specified, the volume + defaultMode will be used. This might be in conflict + with other options that affect the file mode, + like fsGroup, and the result can be other mode + bits set.' + format: int32 + type: integer + path: + description: path is the relative path of the + file to map the key to. May not be an absolute + path. May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + optional: + description: optional field specify whether the Secret + or its keys must be defined + type: boolean + secretName: + description: 'secretName is the name of the secret in + the pod''s namespace to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + type: object + storageos: + description: storageOS represents a StorageOS volume attached + and mounted on Kubernetes nodes. + properties: + fsType: + description: fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. + type: string + readOnly: + description: readOnly defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: secretRef specifies the secret to use for + obtaining the StorageOS API credentials. If not specified, + default values will be attempted. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + description: volumeName is the human-readable name of + the StorageOS volume. Volume names are only unique + within a namespace. + type: string + volumeNamespace: + description: volumeNamespace specifies the scope of + the volume within StorageOS. If no namespace is specified + then the Pod's namespace will be used. This allows + the Kubernetes name scoping to be mirrored within + StorageOS for tighter integration. Set VolumeName + to any name to override the default behaviour. Set + to "default" if you are not using namespaces within + StorageOS. Namespaces that do not pre-exist within + StorageOS will be created. + type: string + type: object + vsphereVolume: + description: vsphereVolume represents a vSphere volume attached + and mounted on kubelets host machine + properties: + fsType: + description: fsType is filesystem type to mount. Must + be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. + type: string + storagePolicyID: + description: storagePolicyID is the storage Policy Based + Management (SPBM) profile ID associated with the StoragePolicyName. + type: string + storagePolicyName: + description: storagePolicyName is the storage Policy + Based Management (SPBM) profile name. + type: string + volumePath: + description: volumePath is the path that identifies + vSphere volume vmdk + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array type: object optimizeKubeSourceConfig: description: Enable kubernetes source config optimization diff --git a/helm/charts/vector-operator/templates/deployment.yaml b/helm/charts/vector-operator/templates/deployment.yaml index bfec2e76..4f42017a 100644 --- a/helm/charts/vector-operator/templates/deployment.yaml +++ b/helm/charts/vector-operator/templates/deployment.yaml @@ -34,7 +34,7 @@ spec: tolerations: {{- toYaml . | nindent 8 }} {{- end }} - serviceAccountName: {{ include "chart.serviceAccountName" . }} + serviceAccountName: {{ include "chart.serviceAccountName" . }} securityContext: {{- toYaml .Values.podSecurityContext | nindent 8 }} containers: @@ -49,5 +49,3 @@ spec: name: vector-operator resources: {{ toYaml .Values.resources | indent 12 }} - - \ No newline at end of file From fd0c761bd4f916253f81f3ea748a7b6f7fc4f866 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Thu, 27 Apr 2023 14:20:24 +0300 Subject: [PATCH 202/316] release v0.0.21 --- CHANGELOG.md | 3 ++ helm/charts/vector-operator/Chart.yaml | 4 +- helm/index.yaml | 47 +++++++++++++++-------- helm/packages/vector-operator-0.0.21.tgz | Bin 0 -> 30239 bytes 4 files changed, 35 insertions(+), 19 deletions(-) create mode 100644 helm/packages/vector-operator-0.0.21.tgz diff --git a/CHANGELOG.md b/CHANGELOG.md index 58b98ffa..4e425e92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +### v0.0.21 +- [[102](https://github.com/kaasops/vector-operator/pull/102] **Fix** add containerSecurityContext, volumes, volumeMounts to Vector resource + ### v0.0.20 - [[100](https://github.com/kaasops/vector-operator/pull/100] **Fix** Create csv and fix rbac auto generation diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index 3061c742..c0ad639b 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.20 +version: 0.0.21 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.0.20" +appVersion: "v0.0.21" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/index.yaml b/helm/index.yaml index 4cabb374..b14afe62 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.0.21 + created: "2023-04-27T14:19:48.238300233+03:00" + description: A Helm chart to install Vector Operator + digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.21.tgz + version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2023-04-20T17:02:02.501524249+03:00" + created: "2023-04-27T14:19:48.237355972+03:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2023-04-20T17:02:02.500938496+03:00" + created: "2023-04-27T14:19:48.236656803+03:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2023-04-20T17:02:02.500242991+03:00" + created: "2023-04-27T14:19:48.235977189+03:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2023-04-20T17:02:02.499500281+03:00" + created: "2023-04-27T14:19:48.235161077+03:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2023-04-20T17:02:02.498661254+03:00" + created: "2023-04-27T14:19:48.234108956+03:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2023-04-20T17:02:02.497449503+03:00" + created: "2023-04-27T14:19:48.233365261+03:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2023-04-20T17:02:02.496703243+03:00" + created: "2023-04-27T14:19:48.232428412+03:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2023-04-20T17:02:02.495983354+03:00" + created: "2023-04-27T14:19:48.23173139+03:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2023-04-20T17:02:02.495277839+03:00" + created: "2023-04-27T14:19:48.230995034+03:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2023-04-20T17:02:02.494569368+03:00" + created: "2023-04-27T14:19:48.22901432+03:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2023-04-20T17:02:02.49396054+03:00" + created: "2023-04-27T14:19:48.228369309+03:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -146,7 +159,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2023-04-20T17:02:02.504385655+03:00" + created: "2023-04-27T14:19:48.241207095+03:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -159,7 +172,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2023-04-20T17:02:02.503795536+03:00" + created: "2023-04-27T14:19:48.240602218+03:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -172,7 +185,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2023-04-20T17:02:02.503207677+03:00" + created: "2023-04-27T14:19:48.240005358+03:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -185,7 +198,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2023-04-20T17:02:02.502200081+03:00" + created: "2023-04-27T14:19:48.238882223+03:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -198,7 +211,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2023-04-20T17:02:02.493250453+03:00" + created: "2023-04-27T14:19:48.227691593+03:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -209,4 +222,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2023-04-20T17:02:02.492281093+03:00" +generated: "2023-04-27T14:19:48.220815939+03:00" diff --git a/helm/packages/vector-operator-0.0.21.tgz b/helm/packages/vector-operator-0.0.21.tgz new file mode 100644 index 0000000000000000000000000000000000000000..83553df300963174bd307efe30fea200a819b886 GIT binary patch literal 30239 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 literal 0 HcmV?d00001 From 083fa2519603cb8768a2470ea1e0b0c52cb2418d Mon Sep 17 00:00:00 2001 From: "Dmitry K. Anisimov" Date: Sat, 29 Apr 2023 20:34:59 +0300 Subject: [PATCH 203/316] add secrets, openshift support to helm, refactor operator deployment --- .../templates/clustervectorpipeline.yaml | 9 +- .../vector-operator/templates/deployment.yaml | 8 +- .../vector-operator/templates/openshift.yaml | 84 +++++++++++++++++++ .../vector-operator/templates/secrets.yaml | 22 +++++ helm/charts/vector-operator/values.yaml | 29 ++++--- 5 files changed, 134 insertions(+), 18 deletions(-) create mode 100644 helm/charts/vector-operator/templates/openshift.yaml create mode 100644 helm/charts/vector-operator/templates/secrets.yaml diff --git a/helm/charts/vector-operator/templates/clustervectorpipeline.yaml b/helm/charts/vector-operator/templates/clustervectorpipeline.yaml index 343547a2..d4fc7e17 100644 --- a/helm/charts/vector-operator/templates/clustervectorpipeline.yaml +++ b/helm/charts/vector-operator/templates/clustervectorpipeline.yaml @@ -1,17 +1,14 @@ {{- range $pipeline := .Values.clustervectorpipeline }} apiVersion: observability.kaasops.io/v1alpha1 kind: ClusterVectorPipeline -metadata: +metadata: name: {{ $pipeline.name }} spec: - sources: + sources: {{- toYaml $pipeline.sources | nindent 4 }} - transforms: + transforms: {{- toYaml $pipeline.transforms | nindent 4 }} sinks: {{- toYaml $pipeline.sinks | nindent 4 }} --- {{- end }} - - - diff --git a/helm/charts/vector-operator/templates/deployment.yaml b/helm/charts/vector-operator/templates/deployment.yaml index 4f42017a..bce2b97f 100644 --- a/helm/charts/vector-operator/templates/deployment.yaml +++ b/helm/charts/vector-operator/templates/deployment.yaml @@ -35,16 +35,20 @@ spec: {{- toYaml . | nindent 8 }} {{- end }} serviceAccountName: {{ include "chart.serviceAccountName" . }} + {{- with .Values.podSecurityContext }} securityContext: - {{- toYaml .Values.podSecurityContext | nindent 8 }} + {{- toYaml . | nindent 8 }} + {{- end }} containers: - image: {{ .Values.image.repository }}:{{ default .Chart.AppVersion .Values.image.tag }} {{- with .Values.args }} args: {{- toYaml . | nindent 10 }} {{- end }} + {{- with .Values.securityContext }} securityContext: - {{- toYaml .Values.securityContext | nindent 10 }} + {{- toYaml . | nindent 10 }} + {{- end }} imagePullPolicy: {{ .Values.image.pullPolicy }} name: vector-operator resources: diff --git a/helm/charts/vector-operator/templates/openshift.yaml b/helm/charts/vector-operator/templates/openshift.yaml new file mode 100644 index 00000000..c041a603 --- /dev/null +++ b/helm/charts/vector-operator/templates/openshift.yaml @@ -0,0 +1,84 @@ +{{- if and .Values.openshift.enable .Values.vector.enable -}} +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ template "chart.fullname" . }}-ocp-scc-role + namespace: {{ .Release.Namespace }} + labels: + {{- include "chart.labels" . | nindent 4 }} +rules: + - verbs: + - use + apiGroups: + - security.openshift.io + resources: + - securitycontextconstraints + resourceNames: + - {{ template "chart.fullname" . }}-ocp-scc +--- +apiVersion: security.openshift.io/v1 +kind: SecurityContextConstraints +metadata: + name: {{ template "chart.fullname" . }}-ocp-scc + labels: + {{- include "chart.labels" . | nindent 4 }} +allowHostPorts: false +priority: null +requiredDropCapabilities: + - CHOWN + - DAC_OVERRIDE + - FSETID + - FOWNER + - SETGID + - SETUID + - SETPCAP + - NET_BIND_SERVICE + - KILL +allowPrivilegedContainer: false +runAsUser: + type: RunAsAny +users: [] +allowHostDirVolumePlugin: true +seccompProfiles: + - runtime/default +allowHostIPC: false +forbiddenSysctls: + - '*' +seLinuxContext: + type: RunAsAny +readOnlyRootFilesystem: true +fsGroup: + type: RunAsAny +groups: [] +defaultAddCapabilities: null +supplementalGroups: + type: RunAsAny +volumes: + - configMap + - emptyDir + - hostPath + - projected + - secret +allowHostPID: false +allowHostNetwork: false +allowPrivilegeEscalation: false +allowedCapabilities: null +defaultAllowPrivilegeEscalation: false +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ template "chart.fullname" . }}-ocp-scc-bindig + namespace: {{ .Release.Namespace }} + labels: + {{- include "chart.labels" . | nindent 4 }} +subjects: + - kind: ServiceAccount + name: {{ .Values.vector.name }}-agent + - kind: ServiceAccount + name: {{ .Values.vector.name }}-configcheck +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "chart.fullname" . }}-ocp-scc-role +{{- end -}} diff --git a/helm/charts/vector-operator/templates/secrets.yaml b/helm/charts/vector-operator/templates/secrets.yaml new file mode 100644 index 00000000..ac5861c9 --- /dev/null +++ b/helm/charts/vector-operator/templates/secrets.yaml @@ -0,0 +1,22 @@ +{{- $outer := . -}} +{{- range $secret := .Values.secrets }} +{{- with $outer -}} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ $secret.name }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "chart.labels" . | nindent 4 }} +type: Opaque +data: +{{- range $k, $v := $secret.data }} + {{- if kindIs "string" $v }} + {{ $k }}: {{ $v | b64enc }} + {{- else }} + {{ $k }}: {{ $v | toJson | trim | b64enc }} + {{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/helm/charts/vector-operator/values.yaml b/helm/charts/vector-operator/values.yaml index 192dd01d..7a77202e 100644 --- a/helm/charts/vector-operator/values.yaml +++ b/helm/charts/vector-operator/values.yaml @@ -21,16 +21,16 @@ strategy: {} imagePullSecrets: [] -securityContext: - allowPrivilegeEscalation: false - runAsGroup: 1000 - runAsNonRoot: true - readOnlyRootFilesystem: true - seccompProfile: - type: RuntimeDefault - capabilities: - drop: - - ALL +securityContext: {} + # allowPrivilegeEscalation: false + # runAsGroup: 1000 + # runAsNonRoot: true + # readOnlyRootFilesystem: true + # seccompProfile: + # type: RuntimeDefault + # capabilities: + # drop: + # - ALL podSecurityContext: {} @@ -79,6 +79,12 @@ vector: # - name: "testenv" # value: "testvalues" +secrets: {} +# - name: elastic-creds +# data: +# username: "logstash" +# password: "password" + clustervectorpipeline: {} # - name: "" # sources: {} @@ -88,3 +94,6 @@ clustervectorpipeline: {} # sources: {} # transforms: {} # sinks: {} + +openshift: + enabled: false From 235090cca029586c164b6f57faeb5c49dceebf98 Mon Sep 17 00:00:00 2001 From: zvlb Date: Wed, 3 May 2023 11:26:00 +0300 Subject: [PATCH 204/316] Prepare for v0.0.22 release Signed-off-by: zvlb --- CHANGELOG.md | 16 ++++---- helm/charts/vector-operator/Chart.yaml | 4 +- helm/index.yaml | 49 ++++++++++++++--------- helm/packages/vector-operator-0.0.22.tgz | Bin 0 -> 30911 bytes 4 files changed, 42 insertions(+), 27 deletions(-) create mode 100644 helm/packages/vector-operator-0.0.22.tgz diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e425e92..b13de15f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,23 +1,25 @@ +### v0.0.22 + ### v0.0.21 -- [[102](https://github.com/kaasops/vector-operator/pull/102] **Fix** add containerSecurityContext, volumes, volumeMounts to Vector resource +- [[104](https://github.com/kaasops/vector-operator/pull/104)] **Feature** Prepare helm for Openshift ### v0.0.20 -- [[100](https://github.com/kaasops/vector-operator/pull/100] **Fix** Create csv and fix rbac auto generation +- [[100](https://github.com/kaasops/vector-operator/pull/100)] **Fix** Create csv and fix rbac auto generation ### v0.0.19 -- [[97](https://github.com/kaasops/vector-operator/pull/97] **Fix** Check if ServiceMonitor CRD exists +- [[97](https://github.com/kaasops/vector-operator/pull/97)] **Fix** Check if ServiceMonitor CRD exists ### v0.0.18 -- [[95](https://github.com/kaasops/vector-operator/pull/95] **Fix** Added ImagePullPolicy to Vector Agent +- [[95](https://github.com/kaasops/vector-operator/pull/95)] **Fix** Added ImagePullPolicy to Vector Agent ### v0.0.17 -- [[93](https://github.com/kaasops/vector-operator/pull/93] **Fix** Fix Condition type +- [[93](https://github.com/kaasops/vector-operator/pull/93)] **Fix** Fix Condition type ### v0.0.16 -- [[92](https://github.com/kaasops/vector-operator/pull/92] **Fix** Added rbac for ServiceMonitros +- [[92](https://github.com/kaasops/vector-operator/pull/92)] **Fix** Added rbac for ServiceMonitros ### v0.0.15 -- [[89](https://github.com/kaasops/vector-operator/pull/89] **Feature** Added experemental config optimization option +- [[89](https://github.com/kaasops/vector-operator/pull/89)] **Feature** Added experemental config optimization option ### v0.0.14 - [[85](https://github.com/kaasops/vector-operator/pull/85)] **Feature** Add metrics exporter and ServiceMonitor creation diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index c0ad639b..2e813e5b 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.21 +version: 0.0.22 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.0.21" +appVersion: "v0.0.22" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/index.yaml b/helm/index.yaml index b14afe62..e6907301 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.0.22 + created: "2023-05-03T11:25:23.847076+03:00" + description: A Helm chart to install Vector Operator + digest: bf96ddc8ac61e9d6beb8bc763fbf3fa6025d950b29d70d80de6e8a0ea45e0411 + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.22.tgz + version: 0.0.22 - apiVersion: v2 appVersion: v0.0.21 - created: "2023-04-27T14:19:48.238300233+03:00" + created: "2023-05-03T11:25:23.845923+03:00" description: A Helm chart to install Vector Operator digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2023-04-27T14:19:48.237355972+03:00" + created: "2023-05-03T11:25:23.844949+03:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2023-04-27T14:19:48.236656803+03:00" + created: "2023-05-03T11:25:23.843095+03:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2023-04-27T14:19:48.235977189+03:00" + created: "2023-05-03T11:25:23.842456+03:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2023-04-27T14:19:48.235161077+03:00" + created: "2023-05-03T11:25:23.840801+03:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2023-04-27T14:19:48.234108956+03:00" + created: "2023-05-03T11:25:23.839337+03:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2023-04-27T14:19:48.233365261+03:00" + created: "2023-05-03T11:25:23.838482+03:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2023-04-27T14:19:48.232428412+03:00" + created: "2023-05-03T11:25:23.836161+03:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2023-04-27T14:19:48.23173139+03:00" + created: "2023-05-03T11:25:23.82884+03:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2023-04-27T14:19:48.230995034+03:00" + created: "2023-05-03T11:25:23.828098+03:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2023-04-27T14:19:48.22901432+03:00" + created: "2023-05-03T11:25:23.827349+03:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -146,7 +159,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2023-04-27T14:19:48.228369309+03:00" + created: "2023-05-03T11:25:23.826724+03:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -159,7 +172,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2023-04-27T14:19:48.241207095+03:00" + created: "2023-05-03T11:25:23.84978+03:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -172,7 +185,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2023-04-27T14:19:48.240602218+03:00" + created: "2023-05-03T11:25:23.849186+03:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -185,7 +198,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2023-04-27T14:19:48.240005358+03:00" + created: "2023-05-03T11:25:23.848123+03:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -198,7 +211,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2023-04-27T14:19:48.238882223+03:00" + created: "2023-05-03T11:25:23.847608+03:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -211,7 +224,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2023-04-27T14:19:48.227691593+03:00" + created: "2023-05-03T11:25:23.826111+03:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -222,4 +235,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2023-04-27T14:19:48.220815939+03:00" +generated: "2023-05-03T11:25:23.825093+03:00" diff --git a/helm/packages/vector-operator-0.0.22.tgz b/helm/packages/vector-operator-0.0.22.tgz new file mode 100644 index 0000000000000000000000000000000000000000..78718c546604c0e90fc35dec08554bfb172e0eb7 GIT binary patch literal 30911 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 V`}R?_{{{d6|NnZ*I@18C1OO)e-2DIm literal 0 HcmV?d00001 From d5a7c302ae417e354ed4a5c69483ab90daa7dbc1 Mon Sep 17 00:00:00 2001 From: zvlb Date: Thu, 4 May 2023 14:16:16 +0300 Subject: [PATCH 205/316] Add logo and telegram link Signed-off-by: zvlb --- README.md | 10 ++++++++-- docs/images/logo.png | Bin 0 -> 248062 bytes docs/images/telegram-logo.png | Bin 0 -> 32972 bytes 3 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 docs/images/logo.png create mode 100644 docs/images/telegram-logo.png diff --git a/README.md b/README.md index f1b13d6f..7b9088e1 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,16 @@ +

+

+

- - +telegra # Vector Operator ## Description diff --git a/docs/images/logo.png b/docs/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..b14178a6ff0c277a7462f2ceffb4801d38e1c8c9 GIT binary patch literal 248062 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
@0i0%a#YS6-gCIQQvWhF}b-ie81gD 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 literal 0 HcmV?d00001 diff --git a/docs/images/telegram-logo.png b/docs/images/telegram-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..c50c40f8aa58db68e0ccb97a87151e30364f6a77 GIT binary patch literal 32972 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 P0i9ibB?gZr literal 0 HcmV?d00001 From db1e573a3ffeb6eeb2b38ee3dd8bb0f277fc2ad4 Mon Sep 17 00:00:00 2001 From: zvlb Date: Thu, 4 May 2023 14:18:26 +0300 Subject: [PATCH 206/316] fux syntax Signed-off-by: zvlb --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7b9088e1..feaa8db3 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ telegram link

-telegra + # Vector Operator ## Description From d050fc7db8923b57f7b3daac9d9e31cb6831927e Mon Sep 17 00:00:00 2001 From: zvlb Date: Thu, 4 May 2023 15:28:14 +0300 Subject: [PATCH 207/316] Update telegram link Signed-off-by: zvlb --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index feaa8db3..ae8059ef 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Go Report Card - + telegram link

From ffcd1fdd457eedaf1202ae91b8e12ca2c415c913 Mon Sep 17 00:00:00 2001 From: zvlb Date: Thu, 4 May 2023 16:43:19 +0300 Subject: [PATCH 208/316] Update ReadMe Signed-off-by: zvlb --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index ae8059ef..a12f8b30 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,8 @@ ## Description The operator deploys and configures a vector agent daemonset on every node to collect container and application logs from the node file system. +Connect us in Telegram - https://t.me/+Y0PzGa1d5DFiYThi + ## Features - [x] Building vector config from namespaced custom resources (kind: VectorPipeline) From 03e8318b785efaed80db995b30a2b21e2d2b73a3 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Fri, 12 May 2023 16:06:14 +0300 Subject: [PATCH 209/316] compress config poc --- api/v1alpha1/vector_types.go | 4 + api/v1alpha1/zz_generated.deepcopy.go | 1 + .../observability.kaasops.io_vectors.yaml | 32 ++++ .../observability_v1alpha1_vector.yaml | 5 +- .../factory/config/configcheck/configcheck.go | 126 +++++++++------- .../config/configcheck/configcheck_config.go | 14 +- .../config/configcheck/configcheck_pod.go | 69 ++++++++- .../factory/utils/compression/compression.go | 22 +++ controllers/factory/utils/k8s/k8s.go | 9 ++ .../vector/vectoragent/vectoragent_config.go | 11 +- .../vectoragent/vectoragent_daemonset.go | 141 ++++++++++++++---- .../vector/vectoragent/vectoragent_default.go | 15 ++ 12 files changed, 353 insertions(+), 96 deletions(-) create mode 100644 controllers/factory/utils/compression/compression.go diff --git a/api/v1alpha1/vector_types.go b/api/v1alpha1/vector_types.go index 44371aff..dd547363 100644 --- a/api/v1alpha1/vector_types.go +++ b/api/v1alpha1/vector_types.go @@ -119,6 +119,10 @@ type VectorAgent struct { VolumeMounts []v1.VolumeMount `json:"volumeMounts,omitempty"` ConfigCheck ConfigCheck `json:"configCheck,omitempty"` + // Compress config file + CompressConfigFile bool `json:"compressConfigFile,omitempty"` + ConfigReloaderImage string `json:"configReloaderImage,omitempty"` + ConfigReloaderResources v1.ResourceRequirements `json:"configReloaderResources,omitempty"` } // ApiSpec is the Schema for the Vector Agent GraphQL API - https://vector.dev/docs/reference/api/ diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 15fd8c20..9567cfcc 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -234,6 +234,7 @@ func (in *VectorAgent) DeepCopyInto(out *VectorAgent) { } } in.ConfigCheck.DeepCopyInto(&out.ConfigCheck) + in.ConfigReloaderResources.DeepCopyInto(&out.ConfigReloaderResources) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VectorAgent. diff --git a/config/crd/bases/observability.kaasops.io_vectors.yaml b/config/crd/bases/observability.kaasops.io_vectors.yaml index 6677c272..9ff19ade 100644 --- a/config/crd/bases/observability.kaasops.io_vectors.yaml +++ b/config/crd/bases/observability.kaasops.io_vectors.yaml @@ -923,6 +923,9 @@ spec: playground: type: boolean type: object + compressConfigFile: + description: Compress config file + type: boolean configCheck: description: ConfigCheck is the Schema for control params for ConfigCheck pods @@ -1905,6 +1908,35 @@ spec: type: object type: array type: object + configReloaderImage: + type: string + configReloaderResources: + description: ResourceRequirements describes the compute resource + requirements. + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, otherwise + to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object containerSecurityContext: description: SecurityContext holds security configuration that will be applied to a container. Some fields are present in both diff --git a/config/samples/observability_v1alpha1_vector.yaml b/config/samples/observability_v1alpha1_vector.yaml index 476e2538..fb88d6f5 100644 --- a/config/samples/observability_v1alpha1_vector.yaml +++ b/config/samples/observability_v1alpha1_vector.yaml @@ -6,7 +6,8 @@ metadata: spec: optimizeKubeSourceConfig: true agent: - image: "timberio/vector:0.28.1-distroless-libc" - internalMetrics: true + image: "timberio/vector:0.28.1-debian" + internalMetrics: false api: enabled: false + compressConfigFile: true diff --git a/controllers/factory/config/configcheck/configcheck.go b/controllers/factory/config/configcheck/configcheck.go index 749b0cf5..5cd9425b 100644 --- a/controllers/factory/config/configcheck/configcheck.go +++ b/controllers/factory/config/configcheck/configcheck.go @@ -23,7 +23,6 @@ import ( "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" corev1 "k8s.io/api/core/v1" - api_errors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/labels" @@ -31,6 +30,7 @@ import ( "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/kubernetes" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/log" vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" @@ -44,14 +44,18 @@ type ConfigCheck struct { Client client.Client ClientSet *kubernetes.Clientset - Name string - Namespace string - Initiator string - Image string - Envs []corev1.EnvVar - Hash string - Tolerations []corev1.Toleration - Resources corev1.ResourceRequirements + Name string + Namespace string + Initiator string + Image string + ImagePullPolicy corev1.PullPolicy + Envs []corev1.EnvVar + Hash string + Tolerations []corev1.Toleration + Resources corev1.ResourceRequirements + CompressedConfig bool + ConfigReloaderImage string + ConfigReloaderResources corev1.ResourceRequirements } func New( @@ -78,21 +82,25 @@ func New( } return &ConfigCheck{ - Config: config, - Client: c, - ClientSet: cs, - Name: va.Name, - Namespace: va.Namespace, - Image: image, - Envs: env, - Tolerations: tolerations, - Resources: resources, + Config: config, + Client: c, + ClientSet: cs, + Name: va.Name, + Namespace: va.Namespace, + Image: image, + ImagePullPolicy: va.Spec.Agent.ImagePullPolicy, + Envs: env, + Tolerations: tolerations, + Resources: resources, + CompressedConfig: va.Spec.Agent.CompressConfigFile, + ConfigReloaderImage: va.Spec.Agent.ConfigReloaderImage, + ConfigReloaderResources: va.Spec.Agent.ConfigReloaderResources, } } func (cc *ConfigCheck) Run(ctx context.Context) (string, error) { log := log.FromContext(ctx).WithValues("Vector ConfigCheck", cc.Initiator) - log.Info("================= Started ConfigCheck =======================") + log.Info("================= Started ConfigCheck =================") if err := cc.ensureVectorConfigCheckRBAC(ctx); err != nil { return "", err @@ -100,20 +108,30 @@ func (cc *ConfigCheck) Run(ctx context.Context) (string, error) { cc.Hash = randStringRunes() - if err := cc.ensureVectorConfigCheckConfig(ctx); err != nil { + vectorConfigCheckSecret, err := cc.createVectorConfigCheckConfig(ctx) + if err != nil { return "", err } vectorConfigCheckPod := cc.createVectorConfigCheckPod() - err := k8s.CreatePod(ctx, vectorConfigCheckPod, cc.Client) - if err != nil { + defer func() { + err = cc.cleanup(ctx, vectorConfigCheckSecret) + }() + + if err = k8s.CreateOrUpdateResource(ctx, vectorConfigCheckSecret, cc.Client); err != nil { return "", err } - defer func() { - err = cc.cleanup(ctx, vectorConfigCheckPod) - }() + // Set OwnerReference to pod + if err = controllerutil.SetOwnerReference(vectorConfigCheckSecret, vectorConfigCheckPod, cc.Client.Scheme()); err != nil { + return "", err + } + + err = k8s.CreatePod(ctx, vectorConfigCheckPod, cc.Client) + if err != nil { + return "", err + } reason, err := cc.getCheckResult(ctx, vectorConfigCheckPod) if err != nil { @@ -132,18 +150,6 @@ func (cc *ConfigCheck) ensureVectorConfigCheckServiceAccount(ctx context.Context return k8s.CreateOrUpdateResource(ctx, vectorAgentServiceAccount, cc.Client) } -func (cc *ConfigCheck) ensureVectorConfigCheckConfig(ctx context.Context) error { - vectorConfigCheckSecret, err := cc.createVectorConfigCheckConfig() - if err != nil { - return err - } - - return k8s.CreateOrUpdateResource(ctx, vectorConfigCheckSecret, cc.Client) -} - -func (cc *ConfigCheck) checkVectorConfigCheckPod(ctx context.Context) error { - return nil -} func labelsForVectorConfigCheck() map[string]string { return map[string]string{ @@ -223,36 +229,40 @@ func (cc *ConfigCheck) getCheckResult(ctx context.Context, pod *corev1.Pod) (rea } } -func (cc *ConfigCheck) cleanup(ctx context.Context, pod *corev1.Pod) error { - pod, err := k8s.FetchPod(ctx, pod, cc.Client) +func (cc *ConfigCheck) cleanup(ctx context.Context, secret *corev1.Secret) error { + + nn := types.NamespacedName{ + Name: secret.Name, + Namespace: secret.Namespace, + } + secret, err := k8s.GetSecret(ctx, nn, cc.Client) if err != nil { - if api_errors.IsNotFound(err) { - return nil - } return err } - for _, v := range pod.Spec.Volumes { - if v.Name == "config" { - nn := types.NamespacedName{ - Name: v.Secret.SecretName, - Namespace: pod.Namespace, - } - secret, err := k8s.GetSecret(ctx, nn, cc.Client) - if err != nil { - return err - } - if err := k8s.DeleteSecret(ctx, secret, cc.Client); err != nil { - return err - } - } + if err := k8s.DeleteSecret(ctx, secret, cc.Client); err != nil { + return err } - if err := k8s.DeletePod(ctx, pod, cc.Client); err != nil { + return nil +} + +func (cc *ConfigCheck) CleanAll(ctx context.Context) error { + listOpts, err := cc.configCheckListOpts() + if err != nil { return err } + secrets, err := k8s.ListSecret(ctx, cc.Client, listOpts) + if err != nil { + return err + } + for _, secret := range secrets { + if err := k8s.DeleteSecret(ctx, &secret, cc.Client); err != nil { + return err + } + } return nil } -func (cc *ConfigCheck) gcRListOptions() (client.ListOptions, error) { +func (cc *ConfigCheck) configCheckListOpts() (client.ListOptions, error) { configCheckLabels := labelsForVectorConfigCheck() var requirements []labels.Requirement for k, v := range configCheckLabels { diff --git a/controllers/factory/config/configcheck/configcheck_config.go b/controllers/factory/config/configcheck/configcheck_config.go index d7a0caa8..5f6d858a 100644 --- a/controllers/factory/config/configcheck/configcheck_config.go +++ b/controllers/factory/config/configcheck/configcheck_config.go @@ -17,15 +17,25 @@ limitations under the License. package configcheck import ( + "context" + + "github.com/kaasops/vector-operator/controllers/factory/utils/compression" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/log" ) -func (cc *ConfigCheck) createVectorConfigCheckConfig() (*corev1.Secret, error) { +func (cc *ConfigCheck) createVectorConfigCheckConfig(ctx context.Context) (*corev1.Secret, error) { + log := log.FromContext(ctx).WithValues("Vector ConfigCheck", cc.Initiator) labels := labelsForVectorConfigCheck() + var data []byte = cc.Config + + if cc.CompressedConfig { + data = compression.Compress(cc.Config, log) + } config := map[string][]byte{ - "agent.json": cc.Config, + "agent.json": data, } secret := &corev1.Secret{ diff --git a/controllers/factory/config/configcheck/configcheck_pod.go b/controllers/factory/config/configcheck/configcheck_pod.go index df63eb71..87df96cf 100644 --- a/controllers/factory/config/configcheck/configcheck_pod.go +++ b/controllers/factory/config/configcheck/configcheck_pod.go @@ -23,6 +23,11 @@ import ( func (cc *ConfigCheck) createVectorConfigCheckPod() *corev1.Pod { labels := labelsForVectorConfigCheck() + var initContainers []corev1.Container + + if cc.CompressedConfig { + initContainers = append(initContainers, *cc.ConfigReloaderInitContainer()) + } pod := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ @@ -35,6 +40,7 @@ func (cc *ConfigCheck) createVectorConfigCheckPod() *corev1.Pod { Volumes: cc.generateVectorConfigCheckVolume(), SecurityContext: &corev1.PodSecurityContext{}, Tolerations: cc.Tolerations, + InitContainers: initContainers, Containers: []corev1.Container{ { Name: "config-check", @@ -60,14 +66,21 @@ func (cc *ConfigCheck) createVectorConfigCheckPod() *corev1.Pod { } func (cc *ConfigCheck) generateVectorConfigCheckVolume() []corev1.Volume { + configVolumeSource := corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: cc.getNameVectorConfigCheck(), + }, + } + if cc.CompressedConfig { + configVolumeSource = corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + } + + } volume := []corev1.Volume{ { - Name: "config", - VolumeSource: corev1.VolumeSource{ - Secret: &corev1.SecretVolumeSource{ - SecretName: cc.getNameVectorConfigCheck(), - }, - }, + Name: "config", + VolumeSource: configVolumeSource, }, { Name: "data", @@ -109,6 +122,17 @@ func (cc *ConfigCheck) generateVectorConfigCheckVolume() []corev1.Volume { }, } + if cc.CompressedConfig { + volume = append(volume, corev1.Volume{ + Name: "app-config-compress", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: cc.getNameVectorConfigCheck(), + }, + }, + }) + } + return volume } @@ -140,6 +164,15 @@ func (cc *ConfigCheck) generateVectorConfigCheckVolumeMounts() []corev1.VolumeMo }, } + if cc.CompressedConfig { + volumeMount = append(volumeMount, []corev1.VolumeMount{ + { + Name: "app-config-compress", + MountPath: "/tmp/archive", + }, + }...) + } + return volumeMount } @@ -186,3 +219,27 @@ func (cc *ConfigCheck) generateVectorConfigCheckEnvs() []corev1.EnvVar { return envs } + +func (cc *ConfigCheck) ConfigReloaderInitContainer() *corev1.Container { + return &corev1.Container{ + Name: "init-config-reloader", + Image: cc.ConfigReloaderImage, + ImagePullPolicy: cc.ImagePullPolicy, + Resources: cc.ConfigReloaderResources, + Args: []string{ + "--init-mode=true", + "--volume-dir-archive=/tmp/archive", + "--dir-for-unarchive=/etc/vector", + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "config", + MountPath: "/etc/vector", + }, + { + Name: "app-config-compress", + MountPath: "/tmp/archive", + }, + }, + } +} diff --git a/controllers/factory/utils/compression/compression.go b/controllers/factory/utils/compression/compression.go new file mode 100644 index 00000000..cae4b025 --- /dev/null +++ b/controllers/factory/utils/compression/compression.go @@ -0,0 +1,22 @@ +package compression + +import ( + "bytes" + "compress/gzip" + + "github.com/go-logr/logr" +) + +func Compress(data []byte, log logr.Logger) []byte { + var b bytes.Buffer + gz := gzip.NewWriter(&b) + if _, err := gz.Write(data); err != nil { + log.Error(err, "Failed to compress") + } + + if err := gz.Close(); err != nil { + log.Error(err, "Failed to close writer for compress") + } + + return b.Bytes() +} diff --git a/controllers/factory/utils/k8s/k8s.go b/controllers/factory/utils/k8s/k8s.go index a50e758e..3a29cfc5 100644 --- a/controllers/factory/utils/k8s/k8s.go +++ b/controllers/factory/utils/k8s/k8s.go @@ -456,6 +456,15 @@ func GetSecret(ctx context.Context, namespacedName types.NamespacedName, c clien return result, nil } +func ListSecret(ctx context.Context, c client.Client, listOpts client.ListOptions) ([]corev1.Secret, error) { + secretList := corev1.SecretList{} + err := c.List(ctx, &secretList, &listOpts) + if err != nil { + return nil, err + } + return secretList.Items, nil +} + func DeleteSecret(ctx context.Context, secret *corev1.Secret, c client.Client) error { if err := c.Delete(ctx, secret); err != nil { return err diff --git a/controllers/factory/vector/vectoragent/vectoragent_config.go b/controllers/factory/vector/vectoragent/vectoragent_config.go index 0db7ff43..5aaa0ff4 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_config.go +++ b/controllers/factory/vector/vectoragent/vectoragent_config.go @@ -19,15 +19,22 @@ package vectoragent import ( "context" + "github.com/kaasops/vector-operator/controllers/factory/utils/compression" corev1 "k8s.io/api/core/v1" + "sigs.k8s.io/controller-runtime/pkg/log" ) func (ctrl *Controller) createVectorAgentConfig(ctx context.Context) (*corev1.Secret, error) { + log := log.FromContext(ctx).WithValues("vector-agent-rbac", ctrl.Vector.Name) labels := ctrl.labelsForVectorAgent() + var data []byte = ctrl.Config + + if ctrl.Vector.Spec.Agent.CompressConfigFile { + data = compression.Compress(ctrl.Config, log) + } config := map[string][]byte{ - "agent.json": ctrl.Config, + "agent.json": data, } - secret := &corev1.Secret{ ObjectMeta: ctrl.objectMetaVectorAgent(labels), Data: config, diff --git a/controllers/factory/vector/vectoragent/vectoragent_daemonset.go b/controllers/factory/vector/vectoragent/vectoragent_daemonset.go index c2eb9714..7461dce4 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_daemonset.go +++ b/controllers/factory/vector/vectoragent/vectoragent_daemonset.go @@ -24,6 +24,17 @@ import ( func (ctrl *Controller) createVectorAgentDaemonSet() *appsv1.DaemonSet { labels := ctrl.labelsForVectorAgent() + var initContainers []corev1.Container + var containers []corev1.Container + containers = append(containers, *ctrl.VectorAgentContainer()) + + if ctrl.Vector.Spec.Agent.CompressConfigFile { + initContainers = append(initContainers, *ctrl.ConfigReloaderInitContainer()) + } + + if ctrl.Vector.Spec.Agent.CompressConfigFile { + containers = append(containers, *ctrl.ConfigReloaderSidecarContainer()) + } daemonset := &appsv1.DaemonSet{ ObjectMeta: ctrl.objectMetaVectorAgent(labels), @@ -43,25 +54,8 @@ func (ctrl *Controller) createVectorAgentDaemonSet() *appsv1.DaemonSet { PriorityClassName: ctrl.Vector.Spec.Agent.PodSecurityPolicyName, HostNetwork: ctrl.Vector.Spec.Agent.HostNetwork, HostAliases: ctrl.Vector.Spec.Agent.HostAliases, - Containers: []corev1.Container{ - { - Name: ctrl.getNameVectorAgent(), - Image: ctrl.Vector.Spec.Agent.Image, - Args: []string{"--config-dir", "/etc/vector/", "--watch-config"}, - Env: ctrl.generateVectorAgentEnvs(), - Ports: []corev1.ContainerPort{ - { - Name: "prom-exporter", - ContainerPort: 9090, - Protocol: "TCP", - }, - }, - VolumeMounts: ctrl.generateVectorAgentVolumeMounts(), - Resources: ctrl.Vector.Spec.Agent.Resources, - SecurityContext: ctrl.Vector.Spec.Agent.ContainerSecurityContext, - ImagePullPolicy: ctrl.Vector.Spec.Agent.ImagePullPolicy, - }, - }, + InitContainers: initContainers, + Containers: containers, }, }, }, @@ -72,15 +66,21 @@ func (ctrl *Controller) createVectorAgentDaemonSet() *appsv1.DaemonSet { func (ctrl *Controller) generateVectorAgentVolume() []corev1.Volume { volume := ctrl.Vector.Spec.Agent.Volumes + configVolumeSource := corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: ctrl.getNameVectorAgent(), + }, + } + if ctrl.Vector.Spec.Agent.CompressConfigFile { + configVolumeSource = corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + } + } volume = append(volume, []corev1.Volume{ { - Name: "config", - VolumeSource: corev1.VolumeSource{ - Secret: &corev1.SecretVolumeSource{ - SecretName: ctrl.getNameVectorAgent(), - }, - }, + Name: "config", + VolumeSource: configVolumeSource, }, { Name: "data", @@ -108,6 +108,17 @@ func (ctrl *Controller) generateVectorAgentVolume() []corev1.Volume { }, }...) + if ctrl.Vector.Spec.Agent.CompressConfigFile { + volume = append(volume, corev1.Volume{ + Name: "app-config-compress", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: ctrl.getNameVectorAgent(), + }, + }, + }) + } + return volume } @@ -117,7 +128,7 @@ func (ctrl *Controller) generateVectorAgentVolumeMounts() []corev1.VolumeMount { volumeMount = append(volumeMount, []corev1.VolumeMount{ { Name: "config", - MountPath: "/etc/vector/", + MountPath: "/etc/vector", }, { Name: "data", @@ -133,6 +144,15 @@ func (ctrl *Controller) generateVectorAgentVolumeMounts() []corev1.VolumeMount { }, }...) + if ctrl.Vector.Spec.Agent.CompressConfigFile { + volumeMount = append(volumeMount, []corev1.VolumeMount{ + { + Name: "app-config-compress", + MountPath: "/tmp/archive", + }, + }...) + } + return volumeMount } @@ -179,3 +199,72 @@ func (ctrl *Controller) generateVectorAgentEnvs() []corev1.EnvVar { return envs } + +func (ctrl *Controller) VectorAgentContainer() *corev1.Container { + return &corev1.Container{ + Name: ctrl.getNameVectorAgent(), + Image: ctrl.Vector.Spec.Agent.Image, + Args: []string{"--config-dir", "/etc/vector", "--watch-config"}, + // Command: []string{"/bin/sleep", "1000000000000000"}, + Env: ctrl.generateVectorAgentEnvs(), + Ports: []corev1.ContainerPort{ + { + Name: "prom-exporter", + ContainerPort: 9090, + Protocol: "TCP", + }, + }, + VolumeMounts: ctrl.generateVectorAgentVolumeMounts(), + Resources: ctrl.Vector.Spec.Agent.Resources, + SecurityContext: ctrl.Vector.Spec.Agent.ContainerSecurityContext, + ImagePullPolicy: ctrl.Vector.Spec.Agent.ImagePullPolicy, + } +} + +func (ctrl *Controller) ConfigReloaderInitContainer() *corev1.Container { + return &corev1.Container{ + Name: "init-config-reloader", + Image: ctrl.Vector.Spec.Agent.ConfigReloaderImage, + ImagePullPolicy: corev1.PullPolicy(ctrl.Vector.Spec.Agent.ImagePullPolicy), + Resources: ctrl.Vector.Spec.Agent.ConfigReloaderResources, + Args: []string{ + "--init-mode=true", + "--volume-dir-archive=/tmp/archive", + "--dir-for-unarchive=/etc/vector", + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "config", + MountPath: "/etc/vector", + }, + { + Name: "app-config-compress", + MountPath: "/tmp/archive", + }, + }, + } +} + +func (ctrl *Controller) ConfigReloaderSidecarContainer() *corev1.Container { + return &corev1.Container{ + Name: "config-reloader", + Image: ctrl.Vector.Spec.Agent.ConfigReloaderImage, + ImagePullPolicy: corev1.PullPolicy(ctrl.Vector.Spec.Agent.ImagePullPolicy), + Resources: ctrl.Vector.Spec.Agent.ConfigReloaderResources, + Args: []string{ + "--init-mode=false", + "--volume-dir-archive=/tmp/archive", + "--dir-for-unarchive=/etc/vector", + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "config", + MountPath: "/etc/vector", + }, + { + Name: "app-config-compress", + MountPath: "/tmp/archive", + }, + }, + } +} diff --git a/controllers/factory/vector/vectoragent/vectoragent_default.go b/controllers/factory/vector/vectoragent/vectoragent_default.go index e5e86110..e63e2d7b 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_default.go +++ b/controllers/factory/vector/vectoragent/vectoragent_default.go @@ -92,4 +92,19 @@ func (ctrl *Controller) SetDefault() { }, } } + if ctrl.Vector.Spec.Agent.CompressConfigFile && ctrl.Vector.Spec.Agent.ConfigReloaderImage == "" { + ctrl.Vector.Spec.Agent.ConfigReloaderImage = "docker.io/kaasops/config-reloader:v0.1.4" + } + if ctrl.Vector.Spec.Agent.CompressConfigFile && ctrl.Vector.Spec.Agent.ConfigReloaderResources.Requests == nil { + ctrl.Vector.Spec.Agent.ConfigReloaderResources.Requests = corev1.ResourceList{ + corev1.ResourceMemory: resourcev1.MustParse("200Mi"), + corev1.ResourceCPU: resourcev1.MustParse("100m"), + } + } + if ctrl.Vector.Spec.Agent.CompressConfigFile && ctrl.Vector.Spec.Agent.ConfigReloaderResources.Limits == nil { + ctrl.Vector.Spec.Agent.ConfigReloaderResources.Limits = corev1.ResourceList{ + corev1.ResourceMemory: resourcev1.MustParse("1024Mi"), + corev1.ResourceCPU: resourcev1.MustParse("1000m"), + } + } } From d7f9c118e73cb1ee7c4fdde9f9197a78555793c2 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Tue, 16 May 2023 10:46:32 +0300 Subject: [PATCH 210/316] add securitycontext to configcheck and reloader --- .../factory/config/configcheck/configcheck.go | 54 ++++++++++--------- .../config/configcheck/configcheck_pod.go | 14 ++--- .../vectoragent/vectoragent_daemonset.go | 2 + 3 files changed, 39 insertions(+), 31 deletions(-) diff --git a/controllers/factory/config/configcheck/configcheck.go b/controllers/factory/config/configcheck/configcheck.go index 5cd9425b..852da917 100644 --- a/controllers/factory/config/configcheck/configcheck.go +++ b/controllers/factory/config/configcheck/configcheck.go @@ -44,18 +44,20 @@ type ConfigCheck struct { Client client.Client ClientSet *kubernetes.Clientset - Name string - Namespace string - Initiator string - Image string - ImagePullPolicy corev1.PullPolicy - Envs []corev1.EnvVar - Hash string - Tolerations []corev1.Toleration - Resources corev1.ResourceRequirements - CompressedConfig bool - ConfigReloaderImage string - ConfigReloaderResources corev1.ResourceRequirements + Name string + Namespace string + Initiator string + Image string + ImagePullPolicy corev1.PullPolicy + Envs []corev1.EnvVar + Hash string + Tolerations []corev1.Toleration + Resources corev1.ResourceRequirements + SecurityContext *corev1.PodSecurityContext + ContainerSecurityContext *corev1.SecurityContext + CompressedConfig bool + ConfigReloaderImage string + ConfigReloaderResources corev1.ResourceRequirements } func New( @@ -82,19 +84,21 @@ func New( } return &ConfigCheck{ - Config: config, - Client: c, - ClientSet: cs, - Name: va.Name, - Namespace: va.Namespace, - Image: image, - ImagePullPolicy: va.Spec.Agent.ImagePullPolicy, - Envs: env, - Tolerations: tolerations, - Resources: resources, - CompressedConfig: va.Spec.Agent.CompressConfigFile, - ConfigReloaderImage: va.Spec.Agent.ConfigReloaderImage, - ConfigReloaderResources: va.Spec.Agent.ConfigReloaderResources, + Config: config, + Client: c, + ClientSet: cs, + Name: va.Name, + Namespace: va.Namespace, + Image: image, + ImagePullPolicy: va.Spec.Agent.ImagePullPolicy, + Envs: env, + Tolerations: tolerations, + Resources: resources, + SecurityContext: va.Spec.Agent.SecurityContext, + ContainerSecurityContext: va.Spec.Agent.ContainerSecurityContext, + CompressedConfig: va.Spec.Agent.CompressConfigFile, + ConfigReloaderImage: va.Spec.Agent.ConfigReloaderImage, + ConfigReloaderResources: va.Spec.Agent.ConfigReloaderResources, } } diff --git a/controllers/factory/config/configcheck/configcheck_pod.go b/controllers/factory/config/configcheck/configcheck_pod.go index 87df96cf..1160fbf1 100644 --- a/controllers/factory/config/configcheck/configcheck_pod.go +++ b/controllers/factory/config/configcheck/configcheck_pod.go @@ -38,16 +38,17 @@ func (cc *ConfigCheck) createVectorConfigCheckPod() *corev1.Pod { Spec: corev1.PodSpec{ ServiceAccountName: "vector-configcheck", Volumes: cc.generateVectorConfigCheckVolume(), - SecurityContext: &corev1.PodSecurityContext{}, + SecurityContext: cc.SecurityContext, Tolerations: cc.Tolerations, InitContainers: initContainers, Containers: []corev1.Container{ { - Name: "config-check", - Image: cc.Image, - Resources: cc.Resources, - Args: []string{"validate", "/etc/vector/*.json"}, - Env: cc.generateVectorConfigCheckEnvs(), + Name: "config-check", + Image: cc.Image, + Resources: cc.Resources, + Args: []string{"validate", "/etc/vector/*.json"}, + Env: cc.generateVectorConfigCheckEnvs(), + SecurityContext: cc.ContainerSecurityContext, Ports: []corev1.ContainerPort{ { Name: "prom-exporter", @@ -226,6 +227,7 @@ func (cc *ConfigCheck) ConfigReloaderInitContainer() *corev1.Container { Image: cc.ConfigReloaderImage, ImagePullPolicy: cc.ImagePullPolicy, Resources: cc.ConfigReloaderResources, + SecurityContext: cc.ContainerSecurityContext, Args: []string{ "--init-mode=true", "--volume-dir-archive=/tmp/archive", diff --git a/controllers/factory/vector/vectoragent/vectoragent_daemonset.go b/controllers/factory/vector/vectoragent/vectoragent_daemonset.go index 7461dce4..186e4d9f 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_daemonset.go +++ b/controllers/factory/vector/vectoragent/vectoragent_daemonset.go @@ -227,6 +227,7 @@ func (ctrl *Controller) ConfigReloaderInitContainer() *corev1.Container { Image: ctrl.Vector.Spec.Agent.ConfigReloaderImage, ImagePullPolicy: corev1.PullPolicy(ctrl.Vector.Spec.Agent.ImagePullPolicy), Resources: ctrl.Vector.Spec.Agent.ConfigReloaderResources, + SecurityContext: ctrl.Vector.Spec.Agent.ContainerSecurityContext, Args: []string{ "--init-mode=true", "--volume-dir-archive=/tmp/archive", @@ -251,6 +252,7 @@ func (ctrl *Controller) ConfigReloaderSidecarContainer() *corev1.Container { Image: ctrl.Vector.Spec.Agent.ConfigReloaderImage, ImagePullPolicy: corev1.PullPolicy(ctrl.Vector.Spec.Agent.ImagePullPolicy), Resources: ctrl.Vector.Spec.Agent.ConfigReloaderResources, + SecurityContext: ctrl.Vector.Spec.Agent.ContainerSecurityContext, Args: []string{ "--init-mode=false", "--volume-dir-archive=/tmp/archive", From b26fcb325b7111216b51b3dd924404c764efb6a7 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Tue, 16 May 2023 10:54:56 +0300 Subject: [PATCH 211/316] release v0.0.23 --- CHANGELOG.md | 3 ++ helm/charts/vector-operator/Chart.yaml | 4 +- helm/index.yaml | 51 ++++++++++++++--------- helm/packages/vector-operator-0.0.23.tgz | Bin 0 -> 30911 bytes 4 files changed, 37 insertions(+), 21 deletions(-) create mode 100644 helm/packages/vector-operator-0.0.23.tgz diff --git a/CHANGELOG.md b/CHANGELOG.md index b13de15f..d66a9120 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +### v0.0.23 +- [[105]](https://github.com/kaasops/vector-operator/pull/105) **Feature** Added config file gz compression for large configs + ### v0.0.22 ### v0.0.21 diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index 2e813e5b..5e52854b 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.22 +version: 0.0.23 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.0.22" +appVersion: "v0.0.23" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/index.yaml b/helm/index.yaml index e6907301..3f47a9df 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.0.23 + created: "2023-05-16T10:54:43.003044186+03:00" + description: A Helm chart to install Vector Operator + digest: 5c72f96e2eac100651f81854a2a26c96a52e0ba73f1a1416b5bd91de73e50922 + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.23.tgz + version: 0.0.23 - apiVersion: v2 appVersion: v0.0.22 - created: "2023-05-03T11:25:23.847076+03:00" + created: "2023-05-16T10:54:43.002102676+03:00" description: A Helm chart to install Vector Operator digest: bf96ddc8ac61e9d6beb8bc763fbf3fa6025d950b29d70d80de6e8a0ea45e0411 home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.0.22 - apiVersion: v2 appVersion: v0.0.21 - created: "2023-05-03T11:25:23.845923+03:00" + created: "2023-05-16T10:54:43.001166173+03:00" description: A Helm chart to install Vector Operator digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2023-05-03T11:25:23.844949+03:00" + created: "2023-05-16T10:54:42.999884391+03:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2023-05-03T11:25:23.843095+03:00" + created: "2023-05-16T10:54:42.999305975+03:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2023-05-03T11:25:23.842456+03:00" + created: "2023-05-16T10:54:42.998562943+03:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2023-05-03T11:25:23.840801+03:00" + created: "2023-05-16T10:54:42.997596871+03:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2023-05-03T11:25:23.839337+03:00" + created: "2023-05-16T10:54:42.996522528+03:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2023-05-03T11:25:23.838482+03:00" + created: "2023-05-16T10:54:42.99593928+03:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2023-05-03T11:25:23.836161+03:00" + created: "2023-05-16T10:54:42.995334998+03:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2023-05-03T11:25:23.82884+03:00" + created: "2023-05-16T10:54:42.994690068+03:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2023-05-03T11:25:23.828098+03:00" + created: "2023-05-16T10:54:42.99404526+03:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -146,7 +159,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2023-05-03T11:25:23.827349+03:00" + created: "2023-05-16T10:54:42.990600559+03:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -159,7 +172,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2023-05-03T11:25:23.826724+03:00" + created: "2023-05-16T10:54:42.990080972+03:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -172,7 +185,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2023-05-03T11:25:23.84978+03:00" + created: "2023-05-16T10:54:43.005197428+03:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -185,7 +198,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2023-05-03T11:25:23.849186+03:00" + created: "2023-05-16T10:54:43.004741256+03:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -198,7 +211,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2023-05-03T11:25:23.848123+03:00" + created: "2023-05-16T10:54:43.004267301+03:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -211,7 +224,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2023-05-03T11:25:23.847608+03:00" + created: "2023-05-16T10:54:43.003511619+03:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -224,7 +237,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2023-05-03T11:25:23.826111+03:00" + created: "2023-05-16T10:54:42.989596617+03:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -235,4 +248,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2023-05-03T11:25:23.825093+03:00" +generated: "2023-05-16T10:54:42.989033352+03:00" diff --git a/helm/packages/vector-operator-0.0.23.tgz b/helm/packages/vector-operator-0.0.23.tgz new file mode 100644 index 0000000000000000000000000000000000000000..c932124a37347030c6d83343208c6e9223b07357 GIT binary patch literal 30911 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 Date: Tue, 16 May 2023 11:17:20 +0300 Subject: [PATCH 212/316] update helm v0.0.23 --- .../observability.kaasops.io_vectors.yaml | 32 +++++++++++++ helm/index.yaml | 42 +++++++++--------- helm/packages/vector-operator-0.0.23.tgz | Bin 30911 -> 31031 bytes 3 files changed, 53 insertions(+), 21 deletions(-) diff --git a/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml index 6677c272..9ff19ade 100644 --- a/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml +++ b/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml @@ -923,6 +923,9 @@ spec: playground: type: boolean type: object + compressConfigFile: + description: Compress config file + type: boolean configCheck: description: ConfigCheck is the Schema for control params for ConfigCheck pods @@ -1905,6 +1908,35 @@ spec: type: object type: array type: object + configReloaderImage: + type: string + configReloaderResources: + description: ResourceRequirements describes the compute resource + requirements. + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, otherwise + to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object containerSecurityContext: description: SecurityContext holds security configuration that will be applied to a container. Some fields are present in both diff --git a/helm/index.yaml b/helm/index.yaml index 3f47a9df..44ddc508 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -3,9 +3,9 @@ entries: vector-operator: - apiVersion: v2 appVersion: v0.0.23 - created: "2023-05-16T10:54:43.003044186+03:00" + created: "2023-05-16T11:16:49.102048767+03:00" description: A Helm chart to install Vector Operator - digest: 5c72f96e2eac100651f81854a2a26c96a52e0ba73f1a1416b5bd91de73e50922 + digest: 546d202b3b9263f789b88335263191098dfcabd5d8698105f37cad24d56a8ed0 home: https://github.com/kaasops/vector-operator name: vector-operator sources: @@ -16,7 +16,7 @@ entries: version: 0.0.23 - apiVersion: v2 appVersion: v0.0.22 - created: "2023-05-16T10:54:43.002102676+03:00" + created: "2023-05-16T11:16:49.101155689+03:00" description: A Helm chart to install Vector Operator digest: bf96ddc8ac61e9d6beb8bc763fbf3fa6025d950b29d70d80de6e8a0ea45e0411 home: https://github.com/kaasops/vector-operator @@ -29,7 +29,7 @@ entries: version: 0.0.22 - apiVersion: v2 appVersion: v0.0.21 - created: "2023-05-16T10:54:43.001166173+03:00" + created: "2023-05-16T11:16:49.099944679+03:00" description: A Helm chart to install Vector Operator digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d home: https://github.com/kaasops/vector-operator @@ -42,7 +42,7 @@ entries: version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2023-05-16T10:54:42.999884391+03:00" + created: "2023-05-16T11:16:49.099011351+03:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -55,7 +55,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2023-05-16T10:54:42.999305975+03:00" + created: "2023-05-16T11:16:49.098443915+03:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -68,7 +68,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2023-05-16T10:54:42.998562943+03:00" + created: "2023-05-16T11:16:49.097889316+03:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -81,7 +81,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2023-05-16T10:54:42.997596871+03:00" + created: "2023-05-16T11:16:49.097339058+03:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -94,7 +94,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2023-05-16T10:54:42.996522528+03:00" + created: "2023-05-16T11:16:49.096349821+03:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -107,7 +107,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2023-05-16T10:54:42.99593928+03:00" + created: "2023-05-16T11:16:49.095770526+03:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -120,7 +120,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2023-05-16T10:54:42.995334998+03:00" + created: "2023-05-16T11:16:49.095184625+03:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -133,7 +133,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2023-05-16T10:54:42.994690068+03:00" + created: "2023-05-16T11:16:49.094601661+03:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -146,7 +146,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2023-05-16T10:54:42.99404526+03:00" + created: "2023-05-16T11:16:49.094001109+03:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -159,7 +159,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2023-05-16T10:54:42.990600559+03:00" + created: "2023-05-16T11:16:49.092905837+03:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -172,7 +172,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2023-05-16T10:54:42.990080972+03:00" + created: "2023-05-16T11:16:49.092328544+03:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -185,7 +185,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2023-05-16T10:54:43.005197428+03:00" + created: "2023-05-16T11:16:49.104095853+03:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -198,7 +198,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2023-05-16T10:54:43.004741256+03:00" + created: "2023-05-16T11:16:49.103570891+03:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -211,7 +211,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2023-05-16T10:54:43.004267301+03:00" + created: "2023-05-16T11:16:49.10305143+03:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -224,7 +224,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2023-05-16T10:54:43.003511619+03:00" + created: "2023-05-16T11:16:49.102567044+03:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -237,7 +237,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2023-05-16T10:54:42.989596617+03:00" + created: "2023-05-16T11:16:49.091804194+03:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -248,4 +248,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2023-05-16T10:54:42.989033352+03:00" +generated: "2023-05-16T11:16:49.09117904+03:00" diff --git a/helm/packages/vector-operator-0.0.23.tgz b/helm/packages/vector-operator-0.0.23.tgz index c932124a37347030c6d83343208c6e9223b07357..2fdbb4fab4cd6675361201a403a8f1d9c7bafeff 100644 GIT binary patch delta 27753 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

_$X?slOt7O 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 E02zSvp#T5? delta 27650 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 Date: Tue, 16 May 2023 11:53:30 +0300 Subject: [PATCH 213/316] increase configcheck timeout --- controllers/factory/config/configcheck/configcheck.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controllers/factory/config/configcheck/configcheck.go b/controllers/factory/config/configcheck/configcheck.go index 852da917..53dfc73a 100644 --- a/controllers/factory/config/configcheck/configcheck.go +++ b/controllers/factory/config/configcheck/configcheck.go @@ -36,7 +36,7 @@ import ( vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" ) -const waitConfigcheckResultTimeout = 180 * time.Second +const waitConfigcheckResultTimeout = 300 * time.Second type ConfigCheck struct { Config []byte From 3f7857c5a53193569fa5b1adfc4f77b72cda2ff7 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Wed, 24 May 2023 14:47:37 +0300 Subject: [PATCH 214/316] return api resources only for a group and version --- controllers/factory/utils/k8s/k8s.go | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/controllers/factory/utils/k8s/k8s.go b/controllers/factory/utils/k8s/k8s.go index 3a29cfc5..b421024c 100644 --- a/controllers/factory/utils/k8s/k8s.go +++ b/controllers/factory/utils/k8s/k8s.go @@ -506,18 +506,19 @@ func NamespaceNameToLabel(namespace string) string { // ResourceExists returns true if the given resource kind exists // in the given api groupversion func ResourceExists(dc discovery.DiscoveryInterface, apiGroupVersion, kind string) (bool, error) { - _, apiLists, err := dc.ServerGroupsAndResources() + apiList, err := dc.ServerResourcesForGroupVersion(apiGroupVersion) if err != nil { + if api_errors.IsNotFound(err) { + return false, nil + } return false, err } - for _, apiList := range apiLists { - if apiList.GroupVersion == apiGroupVersion { - for _, r := range apiList.APIResources { - if r.Kind == kind { - return true, nil - } - } + + for _, r := range apiList.APIResources { + if r.Kind == kind { + return true, nil } } + return false, nil } From 2c2f2436d6c3bc907d9c2fb5cd0027aece099c9b Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Wed, 24 May 2023 17:10:13 +0300 Subject: [PATCH 215/316] replace servicemonitor to podmonitor --- controllers/factory/utils/k8s/k8s.go | 12 +++---- .../vectoragent/vectoragent_controller.go | 16 ++++----- ...cemonitor.go => vectoragent_podmonitor.go} | 12 +++---- .../vector/vectoragent/vectoragent_service.go | 36 ++++++------------- controllers/vector_controller.go | 4 +-- .../templates/clusterrole.yaml | 2 +- 6 files changed, 33 insertions(+), 49 deletions(-) rename controllers/factory/vector/vectoragent/{vectoragent_servicemonitor.go => vectoragent_podmonitor.go} (58%) diff --git a/controllers/factory/utils/k8s/k8s.go b/controllers/factory/utils/k8s/k8s.go index b421024c..ab31d66b 100644 --- a/controllers/factory/utils/k8s/k8s.go +++ b/controllers/factory/utils/k8s/k8s.go @@ -62,8 +62,8 @@ func CreateOrUpdateResource(ctx context.Context, obj client.Object, c client.Cli return createOrUpdateClusterRole(ctx, obj, c) case *rbacv1.ClusterRoleBinding: return createOrUpdateClusterRoleBinding(ctx, obj, c) - case *monitorv1.ServiceMonitor: - return createOrUpdateServiceMonitor(ctx, obj, c) + case *monitorv1.PodMonitor: + return createOrUpdatePodMonitor(ctx, obj, c) default: return NewNotSupportedError(obj) } @@ -375,14 +375,14 @@ func createOrUpdateClusterRoleBinding(ctx context.Context, obj runtime.Object, c // -func createOrUpdateServiceMonitor(ctx context.Context, obj runtime.Object, c client.Client) error { - desired := obj.(*monitorv1.ServiceMonitor) +func createOrUpdatePodMonitor(ctx context.Context, obj runtime.Object, c client.Client) error { + desired := obj.(*monitorv1.PodMonitor) - // Create ServiceMonitor + // Create PodMonitor err := c.Create(ctx, desired) if api_errors.IsAlreadyExists(err) { // If alredy exist - compare with existed - existing := &monitorv1.ServiceMonitor{} + existing := &monitorv1.PodMonitor{} err := c.Get(ctx, client.ObjectKeyFromObject(desired), existing) if err != nil { return err diff --git a/controllers/factory/vector/vectoragent/vectoragent_controller.go b/controllers/factory/vector/vectoragent/vectoragent_controller.go index 6a3a0bd6..814d3e11 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_controller.go +++ b/controllers/factory/vector/vectoragent/vectoragent_controller.go @@ -30,7 +30,7 @@ func (ctrl *Controller) EnsureVectorAgent(ctx context.Context, configOnly bool) log := log.FromContext(ctx).WithValues("vector-agent", ctrl.Vector.Name) log.Info("start Reconcile Vector Agent") - monitoringCRD, err := k8s.ResourceExists(ctrl.ClientSet.Discovery(), monitorv1.SchemeGroupVersion.String(), monitorv1.ServiceMonitorsKind) + monitoringCRD, err := k8s.ResourceExists(ctrl.ClientSet.Discovery(), monitorv1.SchemeGroupVersion.String(), monitorv1.PodMonitorsKind) if err != nil { return err } @@ -43,14 +43,14 @@ func (ctrl *Controller) EnsureVectorAgent(ctx context.Context, configOnly bool) return err } - if ctrl.Vector.Spec.Agent.InternalMetrics || ctrl.Vector.Spec.Agent.Api.Enabled { + if ctrl.Vector.Spec.Agent.Api.Enabled { if err := ctrl.ensureVectorAgentService(ctx); err != nil { return err } } if ctrl.Vector.Spec.Agent.InternalMetrics && monitoringCRD { - if err := ctrl.ensureVectorAgentServiceMonitor(ctx); err != nil { + if err := ctrl.ensureVectorAgentPodMonitor(ctx); err != nil { return err } } @@ -131,14 +131,14 @@ func (ctrl *Controller) ensureVectorAgentDaemonSet(ctx context.Context) error { return k8s.CreateOrUpdateResource(ctx, vectorAgentDaemonSet, ctrl.Client) } -func (ctrl *Controller) ensureVectorAgentServiceMonitor(ctx context.Context) error { - log := log.FromContext(ctx).WithValues("vector-agent-servicemonitor", ctrl.Vector.Name) +func (ctrl *Controller) ensureVectorAgentPodMonitor(ctx context.Context) error { + log := log.FromContext(ctx).WithValues("vector-agent-podmonitor", ctrl.Vector.Name) - log.Info("start Reconcile Vector Agent ServiceMonitor") + log.Info("start Reconcile Vector Agent PodMonitor") - vectorAgentServiceMonitor := ctrl.createVectorAgentServiceMonitor() + vectorAgentPodMonitor := ctrl.createVectorAgentPodMonitor() - return k8s.CreateOrUpdateResource(ctx, vectorAgentServiceMonitor, ctrl.Client) + return k8s.CreateOrUpdateResource(ctx, vectorAgentPodMonitor, ctrl.Client) } func (ctrl *Controller) labelsForVectorAgent() map[string]string { diff --git a/controllers/factory/vector/vectoragent/vectoragent_servicemonitor.go b/controllers/factory/vector/vectoragent/vectoragent_podmonitor.go similarity index 58% rename from controllers/factory/vector/vectoragent/vectoragent_servicemonitor.go rename to controllers/factory/vector/vectoragent/vectoragent_podmonitor.go index 6e7425fe..2b203e0e 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_servicemonitor.go +++ b/controllers/factory/vector/vectoragent/vectoragent_podmonitor.go @@ -6,16 +6,16 @@ import ( monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" ) -func (ctrl *Controller) createVectorAgentServiceMonitor() *monitorv1.ServiceMonitor { +func (ctrl *Controller) createVectorAgentPodMonitor() *monitorv1.PodMonitor { labels := ctrl.labelsForVectorAgent() - servicemonitor := &monitorv1.ServiceMonitor{ + podmonitor := &monitorv1.PodMonitor{ ObjectMeta: ctrl.objectMetaVectorAgent(labels), - Spec: monitorv1.ServiceMonitorSpec{ - Endpoints: []monitorv1.Endpoint{ + Spec: monitorv1.PodMonitorSpec{ + PodMetricsEndpoints: []monitorv1.PodMetricsEndpoint{ { Path: "/metrics", - Port: "vectoragent-metrics", + Port: "9598", }, }, Selector: metav1.LabelSelector{ @@ -24,5 +24,5 @@ func (ctrl *Controller) createVectorAgentServiceMonitor() *monitorv1.ServiceMoni }, } - return servicemonitor + return podmonitor } diff --git a/controllers/factory/vector/vectoragent/vectoragent_service.go b/controllers/factory/vector/vectoragent/vectoragent_service.go index 3b25c3d5..06e41327 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_service.go +++ b/controllers/factory/vector/vectoragent/vectoragent_service.go @@ -23,40 +23,24 @@ import ( ) const ( - ApiPort = 8686 - MetricsExporterPort = 9598 + ApiPort = 8686 ) func (ctrl *Controller) createVectorAgentService() *corev1.Service { labels := ctrl.labelsForVectorAgent() - ports := []corev1.ServicePort{} - - if ctrl.Vector.Spec.Agent.InternalMetrics { - ports = append(ports, corev1.ServicePort{ - Name: "vectoragent-metrics", - Protocol: corev1.Protocol("TCP"), - Port: MetricsExporterPort, - TargetPort: intstr.FromInt(MetricsExporterPort), - }) - } - - if ctrl.Vector.Spec.Agent.Api.Enabled { - ports = append(ports, corev1.ServicePort{ - Name: "api", - Protocol: corev1.Protocol("TCP"), - Port: ApiPort, - TargetPort: intstr.FromInt(ApiPort), - }) - } - - service := &corev1.Service{ + return &corev1.Service{ ObjectMeta: ctrl.objectMetaVectorAgent(labels), Spec: corev1.ServiceSpec{ - Ports: ports, + Ports: []corev1.ServicePort{ + { + Name: "api", + Protocol: corev1.Protocol("TCP"), + Port: ApiPort, + TargetPort: intstr.FromInt(ApiPort), + }, + }, Selector: labels, }, } - - return service } diff --git a/controllers/vector_controller.go b/controllers/vector_controller.go index 7cd411c8..fb96febd 100644 --- a/controllers/vector_controller.go +++ b/controllers/vector_controller.go @@ -105,7 +105,7 @@ func (r *VectorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr // SetupWithManager sets up the controller with the Manager. func (r *VectorReconciler) SetupWithManager(mgr ctrl.Manager) error { - monitoringCRD, err := k8s.ResourceExists(r.DiscoveryClient, monitorv1.SchemeGroupVersion.String(), monitorv1.ServiceMonitorsKind) + monitoringCRD, err := k8s.ResourceExists(r.DiscoveryClient, monitorv1.SchemeGroupVersion.String(), monitorv1.PodMonitorsKind) if err != nil { return err } @@ -120,7 +120,7 @@ func (r *VectorReconciler) SetupWithManager(mgr ctrl.Manager) error { Owns(&rbacv1.ClusterRoleBinding{}) if monitoringCRD { - builder.Owns(&monitorv1.ServiceMonitor{}) + builder.Owns(&monitorv1.PodMonitor{}) } if err = builder.Complete(r); err != nil { diff --git a/helm/charts/vector-operator/templates/clusterrole.yaml b/helm/charts/vector-operator/templates/clusterrole.yaml index 2bd00db9..66e43312 100644 --- a/helm/charts/vector-operator/templates/clusterrole.yaml +++ b/helm/charts/vector-operator/templates/clusterrole.yaml @@ -100,7 +100,7 @@ rules: - apiGroups: - monitoring.coreos.com resources: - - servicemonitors + - podmonitors verbs: - '*' {{- end -}} From cf16f474301d79c2ac7d49f0f8baf407db715da2 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Thu, 25 May 2023 13:59:09 +0300 Subject: [PATCH 216/316] allow override internal metrics pipeline --- .../observability_v1alpha1_vector.yaml | 2 +- controllers/factory/config/config_build.go | 33 +++++++++++++------ 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/config/samples/observability_v1alpha1_vector.yaml b/config/samples/observability_v1alpha1_vector.yaml index fb88d6f5..72d01760 100644 --- a/config/samples/observability_v1alpha1_vector.yaml +++ b/config/samples/observability_v1alpha1_vector.yaml @@ -10,4 +10,4 @@ spec: internalMetrics: false api: enabled: false - compressConfigFile: true + compressConfigFile: false diff --git a/controllers/factory/config/config_build.go b/controllers/factory/config/config_build.go index dbf152d8..3b73ae62 100644 --- a/controllers/factory/config/config_build.go +++ b/controllers/factory/config/config_build.go @@ -34,7 +34,9 @@ const ( KubernetesSourceType = "kubernetes_logs" BlackholeSinkType = "blackhole" InternalMetricsSourceType = "internal_metrics" + InternalMetricsSourceName = "internalMetricsSource" InternalMetricsSinkType = "prometheus_exporter" + InternalMetricsSinkName = "internalMetricsSink" OptimizedKubernetesSourceName = "optimizedKubernetesSource" FilterTransformType = "filter" DefaultSourceName = "defaultSource" @@ -49,7 +51,7 @@ var ( Type: KubernetesSourceType, } internalMetricSource = &Source{ - Name: "internalMetricsSource", + Name: InternalMetricsSourceName, Type: InternalMetricsSourceType, } sinkDefault = &Sink{ @@ -62,7 +64,7 @@ var ( }, } internalMetricsExporter = &Sink{ - Name: "internalMetricsSink", + Name: InternalMetricsSinkName, Type: InternalMetricsSinkType, Inputs: []string{internalMetricSource.Name}, } @@ -106,18 +108,18 @@ func (b *Builder) generateVectorConfig() (*VectorConfig, error) { return nil, err } - if len(sources) == 0 { + if b.vaCtrl.Vector.Spec.Agent.InternalMetrics && !isExporterSinkExists(sinks) { + sources = append(sources, internalMetricSource) + sinks = append(sinks, internalMetricsExporter) + } + + if !b.vaCtrl.Vector.Spec.Agent.InternalMetrics && len(sources) == 0 { sources = []*Source{sourceDefault} } - if len(sinks) == 0 { + if !b.vaCtrl.Vector.Spec.Agent.InternalMetrics && len(sinks) == 0 { sinks = []*Sink{sinkDefault} } - if b.vaCtrl.Vector.Spec.Agent.InternalMetrics { - sources = append(sources, internalMetricSource) - sinks = append(sinks, internalMetricsExporter) - } - vectorConfig.Sinks = sinks vectorConfig.Sources = sources vectorConfig.Transforms = transforms @@ -132,7 +134,6 @@ func (b *Builder) generateVectorConfig() (*VectorConfig, error) { } func (b *Builder) getComponents() (sources []*Source, transforms []*Transform, sinks []*Sink, err error) { - for _, pipeline := range b.Pipelines { pipelineSources, err := getSources(pipeline, nil) if err != nil { @@ -151,6 +152,9 @@ func (b *Builder) getComponents() (sources []*Source, transforms []*Transform, s if source.Type != KubernetesSourceType { return nil, nil, nil, PipelineTypeError } + if source.Type == InternalMetricsSourceType { + return nil, nil, nil, PipelineTypeError + } if source.ExtraNamespaceLabelSelector != "" { if source.ExtraNamespaceLabelSelector != k8s.NamespaceNameToLabel(pipeline.GetNamespace()) { return nil, nil, nil, PipelineScopeError @@ -341,6 +345,15 @@ func (b *Builder) optimizeVectorConfig(config *VectorConfig) error { return nil } +func isExporterSinkExists(sinks []*Sink) bool { + for _, sink := range sinks { + if sink.Type == InternalMetricsSinkType { + return true + } + } + return false +} + func generateVrlFilter(selector string, selectorType string) string { buffer := new(bytes.Buffer) From 9cd33e525cad0681ea75f688ae21c15c4d559928 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Thu, 25 May 2023 14:50:55 +0300 Subject: [PATCH 217/316] copy crds when helm make package --- controllers/factory/config/config_build.go | 4 ++-- helm/Makefile | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/controllers/factory/config/config_build.go b/controllers/factory/config/config_build.go index 3b73ae62..ac240a2c 100644 --- a/controllers/factory/config/config_build.go +++ b/controllers/factory/config/config_build.go @@ -113,10 +113,10 @@ func (b *Builder) generateVectorConfig() (*VectorConfig, error) { sinks = append(sinks, internalMetricsExporter) } - if !b.vaCtrl.Vector.Spec.Agent.InternalMetrics && len(sources) == 0 { + if len(sources) == 0 { sources = []*Source{sourceDefault} } - if !b.vaCtrl.Vector.Spec.Agent.InternalMetrics && len(sinks) == 0 { + if len(sinks) == 0 { sinks = []*Sink{sinkDefault} } diff --git a/helm/Makefile b/helm/Makefile index 20dcbb1b..505d3720 100644 --- a/helm/Makefile +++ b/helm/Makefile @@ -9,6 +9,7 @@ lint: # Package chart into zip file package: + cp ../config/crd/bases/*.yaml charts/vector-operator/crds/ helm package charts/* -d packages # Create index file (use only for initial setup) From 8ab8ab7fcfb6aec89df219c072b0ef3f56ee2f47 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Fri, 2 Jun 2023 16:01:38 +0300 Subject: [PATCH 218/316] release v0.0.24 --- CHANGELOG.md | 3 ++ helm/charts/vector-operator/Chart.yaml | 4 +- helm/index.yaml | 53 ++++++++++++++--------- helm/packages/vector-operator-0.0.24.tgz | Bin 0 -> 31025 bytes 4 files changed, 38 insertions(+), 22 deletions(-) create mode 100644 helm/packages/vector-operator-0.0.24.tgz diff --git a/CHANGELOG.md b/CHANGELOG.md index d66a9120..b6fedc29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +### v0.0.24 +- [[109]](https://github.com/kaasops/vector-operator/pull/109) **Fix** fix broken apiservice error + ### v0.0.23 - [[105]](https://github.com/kaasops/vector-operator/pull/105) **Feature** Added config file gz compression for large configs diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index 5e52854b..95daf339 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.23 +version: 0.0.24 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.0.23" +appVersion: "v0.0.24" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/index.yaml b/helm/index.yaml index 44ddc508..f36d4720 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.0.24 + created: "2023-06-02T16:01:01.963041489+03:00" + description: A Helm chart to install Vector Operator + digest: 66f16cde50392caf51e76fe1059105864d7e65dd2127996ef777c87a9d65db7e + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.24.tgz + version: 0.0.24 - apiVersion: v2 appVersion: v0.0.23 - created: "2023-05-16T11:16:49.102048767+03:00" + created: "2023-06-02T16:01:01.961892887+03:00" description: A Helm chart to install Vector Operator digest: 546d202b3b9263f789b88335263191098dfcabd5d8698105f37cad24d56a8ed0 home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.0.23 - apiVersion: v2 appVersion: v0.0.22 - created: "2023-05-16T11:16:49.101155689+03:00" + created: "2023-06-02T16:01:01.960860484+03:00" description: A Helm chart to install Vector Operator digest: bf96ddc8ac61e9d6beb8bc763fbf3fa6025d950b29d70d80de6e8a0ea45e0411 home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.0.22 - apiVersion: v2 appVersion: v0.0.21 - created: "2023-05-16T11:16:49.099944679+03:00" + created: "2023-06-02T16:01:01.959777945+03:00" description: A Helm chart to install Vector Operator digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2023-05-16T11:16:49.099011351+03:00" + created: "2023-06-02T16:01:01.958393538+03:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2023-05-16T11:16:49.098443915+03:00" + created: "2023-06-02T16:01:01.95768313+03:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2023-05-16T11:16:49.097889316+03:00" + created: "2023-06-02T16:01:01.956990187+03:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2023-05-16T11:16:49.097339058+03:00" + created: "2023-06-02T16:01:01.956292881+03:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2023-05-16T11:16:49.096349821+03:00" + created: "2023-06-02T16:01:01.95559982+03:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2023-05-16T11:16:49.095770526+03:00" + created: "2023-06-02T16:01:01.95419483+03:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2023-05-16T11:16:49.095184625+03:00" + created: "2023-06-02T16:01:01.953477494+03:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2023-05-16T11:16:49.094601661+03:00" + created: "2023-06-02T16:01:01.952728676+03:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -146,7 +159,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2023-05-16T11:16:49.094001109+03:00" + created: "2023-06-02T16:01:01.95196445+03:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -159,7 +172,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2023-05-16T11:16:49.092905837+03:00" + created: "2023-06-02T16:01:01.951216428+03:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -172,7 +185,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2023-05-16T11:16:49.092328544+03:00" + created: "2023-06-02T16:01:01.947816316+03:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -185,7 +198,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2023-05-16T11:16:49.104095853+03:00" + created: "2023-06-02T16:01:01.965636506+03:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -198,7 +211,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2023-05-16T11:16:49.103570891+03:00" + created: "2023-06-02T16:01:01.964960188+03:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -211,7 +224,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2023-05-16T11:16:49.10305143+03:00" + created: "2023-06-02T16:01:01.964357932+03:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -224,7 +237,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2023-05-16T11:16:49.102567044+03:00" + created: "2023-06-02T16:01:01.96365195+03:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -237,7 +250,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2023-05-16T11:16:49.091804194+03:00" + created: "2023-06-02T16:01:01.947221051+03:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -248,4 +261,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2023-05-16T11:16:49.09117904+03:00" +generated: "2023-06-02T16:01:01.943604918+03:00" diff --git a/helm/packages/vector-operator-0.0.24.tgz b/helm/packages/vector-operator-0.0.24.tgz new file mode 100644 index 0000000000000000000000000000000000000000..93d13498d1e725baf06beffa68a1a90b05a409f6 GIT binary patch literal 31025 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 Date: Fri, 2 Jun 2023 17:12:36 +0300 Subject: [PATCH 219/316] fix podmonitor port --- .../factory/vector/vectoragent/vectoragent_podmonitor.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controllers/factory/vector/vectoragent/vectoragent_podmonitor.go b/controllers/factory/vector/vectoragent/vectoragent_podmonitor.go index 2b203e0e..5cbe31a3 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_podmonitor.go +++ b/controllers/factory/vector/vectoragent/vectoragent_podmonitor.go @@ -15,7 +15,7 @@ func (ctrl *Controller) createVectorAgentPodMonitor() *monitorv1.PodMonitor { PodMetricsEndpoints: []monitorv1.PodMetricsEndpoint{ { Path: "/metrics", - Port: "9598", + Port: "prom-exporter", }, }, Selector: metav1.LabelSelector{ From 461908ef52b8019b7f2130ac51d5ea6fe90d5715 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Fri, 2 Jun 2023 17:14:04 +0300 Subject: [PATCH 220/316] hotfix v0.0.24 release --- helm/index.yaml | 44 +++++++++++------------ helm/packages/vector-operator-0.0.24.tgz | Bin 31025 -> 31028 bytes 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/helm/index.yaml b/helm/index.yaml index f36d4720..1ce21fe3 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -3,9 +3,9 @@ entries: vector-operator: - apiVersion: v2 appVersion: v0.0.24 - created: "2023-06-02T16:01:01.963041489+03:00" + created: "2023-06-02T17:13:48.339751119+03:00" description: A Helm chart to install Vector Operator - digest: 66f16cde50392caf51e76fe1059105864d7e65dd2127996ef777c87a9d65db7e + digest: ea257e60ecde063a0d1ed52ce5e3283245b8f0e2daba58ea3a5adb0ba82d7799 home: https://github.com/kaasops/vector-operator name: vector-operator sources: @@ -16,7 +16,7 @@ entries: version: 0.0.24 - apiVersion: v2 appVersion: v0.0.23 - created: "2023-06-02T16:01:01.961892887+03:00" + created: "2023-06-02T17:13:48.338820454+03:00" description: A Helm chart to install Vector Operator digest: 546d202b3b9263f789b88335263191098dfcabd5d8698105f37cad24d56a8ed0 home: https://github.com/kaasops/vector-operator @@ -29,7 +29,7 @@ entries: version: 0.0.23 - apiVersion: v2 appVersion: v0.0.22 - created: "2023-06-02T16:01:01.960860484+03:00" + created: "2023-06-02T17:13:48.337873163+03:00" description: A Helm chart to install Vector Operator digest: bf96ddc8ac61e9d6beb8bc763fbf3fa6025d950b29d70d80de6e8a0ea45e0411 home: https://github.com/kaasops/vector-operator @@ -42,7 +42,7 @@ entries: version: 0.0.22 - apiVersion: v2 appVersion: v0.0.21 - created: "2023-06-02T16:01:01.959777945+03:00" + created: "2023-06-02T17:13:48.33699567+03:00" description: A Helm chart to install Vector Operator digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d home: https://github.com/kaasops/vector-operator @@ -55,7 +55,7 @@ entries: version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2023-06-02T16:01:01.958393538+03:00" + created: "2023-06-02T17:13:48.335705194+03:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -68,7 +68,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2023-06-02T16:01:01.95768313+03:00" + created: "2023-06-02T17:13:48.335140119+03:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -81,7 +81,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2023-06-02T16:01:01.956990187+03:00" + created: "2023-06-02T17:13:48.334588984+03:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -94,7 +94,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2023-06-02T16:01:01.956292881+03:00" + created: "2023-06-02T17:13:48.334004006+03:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -107,7 +107,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2023-06-02T16:01:01.95559982+03:00" + created: "2023-06-02T17:13:48.333024435+03:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -120,7 +120,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2023-06-02T16:01:01.95419483+03:00" + created: "2023-06-02T17:13:48.332443305+03:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -133,7 +133,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2023-06-02T16:01:01.953477494+03:00" + created: "2023-06-02T17:13:48.331870728+03:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -146,7 +146,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2023-06-02T16:01:01.952728676+03:00" + created: "2023-06-02T17:13:48.331281269+03:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -159,7 +159,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2023-06-02T16:01:01.95196445+03:00" + created: "2023-06-02T17:13:48.330688757+03:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -172,7 +172,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2023-06-02T16:01:01.951216428+03:00" + created: "2023-06-02T17:13:48.329289156+03:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -185,7 +185,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2023-06-02T16:01:01.947816316+03:00" + created: "2023-06-02T17:13:48.328677038+03:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -198,7 +198,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2023-06-02T16:01:01.965636506+03:00" + created: "2023-06-02T17:13:48.342166043+03:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -211,7 +211,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2023-06-02T16:01:01.964960188+03:00" + created: "2023-06-02T17:13:48.34169699+03:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -224,7 +224,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2023-06-02T16:01:01.964357932+03:00" + created: "2023-06-02T17:13:48.340714375+03:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -237,7 +237,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2023-06-02T16:01:01.96365195+03:00" + created: "2023-06-02T17:13:48.340239954+03:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -250,7 +250,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2023-06-02T16:01:01.947221051+03:00" + created: "2023-06-02T17:13:48.328150582+03:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -261,4 +261,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2023-06-02T16:01:01.943604918+03:00" +generated: "2023-06-02T17:13:48.327362902+03:00" diff --git a/helm/packages/vector-operator-0.0.24.tgz b/helm/packages/vector-operator-0.0.24.tgz index 93d13498d1e725baf06beffa68a1a90b05a409f6..01fd65b5d52abaf0964cd05b65b066c77e797672 100644 GIT binary patch delta 25920 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 )bF-YMwXY9|hZJvOZxQ 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 C#yQ{s From 19d3b7039ed2001460a20f58560d4e97da6d4f4e Mon Sep 17 00:00:00 2001 From: zvlb Date: Fri, 9 Jun 2023 18:16:33 +0300 Subject: [PATCH 221/316] cleanup docs --- docs/logs-from-file.md | 3 +-- docs/quick-start.md | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/logs-from-file.md b/docs/logs-from-file.md index fb791181..8192efa4 100644 --- a/docs/logs-from-file.md +++ b/docs/logs-from-file.md @@ -10,7 +10,7 @@ apiVersion: observability.kaasops.io/v1alpha1 kind: ClusterVectorPipeline metadata: labels: - app.kubernetes.io/instance: ams-infra-vector + app.kubernetes.io/instance: vector name: k9s-audit namespace: vector spec: @@ -28,7 +28,6 @@ spec: .@timestamp = .stageTimestamp - .cluster = "ams-infra" type: remap sinks: k8s-audit-sink: diff --git a/docs/quick-start.md b/docs/quick-start.md index e2fdcf79..d737e642 100644 --- a/docs/quick-start.md +++ b/docs/quick-start.md @@ -373,8 +373,8 @@ kubectl logs -n vector -l app.kubernetes.io/instance=sample -f ``` Output ```bash -{"file":"/var/log/pods/vector_log-spamer-788b9ffbf5-nmz4m_9585867b-5457-4729-a682-db3bed0ffd67/log-spamer/0.log","kubernetes":{"container_id":"containerd://d280076162fcd9a1521a8054c215521c1f2d7a4e8e72fe63b2195dd2b7d99b7d","container_image":"zvlb/log-spamer:latest","container_name":"log-spamer","namespace_labels":{"kubernetes.io/metadata.name":"vector"},"node_labels":{"beta.kubernetes.io/arch":"amd64","beta.kubernetes.io/os":"linux","kubernetes.io/arch":"amd64","kubernetes.io/hostname":"lux-kube-node13","kubernetes.io/os":"linux"},"pod_ip":"172.24.35.158","pod_ips":["172.24.35.158"],"pod_labels":{"app":"log-spamer","pod-template-hash":"788b9ffbf5"},"pod_name":"log-spamer-788b9ffbf5-nmz4m","pod_namespace":"vector","pod_node_name":"lux-kube-node13","pod_owner":"ReplicaSet/log-spamer-788b9ffbf5","pod_uid":"9585867b-5457-4729-a682-db3bed0ffd67"},"message":"{\"level\":\"info\",\"time\":\"2022-11-01T12:03:31Z\",\"message\":\"3irbVba4Sf8qFC2i78UfjVzwUGzBu3m3AnbMbSTXkkyqTcAaLtuL6S39hAVfqx\"}","source_type":"kubernetes_logs","stream":"stderr","testfield":"test","timestamp":"2022-11-01T12:03:31.766514243Z","timestamp_end":"2022-11-01T12:03:31.766514243Z"} -{"file":"/var/log/pods/vector_log-spamer-788b9ffbf5-nmz4m_9585867b-5457-4729-a682-db3bed0ffd67/log-spamer/0.log","kubernetes":{"container_id":"containerd://d280076162fcd9a1521a8054c215521c1f2d7a4e8e72fe63b2195dd2b7d99b7d","container_image":"zvlb/log-spamer:latest","container_name":"log-spamer","namespace_labels":{"kubernetes.io/metadata.name":"vector"},"node_labels":{"beta.kubernetes.io/arch":"amd64","beta.kubernetes.io/os":"linux","kubernetes.io/arch":"amd64","kubernetes.io/hostname":"lux-kube-node13","kubernetes.io/os":"linux"},"pod_ip":"172.24.35.158","pod_ips":["172.24.35.158"],"pod_labels":{"app":"log-spamer","pod-template-hash":"788b9ffbf5"},"pod_name":"log-spamer-788b9ffbf5-nmz4m","pod_namespace":"vector","pod_node_name":"lux-kube-node13","pod_owner":"ReplicaSet/log-spamer-788b9ffbf5","pod_uid":"9585867b-5457-4729-a682-db3bed0ffd67"},"message":"{\"level\":\"info\",\"time\":\"2022-11-01T12:03:31Z\",\"message\":\"SJtmGewiQcE9hnEtgCjxkzHZpWbvmTNB69temrBZ6pH3aMSGsa5WXFqPBRz7gVhmnYpmpQP7\"}","source_type":"kubernetes_logs","stream":"stderr","testfield":"test","timestamp":"2022-11-01T12:03:31.776769316Z","timestamp_end":"2022-11-01T12:03:31.776769316Z"} +{"file":"/var/log/pods/vector_log-spamer-788b9ffbf5-nmz4m_9585867b-5457-4729-a682-db3bed0ffd67/log-spamer/0.log","kubernetes":{"container_id":"containerd://d280076162fcd9a1521a8054c215521c1f2d7a4e8e72fe63b2195dd2b7d99b7d","container_image":"zvlb/log-spamer:latest","container_name":"log-spamer","namespace_labels":{"kubernetes.io/metadata.name":"vector"},"node_labels":{"beta.kubernetes.io/arch":"amd64","beta.kubernetes.io/os":"linux","kubernetes.io/arch":"amd64","kubernetes.io/hostname":"kube-node13","kubernetes.io/os":"linux"},"pod_ip":"172.24.35.158","pod_ips":["172.24.35.158"],"pod_labels":{"app":"log-spamer","pod-template-hash":"788b9ffbf5"},"pod_name":"log-spamer-788b9ffbf5-nmz4m","pod_namespace":"vector","pod_node_name":"kube-node13","pod_owner":"ReplicaSet/log-spamer-788b9ffbf5","pod_uid":"9585867b-5457-4729-a682-db3bed0ffd67"},"message":"{\"level\":\"info\",\"time\":\"2022-11-01T12:03:31Z\",\"message\":\"3irbVba4Sf8qFC2i78UfjVzwUGzBu3m3AnbMbSTXkkyqTcAaLtuL6S39hAVfqx\"}","source_type":"kubernetes_logs","stream":"stderr","testfield":"test","timestamp":"2022-11-01T12:03:31.766514243Z","timestamp_end":"2022-11-01T12:03:31.766514243Z"} +{"file":"/var/log/pods/vector_log-spamer-788b9ffbf5-nmz4m_9585867b-5457-4729-a682-db3bed0ffd67/log-spamer/0.log","kubernetes":{"container_id":"containerd://d280076162fcd9a1521a8054c215521c1f2d7a4e8e72fe63b2195dd2b7d99b7d","container_image":"zvlb/log-spamer:latest","container_name":"log-spamer","namespace_labels":{"kubernetes.io/metadata.name":"vector"},"node_labels":{"beta.kubernetes.io/arch":"amd64","beta.kubernetes.io/os":"linux","kubernetes.io/arch":"amd64","kubernetes.io/hostname":"kube-node13","kubernetes.io/os":"linux"},"pod_ip":"172.24.35.158","pod_ips":["172.24.35.158"],"pod_labels":{"app":"log-spamer","pod-template-hash":"788b9ffbf5"},"pod_name":"log-spamer-788b9ffbf5-nmz4m","pod_namespace":"vector","pod_node_name":"kube-node13","pod_owner":"ReplicaSet/log-spamer-788b9ffbf5","pod_uid":"9585867b-5457-4729-a682-db3bed0ffd67"},"message":"{\"level\":\"info\",\"time\":\"2022-11-01T12:03:31Z\",\"message\":\"SJtmGewiQcE9hnEtgCjxkzHZpWbvmTNB69temrBZ6pH3aMSGsa5WXFqPBRz7gVhmnYpmpQP7\"}","source_type":"kubernetes_logs","stream":"stderr","testfield":"test","timestamp":"2022-11-01T12:03:31.776769316Z","timestamp_end":"2022-11-01T12:03:31.776769316Z"} ``` You can see field `testfield`, which we add in transform section in VectorPipeline From 26d4ae20ab97a1df1c9007de59a95f63089ef939 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Wed, 14 Jun 2023 12:31:39 +0300 Subject: [PATCH 222/316] change merge k8s sources logic to routes --- api/v1alpha1/vector_types.go | 4 +- .../observability.kaasops.io_vectors.yaml | 5 +- .../observability_v1alpha1_vector.yaml | 2 +- controllers/factory/config/config_build.go | 105 +++++++++++------- controllers/factory/config/types.go | 10 +- 5 files changed, 73 insertions(+), 53 deletions(-) diff --git a/api/v1alpha1/vector_types.go b/api/v1alpha1/vector_types.go index dd547363..222966ba 100644 --- a/api/v1alpha1/vector_types.go +++ b/api/v1alpha1/vector_types.go @@ -31,9 +31,9 @@ type VectorSpec struct { // Vector Agent Agent *VectorAgent `json:"agent,omitempty"` - // Enable kubernetes source config optimization + // Merge kubernetes sources and move selectors processing to transforms. // +optional - OptimizeKubeSourceConfig bool `json:"optimizeKubeSourceConfig,omitempty"` + MergeKubernetesSources bool `json:"mergeKubernetesSources,omitempty"` // Vector Aggregator // Aggregator *VectorAggregator `json:"aggregator,omitempty"` diff --git a/config/crd/bases/observability.kaasops.io_vectors.yaml b/config/crd/bases/observability.kaasops.io_vectors.yaml index 9ff19ade..910673df 100644 --- a/config/crd/bases/observability.kaasops.io_vectors.yaml +++ b/config/crd/bases/observability.kaasops.io_vectors.yaml @@ -4140,8 +4140,9 @@ spec: type: object type: array type: object - optimizeKubeSourceConfig: - description: Enable kubernetes source config optimization + mergeKubernetesSources: + description: Merge kubernetes sources and move selectors processing + to transforms. type: boolean type: object status: diff --git a/config/samples/observability_v1alpha1_vector.yaml b/config/samples/observability_v1alpha1_vector.yaml index 72d01760..66324587 100644 --- a/config/samples/observability_v1alpha1_vector.yaml +++ b/config/samples/observability_v1alpha1_vector.yaml @@ -4,7 +4,7 @@ metadata: name: vector-sample namespace: vector spec: - optimizeKubeSourceConfig: true + mergeKubernetesSources: true agent: image: "timberio/vector:0.28.1-debian" internalMetrics: false diff --git a/controllers/factory/config/config_build.go b/controllers/factory/config/config_build.go index ac240a2c..d831c3e4 100644 --- a/controllers/factory/config/config_build.go +++ b/controllers/factory/config/config_build.go @@ -31,18 +31,19 @@ import ( ) const ( - KubernetesSourceType = "kubernetes_logs" - BlackholeSinkType = "blackhole" - InternalMetricsSourceType = "internal_metrics" - InternalMetricsSourceName = "internalMetricsSource" - InternalMetricsSinkType = "prometheus_exporter" - InternalMetricsSinkName = "internalMetricsSink" - OptimizedKubernetesSourceName = "optimizedKubernetesSource" - FilterTransformType = "filter" - DefaultSourceName = "defaultSource" - PodSelectorType = "pod_labels" - NamespaceSelectorType = "ns_labels" - OptimizationConditionType = "vrl" + KubernetesSourceType = "kubernetes_logs" + BlackholeSinkType = "blackhole" + InternalMetricsSourceType = "internal_metrics" + InternalMetricsSourceName = "internalMetricsSource" + InternalMetricsSinkType = "prometheus_exporter" + InternalMetricsSinkName = "internalMetricsSink" + MergedKubernetesSourceName = "mergedKubernetesSource" + MergedSourceTransformName = "merged" + RouteTransformType = "route" + DefaultSourceName = "defaultSource" + PodSelectorType = "pod_labels" + NamespaceSelectorType = "ns_labels" + OptimizationConditionType = "vrl" ) var ( @@ -124,8 +125,8 @@ func (b *Builder) generateVectorConfig() (*VectorConfig, error) { vectorConfig.Sources = sources vectorConfig.Transforms = transforms - if b.vaCtrl.Vector.Spec.OptimizeKubeSourceConfig { - if err := b.optimizeVectorConfig(vectorConfig); err != nil { + if b.vaCtrl.Vector.Spec.MergeKubernetesSources { + if err := b.mergeKubernetesSources(vectorConfig); err != nil { return nil, err } } @@ -311,47 +312,65 @@ func cfgToMap(config *VectorConfig) (cfgMap map[string]interface{}, err error) { return cfgMap, nil } -// Experemental -func (b *Builder) optimizeVectorConfig(config *VectorConfig) error { - var optimizedSource []*Source - var optimizationRequired bool +func isExporterSinkExists(sinks []*Sink) bool { + for _, sink := range sinks { + if sink.Type == InternalMetricsSinkType { + return true + } + } + return false +} + +// Merges a large number of kubernetes sources to reduce k8s api pressure during vector start. +func (b *Builder) mergeKubernetesSources(config *VectorConfig) error { + var mergedSource []*Source + routes := make(map[string]string) for _, source := range config.Sources { - if source.ExtraNamespaceLabelSelector != "" && source.Type == KubernetesSourceType && source.ExtraLabelSelector != "" { - if source.ExtraFieldSelector != "" { - optimizedSource = append(optimizedSource, source) + if source.Type == KubernetesSourceType { + if source.ExtraFieldSelector == "" && source.ExtraNamespaceLabelSelector != "" && source.ExtraLabelSelector != "" { + routes[source.Name] = generateVrlFilter(source.ExtraLabelSelector, PodSelectorType) + "&&" + generateVrlFilter(source.ExtraNamespaceLabelSelector, NamespaceSelectorType) continue } - optimizationRequired = true - - config.Transforms = append(config.Transforms, &Transform{ - Name: source.Name, - Inputs: []string{OptimizedKubernetesSourceName}, - Type: FilterTransformType, - Condition: generateVrlFilter(source.ExtraLabelSelector, PodSelectorType) + "&&" + generateVrlFilter(source.ExtraNamespaceLabelSelector, NamespaceSelectorType), - }) - continue } - optimizedSource = append(optimizedSource, source) + mergedSource = append(mergedSource, source) } - if optimizationRequired { - optimizedSource = append(optimizedSource, &Source{ - Name: OptimizedKubernetesSourceName, + if len(routes) > 0 { + mergedSource = append(mergedSource, &Source{ + Name: MergedKubernetesSourceName, Type: KubernetesSourceType, }) - config.Sources = optimizedSource - } + transform := &Transform{ + Name: MergedSourceTransformName, + Type: RouteTransformType, + Inputs: []string{MergedKubernetesSourceName}, + Route: routes, + } - return nil -} + for _, t := range config.Transforms { + for n, i := range t.Inputs { + _, ok := routes[i] + if ok { + t.Inputs[n] = MergedSourceTransformName + "." + i -func isExporterSinkExists(sinks []*Sink) bool { - for _, sink := range sinks { - if sink.Type == InternalMetricsSinkType { - return true + } + } } + + for _, t := range config.Sinks { + for n, i := range t.Inputs { + _, ok := routes[i] + if ok { + t.Inputs[n] = MergedSourceTransformName + "." + i + + } + } + } + config.Sources = mergedSource + config.Transforms = append(config.Transforms, transform) } - return false + + return nil } func generateVrlFilter(selector string, selectorType string) string { diff --git a/controllers/factory/config/types.go b/controllers/factory/config/types.go index aeeba195..97f5c0c4 100644 --- a/controllers/factory/config/types.go +++ b/controllers/factory/config/types.go @@ -34,11 +34,11 @@ type Source struct { } type Transform struct { - Name string - Type string `mapper:"type"` - Inputs []string `mapper:"inputs"` - Condition interface{} `mapper:"condition,omitempty"` - Options map[string]interface{} `mapstructure:",remain"` + Name string + Type string `mapper:"type"` + Inputs []string `mapper:"inputs"` + Route map[string]string `mapper:"route,omitempty"` + Options map[string]interface{} `mapstructure:",remain"` } type Sink struct { From 23d621968ebc42660ec1d4db3c6743e5272a6444 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Wed, 14 Jun 2023 16:32:08 +0300 Subject: [PATCH 223/316] do not reconcile vector if config is broken --- .../factory/config/configcheck/configcheck.go | 6 +++++- controllers/vector_controller.go | 16 ++++++++++------ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/controllers/factory/config/configcheck/configcheck.go b/controllers/factory/config/configcheck/configcheck.go index 53dfc73a..c94d2440 100644 --- a/controllers/factory/config/configcheck/configcheck.go +++ b/controllers/factory/config/configcheck/configcheck.go @@ -18,6 +18,7 @@ package configcheck import ( "context" + "errors" "math/rand" "time" @@ -139,6 +140,9 @@ func (cc *ConfigCheck) Run(ctx context.Context) (string, error) { reason, err := cc.getCheckResult(ctx, vectorConfigCheckPod) if err != nil { + if errors.Is(err, ValidationError) { + return reason, err + } return "", err } @@ -220,7 +224,7 @@ func (cc *ConfigCheck) getCheckResult(ctx context.Context, pod *corev1.Pod) (rea if err != nil { return "", err } - return reason, nil + return reason, ValidationError } } case <-ctx.Done(): diff --git a/controllers/vector_controller.go b/controllers/vector_controller.go index fb96febd..455bc73b 100644 --- a/controllers/vector_controller.go +++ b/controllers/vector_controller.go @@ -171,6 +171,8 @@ func reconcileVectors(ctx context.Context, client client.Client, clientset *kube } func createOrUpdateVector(ctx context.Context, client client.Client, clientset *kubernetes.Clientset, v *vectorv1alpha1.Vector, configOnly bool) (ctrl.Result, error) { + _ = log.FromContext(ctx) + log := log.FromContext(ctx).WithValues("Vector", v.Name) // Init Controller for Vector Agent vaCtrl := vectoragent.NewController(v, client, clientset) @@ -199,15 +201,17 @@ func createOrUpdateVector(ctx context.Context, client client.Client, clientset * ) configCheck.Initiator = configcheck.ConfigCheckInitiatorVector reason, err := configCheck.Run(ctx) - if errors.Is(err, configcheck.ValidationError) { - if err := vaCtrl.SetFailedStatus(ctx, reason); err != nil { - return ctrl.Result{}, err - } - return ctrl.Result{}, err - } if err != nil { + if errors.Is(err, configcheck.ValidationError) { + if err := vaCtrl.SetFailedStatus(ctx, reason); err != nil { + return ctrl.Result{}, err + } + log.Error(err, "Invalid config") + return ctrl.Result{}, nil + } return ctrl.Result{}, err } + } vaCtrl.Config = byteConfig From 838725cab87ccaa8f90f480b8ccf78bd5bd31262 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Wed, 14 Jun 2023 16:51:13 +0300 Subject: [PATCH 224/316] release v0.0.25 --- CHANGELOG.md | 3 + helm/charts/vector-operator/Chart.yaml | 4 +- .../observability.kaasops.io_vectors.yaml | 5 +- helm/index.yaml | 55 +++++++++++------- helm/packages/vector-operator-0.0.25.tgz | Bin 0 -> 31043 bytes 5 files changed, 42 insertions(+), 25 deletions(-) create mode 100644 helm/packages/vector-operator-0.0.25.tgz diff --git a/CHANGELOG.md b/CHANGELOG.md index b6fedc29..033700ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +### v0.0.25 +- [[112]](https://github.com/kaasops/vector-operator/pull/112) **Feature** change merge k8s sources logic to routes + ### v0.0.24 - [[109]](https://github.com/kaasops/vector-operator/pull/109) **Fix** fix broken apiservice error diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index 95daf339..f25add37 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.24 +version: 0.0.25 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.0.24" +appVersion: "v0.0.25" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml index 9ff19ade..910673df 100644 --- a/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml +++ b/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml @@ -4140,8 +4140,9 @@ spec: type: object type: array type: object - optimizeKubeSourceConfig: - description: Enable kubernetes source config optimization + mergeKubernetesSources: + description: Merge kubernetes sources and move selectors processing + to transforms. type: boolean type: object status: diff --git a/helm/index.yaml b/helm/index.yaml index 1ce21fe3..6fca0bd7 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.0.25 + created: "2023-06-14T16:50:46.130226539+03:00" + description: A Helm chart to install Vector Operator + digest: 776f4ca527828b6fd4f1aced2bb887c8955118c60668c922657ad1aa4d764f96 + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.25.tgz + version: 0.0.25 - apiVersion: v2 appVersion: v0.0.24 - created: "2023-06-02T17:13:48.339751119+03:00" + created: "2023-06-14T16:50:46.129178592+03:00" description: A Helm chart to install Vector Operator digest: ea257e60ecde063a0d1ed52ce5e3283245b8f0e2daba58ea3a5adb0ba82d7799 home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.0.24 - apiVersion: v2 appVersion: v0.0.23 - created: "2023-06-02T17:13:48.338820454+03:00" + created: "2023-06-14T16:50:46.127837554+03:00" description: A Helm chart to install Vector Operator digest: 546d202b3b9263f789b88335263191098dfcabd5d8698105f37cad24d56a8ed0 home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.0.23 - apiVersion: v2 appVersion: v0.0.22 - created: "2023-06-02T17:13:48.337873163+03:00" + created: "2023-06-14T16:50:46.126896951+03:00" description: A Helm chart to install Vector Operator digest: bf96ddc8ac61e9d6beb8bc763fbf3fa6025d950b29d70d80de6e8a0ea45e0411 home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.0.22 - apiVersion: v2 appVersion: v0.0.21 - created: "2023-06-02T17:13:48.33699567+03:00" + created: "2023-06-14T16:50:46.126018435+03:00" description: A Helm chart to install Vector Operator digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2023-06-02T17:13:48.335705194+03:00" + created: "2023-06-14T16:50:46.12483315+03:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2023-06-02T17:13:48.335140119+03:00" + created: "2023-06-14T16:50:46.124257114+03:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2023-06-02T17:13:48.334588984+03:00" + created: "2023-06-14T16:50:46.12368886+03:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2023-06-02T17:13:48.334004006+03:00" + created: "2023-06-14T16:50:46.123130399+03:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2023-06-02T17:13:48.333024435+03:00" + created: "2023-06-14T16:50:46.122585426+03:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2023-06-02T17:13:48.332443305+03:00" + created: "2023-06-14T16:50:46.121609024+03:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2023-06-02T17:13:48.331870728+03:00" + created: "2023-06-14T16:50:46.121002392+03:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -146,7 +159,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2023-06-02T17:13:48.331281269+03:00" + created: "2023-06-14T16:50:46.120403041+03:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -159,7 +172,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2023-06-02T17:13:48.330688757+03:00" + created: "2023-06-14T16:50:46.119819981+03:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -172,7 +185,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2023-06-02T17:13:48.329289156+03:00" + created: "2023-06-14T16:50:46.119233803+03:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -185,7 +198,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2023-06-02T17:13:48.328677038+03:00" + created: "2023-06-14T16:50:46.118119082+03:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -198,7 +211,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2023-06-02T17:13:48.342166043+03:00" + created: "2023-06-14T16:50:46.132470654+03:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -211,7 +224,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2023-06-02T17:13:48.34169699+03:00" + created: "2023-06-14T16:50:46.13163377+03:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -224,7 +237,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2023-06-02T17:13:48.340714375+03:00" + created: "2023-06-14T16:50:46.131160344+03:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -237,7 +250,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2023-06-02T17:13:48.340239954+03:00" + created: "2023-06-14T16:50:46.130701147+03:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -250,7 +263,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2023-06-02T17:13:48.328150582+03:00" + created: "2023-06-14T16:50:46.117613226+03:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -261,4 +274,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2023-06-02T17:13:48.327362902+03:00" +generated: "2023-06-14T16:50:46.117016285+03:00" diff --git a/helm/packages/vector-operator-0.0.25.tgz b/helm/packages/vector-operator-0.0.25.tgz new file mode 100644 index 0000000000000000000000000000000000000000..522956120526a1a077a6d8e61065226ccc407064 GIT binary patch literal 31043 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 Date: Wed, 14 Jun 2023 17:41:46 +0300 Subject: [PATCH 225/316] add mergeKubernetesSources to helm chart values --- helm/charts/vector-operator/templates/vector.yaml | 2 +- helm/charts/vector-operator/values.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/helm/charts/vector-operator/templates/vector.yaml b/helm/charts/vector-operator/templates/vector.yaml index 49d9311f..a31edf4a 100644 --- a/helm/charts/vector-operator/templates/vector.yaml +++ b/helm/charts/vector-operator/templates/vector.yaml @@ -5,7 +5,7 @@ metadata: name: {{ .Values.vector.name }} namespace: {{ .Release.Namespace }} spec: - optimizeKubeSourceConfig: {{ .Values.vector.optimizeKubeSourceConfig}} + mergeKubernetesSources: {{ .Values.vector.mergeKubernetesSources}} {{- with .Values.vector.agent }} agent: {{ toYaml . | indent 4 }} diff --git a/helm/charts/vector-operator/values.yaml b/helm/charts/vector-operator/values.yaml index 7a77202e..96539728 100644 --- a/helm/charts/vector-operator/values.yaml +++ b/helm/charts/vector-operator/values.yaml @@ -72,7 +72,7 @@ args: vector: enable: false name: "vector" - optimizeKubeSourceConfig: false + mergeKubernetesSources: false # agent: # image: timberio/vector:0.24.0-distroless-libc # env: From 77def48ae09e23e2368ac30b05c36b806c0851dc Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Wed, 14 Jun 2023 17:44:51 +0300 Subject: [PATCH 226/316] rebuild helm package --- helm/index.yaml | 46 +++++++++++------------ helm/packages/vector-operator-0.0.25.tgz | Bin 31043 -> 31028 bytes 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/helm/index.yaml b/helm/index.yaml index 6fca0bd7..fe7cd23f 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -3,9 +3,9 @@ entries: vector-operator: - apiVersion: v2 appVersion: v0.0.25 - created: "2023-06-14T16:50:46.130226539+03:00" + created: "2023-06-14T17:44:32.133101966+03:00" description: A Helm chart to install Vector Operator - digest: 776f4ca527828b6fd4f1aced2bb887c8955118c60668c922657ad1aa4d764f96 + digest: fd22b996b071b6d85740ccf76e85cb640fa717c2620748d206d3f4fdd44cbcc2 home: https://github.com/kaasops/vector-operator name: vector-operator sources: @@ -16,7 +16,7 @@ entries: version: 0.0.25 - apiVersion: v2 appVersion: v0.0.24 - created: "2023-06-14T16:50:46.129178592+03:00" + created: "2023-06-14T17:44:32.132136325+03:00" description: A Helm chart to install Vector Operator digest: ea257e60ecde063a0d1ed52ce5e3283245b8f0e2daba58ea3a5adb0ba82d7799 home: https://github.com/kaasops/vector-operator @@ -29,7 +29,7 @@ entries: version: 0.0.24 - apiVersion: v2 appVersion: v0.0.23 - created: "2023-06-14T16:50:46.127837554+03:00" + created: "2023-06-14T17:44:32.131216917+03:00" description: A Helm chart to install Vector Operator digest: 546d202b3b9263f789b88335263191098dfcabd5d8698105f37cad24d56a8ed0 home: https://github.com/kaasops/vector-operator @@ -42,7 +42,7 @@ entries: version: 0.0.23 - apiVersion: v2 appVersion: v0.0.22 - created: "2023-06-14T16:50:46.126896951+03:00" + created: "2023-06-14T17:44:32.129697195+03:00" description: A Helm chart to install Vector Operator digest: bf96ddc8ac61e9d6beb8bc763fbf3fa6025d950b29d70d80de6e8a0ea45e0411 home: https://github.com/kaasops/vector-operator @@ -55,7 +55,7 @@ entries: version: 0.0.22 - apiVersion: v2 appVersion: v0.0.21 - created: "2023-06-14T16:50:46.126018435+03:00" + created: "2023-06-14T17:44:32.128295663+03:00" description: A Helm chart to install Vector Operator digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d home: https://github.com/kaasops/vector-operator @@ -68,7 +68,7 @@ entries: version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2023-06-14T16:50:46.12483315+03:00" + created: "2023-06-14T17:44:32.127361523+03:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -81,7 +81,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2023-06-14T16:50:46.124257114+03:00" + created: "2023-06-14T17:44:32.12674925+03:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -94,7 +94,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2023-06-14T16:50:46.12368886+03:00" + created: "2023-06-14T17:44:32.125695231+03:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -107,7 +107,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2023-06-14T16:50:46.123130399+03:00" + created: "2023-06-14T17:44:32.125098151+03:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -120,7 +120,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2023-06-14T16:50:46.122585426+03:00" + created: "2023-06-14T17:44:32.124482641+03:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -133,7 +133,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2023-06-14T16:50:46.121609024+03:00" + created: "2023-06-14T17:44:32.123918668+03:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -146,7 +146,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2023-06-14T16:50:46.121002392+03:00" + created: "2023-06-14T17:44:32.123341283+03:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -159,7 +159,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2023-06-14T16:50:46.120403041+03:00" + created: "2023-06-14T17:44:32.122739804+03:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -172,7 +172,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2023-06-14T16:50:46.119819981+03:00" + created: "2023-06-14T17:44:32.122072882+03:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -185,7 +185,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2023-06-14T16:50:46.119233803+03:00" + created: "2023-06-14T17:44:32.121016522+03:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -198,7 +198,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2023-06-14T16:50:46.118119082+03:00" + created: "2023-06-14T17:44:32.12050067+03:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -211,7 +211,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2023-06-14T16:50:46.132470654+03:00" + created: "2023-06-14T17:44:32.13561841+03:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -224,7 +224,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2023-06-14T16:50:46.13163377+03:00" + created: "2023-06-14T17:44:32.134679828+03:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -237,7 +237,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2023-06-14T16:50:46.131160344+03:00" + created: "2023-06-14T17:44:32.134031764+03:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -250,7 +250,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2023-06-14T16:50:46.130701147+03:00" + created: "2023-06-14T17:44:32.133567191+03:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -263,7 +263,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2023-06-14T16:50:46.117613226+03:00" + created: "2023-06-14T17:44:32.119999223+03:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -274,4 +274,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2023-06-14T16:50:46.117016285+03:00" +generated: "2023-06-14T17:44:32.119409796+03:00" diff --git a/helm/packages/vector-operator-0.0.25.tgz b/helm/packages/vector-operator-0.0.25.tgz index 522956120526a1a077a6d8e61065226ccc407064..08bc5c3a33b80c5cede48eff5e3e046f1cb038b7 100644 GIT binary patch delta 25896 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 HMU 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 E01q__00000 From c55927384d0d090380d728c9953f40db33185375 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Thu, 15 Jun 2023 12:18:57 +0300 Subject: [PATCH 227/316] don not merge source if custom options exist --- controllers/factory/config/config_build.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controllers/factory/config/config_build.go b/controllers/factory/config/config_build.go index d831c3e4..f5845237 100644 --- a/controllers/factory/config/config_build.go +++ b/controllers/factory/config/config_build.go @@ -327,7 +327,7 @@ func (b *Builder) mergeKubernetesSources(config *VectorConfig) error { routes := make(map[string]string) for _, source := range config.Sources { if source.Type == KubernetesSourceType { - if source.ExtraFieldSelector == "" && source.ExtraNamespaceLabelSelector != "" && source.ExtraLabelSelector != "" { + if source.ExtraFieldSelector == "" && source.ExtraNamespaceLabelSelector != "" && source.ExtraLabelSelector != "" && source.Options == nil { routes[source.Name] = generateVrlFilter(source.ExtraLabelSelector, PodSelectorType) + "&&" + generateVrlFilter(source.ExtraNamespaceLabelSelector, NamespaceSelectorType) continue } From 1f89782f784b669267b4d4751742bb011bfa29c4 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Thu, 15 Jun 2023 12:26:33 +0300 Subject: [PATCH 228/316] release v0.0.26 --- CHANGELOG.md | 5 +- helm/charts/vector-operator/Chart.yaml | 4 +- helm/index.yaml | 57 ++++++++++++++--------- helm/packages/vector-operator-0.0.26.tgz | Bin 0 -> 31028 bytes 4 files changed, 41 insertions(+), 25 deletions(-) create mode 100644 helm/packages/vector-operator-0.0.26.tgz diff --git a/CHANGELOG.md b/CHANGELOG.md index 033700ad..5ef99a46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,7 @@ -### v0.0.25 +## v0.0.26 +- [[116]](https://github.com/kaasops/vector-operator/pull/116) **Fix** don not merge source if custom options exists + +## v0.0.25 - [[112]](https://github.com/kaasops/vector-operator/pull/112) **Feature** change merge k8s sources logic to routes ### v0.0.24 diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index f25add37..98b5b71b 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.25 +version: 0.0.26 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.0.25" +appVersion: "v0.0.26" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/index.yaml b/helm/index.yaml index fe7cd23f..496bc5da 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.0.26 + created: "2023-06-15T12:26:23.850490495+03:00" + description: A Helm chart to install Vector Operator + digest: 760a2833f4c1a33466982419b079ff18d996331ebacc40cf93b0f55229cdb7db + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.26.tgz + version: 0.0.26 - apiVersion: v2 appVersion: v0.0.25 - created: "2023-06-14T17:44:32.133101966+03:00" + created: "2023-06-15T12:26:23.849596878+03:00" description: A Helm chart to install Vector Operator digest: fd22b996b071b6d85740ccf76e85cb640fa717c2620748d206d3f4fdd44cbcc2 home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.0.25 - apiVersion: v2 appVersion: v0.0.24 - created: "2023-06-14T17:44:32.132136325+03:00" + created: "2023-06-15T12:26:23.848340501+03:00" description: A Helm chart to install Vector Operator digest: ea257e60ecde063a0d1ed52ce5e3283245b8f0e2daba58ea3a5adb0ba82d7799 home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.0.24 - apiVersion: v2 appVersion: v0.0.23 - created: "2023-06-14T17:44:32.131216917+03:00" + created: "2023-06-15T12:26:23.847389511+03:00" description: A Helm chart to install Vector Operator digest: 546d202b3b9263f789b88335263191098dfcabd5d8698105f37cad24d56a8ed0 home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.0.23 - apiVersion: v2 appVersion: v0.0.22 - created: "2023-06-14T17:44:32.129697195+03:00" + created: "2023-06-15T12:26:23.84650396+03:00" description: A Helm chart to install Vector Operator digest: bf96ddc8ac61e9d6beb8bc763fbf3fa6025d950b29d70d80de6e8a0ea45e0411 home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.0.22 - apiVersion: v2 appVersion: v0.0.21 - created: "2023-06-14T17:44:32.128295663+03:00" + created: "2023-06-15T12:26:23.845612004+03:00" description: A Helm chart to install Vector Operator digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2023-06-14T17:44:32.127361523+03:00" + created: "2023-06-15T12:26:23.844336832+03:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2023-06-14T17:44:32.12674925+03:00" + created: "2023-06-15T12:26:23.843754574+03:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2023-06-14T17:44:32.125695231+03:00" + created: "2023-06-15T12:26:23.843197143+03:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2023-06-14T17:44:32.125098151+03:00" + created: "2023-06-15T12:26:23.842645392+03:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2023-06-14T17:44:32.124482641+03:00" + created: "2023-06-15T12:26:23.841687807+03:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2023-06-14T17:44:32.123918668+03:00" + created: "2023-06-15T12:26:23.841089789+03:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -146,7 +159,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2023-06-14T17:44:32.123341283+03:00" + created: "2023-06-15T12:26:23.840515506+03:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -159,7 +172,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2023-06-14T17:44:32.122739804+03:00" + created: "2023-06-15T12:26:23.839909817+03:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -172,7 +185,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2023-06-14T17:44:32.122072882+03:00" + created: "2023-06-15T12:26:23.839326897+03:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -185,7 +198,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2023-06-14T17:44:32.121016522+03:00" + created: "2023-06-15T12:26:23.837584443+03:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -198,7 +211,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2023-06-14T17:44:32.12050067+03:00" + created: "2023-06-15T12:26:23.837059599+03:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -211,7 +224,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2023-06-14T17:44:32.13561841+03:00" + created: "2023-06-15T12:26:23.852367575+03:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -224,7 +237,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2023-06-14T17:44:32.134679828+03:00" + created: "2023-06-15T12:26:23.851897742+03:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -237,7 +250,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2023-06-14T17:44:32.134031764+03:00" + created: "2023-06-15T12:26:23.851439541+03:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -250,7 +263,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2023-06-14T17:44:32.133567191+03:00" + created: "2023-06-15T12:26:23.850973794+03:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -263,7 +276,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2023-06-14T17:44:32.119999223+03:00" + created: "2023-06-15T12:26:23.836580664+03:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -274,4 +287,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2023-06-14T17:44:32.119409796+03:00" +generated: "2023-06-15T12:26:23.835987399+03:00" diff --git a/helm/packages/vector-operator-0.0.26.tgz b/helm/packages/vector-operator-0.0.26.tgz new file mode 100644 index 0000000000000000000000000000000000000000..1f92e7a514fc40a6f5914c4131af4c8e17ecaed0 GIT binary patch literal 31028 zcmX`R19T?O(*_#bwr$(CH{95^wc*Cb#fI_8^yCw!Q~kX-s^10D7nF?GcZ7$_|&DtFtrj&1U+moOZ8`)bR{Kc51}LgitP6 z76_|!zMO}RnGncd+^P^FW3_UbuL-RbF0dW+D`#+Vmcoe^^ zEw3KGj|=9WP+(qeZZk0OeQ0Ipb0M0!=kxxduKWFd!m<0SYysOa;BF~8P^_%uqW@SE zCh{SWZ|l8fJ>(;mBYWX(OYhMR&87%J13i={3lj}_25FXZw%zjpsek|6>GJDRUn;L zqO?Tzm7W6CfxxBx;8_NZbQ(h&wNETL@^F z7r>bdJh6HplUJrz7JYsWo%}};y@dPzmfkg^ zGMyY+KlGv#L&>}Gflz=E*h+s7$t#{0_#`|Dy~Fb_F5(d zopyRQ@attemqayDy{&y$Aqg2R0JmUfk>&M3QHj_}4C_!%f~PMtamJYpMj#pc@~2)L2x{5+)lk&iBNznI+>M_A9)S;% zm_N*oM)k(6a(iJ{D)gC=c`-Q#~z;sxSo zX1f8dfsV{_aHO)e(n)hM6e*|=$v=K0B2G9>uE2~Du(O_c>wYMxcrbfN7{^U}j(>t=tpQpifVu%@o173Rx-ecVqpf4)3%8^*owG&JS~3cU&X*WsEl~_tC&nm8zJywTmKXvA-$)7 zsRHx8qBpWV+jNM$U}vF=IHDA3M*a!EvJPxYCKO&YVV71E^JAeXM?Gl}Y%klYqThg3 zBY$8G&SL+5D#u5Y)jTor)T-T6P?}d32FqfcY?`8UQYPa#F15r;2WCk*h{bLD(PLn6 zoxe2`nUDFTo^dIC#?G(TwjW(p^_|e|>vHFFrEz)2g@_~dtR|NN;Ig(dT-HZ8=`wyu za0NtWeIqvMkyHdaj52LJdYZm0rTrkpG<8>zhm*H`jtqdSJ&K)x2X5`~{CWR*(G%Mp z`c>;zClN{U0ql%_4%{SmN%P8dDfOk{dQu6jl4X!hQX0C4=;jp{2FB1tCy_ZITSooT z_|OwYYYjKRO*&@nD%rp*cz4j>rbQGG25#Ps@60Jfrb8@AZOE1+Wpa>qSYF3aZ1L)O zmcGQj*&w^juYogSS1Tt9oiAKFgrLSr7Oz>a68d*EO^tOtg-sn)REarq{VO~+RWGtC zhfY;^c$lFC^4P_;nm8>KDK2XJM?BxQoL*-MKX*k`=iSEpiwoOtFaU8;wIi?fGQsul zccK@**T>X~p&$Er;LMzE*^i2wCw5&=hQORnUcQzgM=c(fQ-fGY;X9+3-|8QR0p2R%!}L%Q(EcfHuQ(iY zR%0}O>v;avTRhV`9h^yh%D2E>j-Z#Tiz!Bl){+X-NQ36 z=(A#?pf(+<4`NEn3}mE8Gc%w#4zWDjgGfY9VsVe?;QNU;n%gbzSK6Gx^;%mx%skuD zulNy7GQJ5FZ!>Wsyc(lHz2p6AM5@1wtg;h^oJ4VJxAX(bo#5t zOh2asq=_k9(d2Gy06yDHezTp>t$H_>blG1trHgjTm5dxkM@6op@*>SpGk(Jf91FJm~}q!%%40~9Kh&z8MZykl?Rk#-Q>cXH3#woDhYufQ|X5BcY!nW z=OZ|l8RZgfx~qyOuv>PuE7ZpsL!8XAh`f?bgmTtt#ya>3xzO}z%VN~rZbRXycu-~` za07fn2~YfI*y;>LM^)>^o=P9>;(#Q>l^0RN<9Bt#LcmlK7g!TF9aV@A5D;(Zl*>)mp!dY0cPZQc zH>X|Br^%h!!I6mYRrWrwX1YUA-%Fdf%KLm7&^(1rX`s=w>hAFnRxX?$@`~aTb1`I{ zWn*fkPwl#p@~m)vo5$SJfEa9$HAg4b{$=1P6$WUwxURYzPvZY!l5%x?lJ^&by}7`? z;ZtiE#~}w{KMC0V{@%z4^mpbv{~nu|H{^y^fzPG>i_!Ue_%5x>vSh;m)+92VlupDj z@yj5AaVoMXs+oNpu4&CHcUA{v{~}BjKujhPCLs0bqiGF%LT{pO-j8QKU1BgJ>AKvb z?}uZrS7l3Q8LY<4yN{bkZPKG2TEAoszaGOf7Cnd#ec-X!_U+F!=Mx>Jw)10L6Un5@ zqr;di&jg^O1jo<-$X~1=SuqfOp9=5TYH%B zP3^Y7tx#8o&x`-l$<)vr;wRwvd@DY%J;3$tU~B2>;{smsvg1Ol?m1J3>n;p4+-`<6 zGinqZRfQKO~>I!Owf4Q0jXrwpH;ML;^dH(Y##Xlg|x{4wgSU6QnDc4qoOoy$nE? z;arOg-R4YSWnc6}JLRzcshwB9VAAkuxgX(?0C!mr419RQwtXWu^cVWi(UPR(VJ96L zJu!zE#wv?A2DgigXe_6xF>^ss4WDoIVwA(;MnQYxe{jytF9H{{Wp`K{*+11qpWPg7 zOuL1pdVE#Ho43@IHNrXhpwpZfXS)-=9gY7}>l+^6nh08!Tv41viSJ$RD zLy7wKD)N}I%Zxf{2VGORUhroF>7~E~0pd|*PWb$=!+L@15iynMLF9(Tm!sJ!AxR(4 zf$;KCPo1*#7m%T5=qrn=y9q{QM+BL0Z*Ho#-JL`E+6)ksg#x#H<|({(B+AA7tJ``t zS&H}eo>D6?;F1#2=ITLMKZ&9Qe%Q6XVaF*K$lPsnAngCNWCaXNd&X_=Q%m|;RCXFB z9WUQMTb(bfum)z%(>?-ER_dEq;3-d+KvN%cqDJzMLN*u+Ht(zzY#eKZh8i~QF~~|K z_1txGziS4C96ol|?az^!lCU;O3Gl)yYP;Snj3~18}B$>aIaBMSpkaW@jS`A%4kW-SfQfVd@R^W+-xtzN@YOT_qL(9!tAahT2!>M zjDOX7q$UGQn}4#+!@|S3o}Ve4|BW9kWB;q{RxVH2u@5*-Rbm#Wk&hyv&f=>H$xahU z=d|2`kVMY-3a^(;-1Qr{@P&ztp!Z*R6t%R0<$ppCJ;1?&q2}f_SEId_O{~BF{YA&q z(dXhFUg`l`X1ma%6;I7;h=TtDky$I7VT-zuVXJQM)uWws`jrNQ_^z4g6OWF^9WlkD z3}eAr6hN!p?bHSvLYS|HXoweZU_fz#!cR83K|4Nm&-*=kGiKL_eF}1XurVW-Y3q8oXEH%xT#Y^;{?PW2b^9-57IRq!C*u28f{4r6dQo-;%&_x5K7ub_<9JiE@{WmhFED{7XYz5J_fNZ4IWCi(VFp9`bN>NRy{z>p_^`%tg{eo8 z-LU7WG7X(!y#;37Z=FlwZT+{;vkg5J#SCYF>6DU$R``X$u;I{`vp{C0p4a=Vg}U!9PRPH2 z>`jJsUoRX;!su)EFo%Tkn#BC;1hrlm^kG{&V$F3IW4yT|{O85yL-*)$I$ z8>$6a!txq2xZpD{-zDx#pE5_)_G0Vme$NGH6fu`fRI>t0xbc{hEl zYHxJGdAZ{YFL)KWQVMy)btb+1NPaO6N)~bjFe3V`%eM7sRk9K#KWuN;D06$=}j(aCcU`j+%tVqHky6N8_5jcRChD3^i%va_wDr@JQA*L_{w+W%|Z6^ zk4!E7^(nDB!CHVZ=)yXM-7F#7MoNHz$Op2U zSu}7@$65`*Nj(Lv@joWnXc`=e7`5_oiy_OkfLzR1E6S@uNd-YTDyB>4WybP>k~QJ8 zFF{#FA%UZDPW;{(a~J(zgu-y?)g+z{`#Qx$YK;$3XI#nHklIe(v#M-<0?#j-sJv<7 zDiQw{N0;aaW?$g3+i3mxuwai?QT%slfE2W>&9J}&(v+ifpr3SDLD&#NH;+G+qP=*e zzrav{nrcB@r0xczmpTp(3hmP0Q1k^_;eqrX-4POTSFceVU3wP5M;Pq;G87NLVfn)o zHag;C?K$(b%Yt;__>Bw9r!S;%iJphxPIl9|==?KTzOikWJ zuC_Ubt({8~d)BWFwTT72W05};=+lEq7C{H;1Rhibs#OvpHAR;;qg2wr*kxup`K26N zHGm!OB4HRKr%C8{;qtGRs~Y5wHH+)Os$Onrrlj28jA^IYq}eB4B~(qd>(JQ@TeVH) zYDCU-DeY6P{B1649)54JQ*O+R5U2Ms4X`qZ>JQOD5*L7li<)U* zGKNi<%zdblstASM&4R|VSRC`##X%GW{YtCNNS$%+_+BYj(Z&ttOQb+0B@AjLFCWBT zi%UK^%l>t*@dOav>FWak*fF2=6QOaRqH6B{NQ<{qQT2*ov+6Rwd6(+)#_AWUc~>v7 z)u2QvPu3s9WPy^z2Y>ql1g^}leEUg8dh5viy(zTJV>Qa%HMpmnUIckuC&*ZjSCjkw z$3@HEFTZ7LTe^G4JRY_ZlQR|9-^vXj(~$=}4PWhB6b(vhy3&s;8Mc%WUo_85B-mGU z%6(H@+&Se^&?5iZwNhBg$q_dphj4?UEl1;#PshB-UUgeL-VhdFm{z?Ak!hn)?o7$> zg5_2cEg%I3*jhXtgJ2H=_{!I1W}z-XdFq*g^u;TBFUmlr1=f3fK7apOC&=M z9Rs5tgG+?Q8*?!@;q_E4R`Wgjm?`VYoIi#ndeM;QS=sPySA9s0szl~j^+6Iq__)2Q znP83(t6gr6)1!#g#l52XdGoiYCV#JR;p<^ocG1A**3So3FwnKaLVeDAggy6msIptA z*S!}O*GRrs27|rmp}gz4wCS|yq`EG48Dob_SKV*JN^xyb%68mJ5x0i6h|}Da`MW{= z-4e9?o`el49mkZXb^Da3|ABAveO>1UJ>@BP$@O@HvnT9NHGZd{p8%0KA(ssDJ#IX7 z*^Rlt_1HG}i_}a$nUJhkXvuUlq{*-{jZ-CnXXMl|8#U*uSUY~#npkVsMb<^~RE6N_ zuq4Ji2w*nS*~`j&%X<%&j-%AY(myuLA-cj<&hy8l6?mRXd72$wHchI5zilTA;!)0( zmiv1Z!ZP8tlvn5^wv-Vq|8tjzigjE@XbwN4h}YbC?`s`tvr8i%2HyYeHELd* zfmyt(rCbo!UJUP?ykL5wXzg`V!JbmG5w1w^;2S(9k>rxYhHaZel*0e2> z2JGxef70Ct%+f2ILH5#|>o+!ee$}oIY^>6>=ysA~NiQ1knGO~jG1NbgfU?guYJ04b zK+oFgF|Ne=f=77hFtuEsKwX&EOo%sYrbD{^MMV~jB}GPLKQ1VM5^UUx#I;xz4~g-m zS?&EDj{7#tB2J9QgHJNtRrw?6+18z7!1(2**WEB()cvHXI%QvBi$R#46guvl*|;92 zv`xh(mUVySR2yFBiaJE5DpxgVvD>#7)_6XY2>*xSQ#+&T?=g>fd8S5+(pX51e1LEL zy0C-OAPaey$j6GEVRW=D!iHR-@xG9}rWNt&rRU%x651+s)=-GS+VpCyO3m?IZK^V( z&p8bI2maTDnq1v4C5_~t6Rq|XOg<3lE{}IS$gXk`XU=Mn(sR4*+q&$~V7N>Gg)&Kr240O?0zjjJm zmt!8%Yr4 z2g@&ZsK{i~EqjxsjH9%+7W0AYcG9<0>&A{wtEj;k&bH*I&1Blqq1e`xu0~LCUXt&wOe{h(^AlcJI({GmA z2tb2GK%r8d6&GtjwT4!wS&{T{h-;2(E6(KiSN`a@!x3bX-NxZ>N_{g41$b&2u$20Y zWyC5jwnWrEnM~8W?PO6*Vi!MY*y_Wp4ydIc%y}DqT?{Jx&GK1*LenLum~2f7f3o`I zUB=A5_mQsn1^AU5D*SFJ7DP=g@tVEZadPK3JH&+oIK7jdjn{Ry?{T>;%3mEo1+DPK zAqPvs21E0!xJt)@8{=)jqM-$W8xvgsqx_*FbNZVyh&>x!ySGt>Vyg0zt}Gb5CBAuR zQgJXK*|I!$-wM}j)5*c50b-MQgwKPJIx>j2UJ5StN9=ewd(IbO-#!?v|NKk%+ABI zYQ)8AF>m5mUw{KA(NZ)cBQbf@Qr$1PU(SS$AR+QjjgRV*a>!9hUo@+lS%A!vI=0QV zjhlr>C{l4sl(mKOU&?zUkGIBgy|7tX3x=?!#Jde!>soJjx9EI;@9bk5m-Cmy$=G)8>;024oQ1~`TsJ6#Z^9oKHi2|y`0|Bs;E}re7yT6rv1L0KK?)Q`(k|WDcA!86(0?;3Tr@FeA}O*IuN7U(ZE3j zq8jM-F4=<=WrNuI1%_(|Gk_dp;@=LV2|WEltf8<4My7RvT4B)mQKY#tz%3BzAzkeu zRrszfDxDzhh*61s zreLa_hwKOSZ-_7KKE%;O%mkVws}7#Da6vH&~YXPiQHzfIH{*|3UA1 zEvf%cixGzC+o&O$=7L#tQMkVC8fE@}W)Ji20lux?t&8hFr||6uR^RB!iRy}Cy*j_y z#oPbir+|SKIg#Vrrz2W?3#$DF;`jxQGy3u}ddi9@I@zedsP~2V?FPX``GDYPCsGpP zrh%cE0rV7=gCh)EU5HcU2L7+f3o>hGUum;#)QpumDKmrkzL?R@IIcp(jPEnnO8iSg zs(K6hOygoVf{UXN@vYGi@>@K~PxcTu+G$|_-+Z^N{l62>TORx0 zEY~%uY7w~~scJbit7>g;9gwp*O5-PbOPd*NefPH^^7T^?iJ^(v&C!Gw1(TlRrQc|6yoZqB3>(uIL3 zK{%VeIAJQ5KHC8s%CpF#sTuinN|Id0CF{yt#t88YdbCaurYdTSR-wLikg`ShviKP2eCQsN@j{!C;`Ehr?m$eEb6jo5#hpog$Fo6u+1 zL(Gs5pP*E1kqebpFQ!XgPzSTiS8`gHi9&NmR}}4R>ln>v>$v;%>()TNIDgp@AG@A*qWFlSD!aaI-8E(SGD5{He#t6We#4@X(X(v)K2=kW`&Kjerjqenv- z<&bGDcp+ki$OJ=S;rm${8v?`85`U}cZ}0nB>S;@~m9MZ>36OHia#$hgK-9YW6}fi0 zS+m&$Uhcu?72hIZAa<%(p*&n=Nb0cgmf7P8?^V6^?l+hG>d*b?%H6%Q&2&@Wu_L#% zq=iRd12Xo6#B>J^v%f2XFER&nHZm*(yM8=r#%sX~nc-ut z8B3y(wc&^rdQQ2x^M+xrqK|8pBI=HBKC234->3>zvrZF|$u*u|gupkRPimhcy0XzQ zmQM_cGEW=`-{k#-48Zxh?b_tvp&49;_*en%?467NaVZ8a(xF1)gbgR9U*!jG5bwL^ zSupE=+!N~=c@)OA=WP#p6c*m={l+^aPSPbn!#BJZc6!usi}f&pzcex8Y30=OhdMvq zP?&zS&On8k`X5VE^^~W;&1`IwJ%0^occ&yEAQipppn7NfJzNJm62!i%2iLvh*chRq zg5zfuA#DnuTwk6QrXy10cE)P&dpqa~n6zvD6ePLoWK3?E$7a@YIF4XJ5p|g%O}yB{N5i;+~Bmhi__`!x1Zbxd_$&xv(%#SQuxU}03?mB!-XC` zS08^9kX+6St$$bz_Mqma>s~$vCg|7V`&s$-n(fwEhzPJFtdXix_mM(K1XC5Fu{Lpw z(SI{c^C;n_Chd`26X?5$;AdN*NR?N1=(nQ&zT_Ya>ui8`A0`CvUbH*j`wi7 zE8az3M7KWtH>z1!e*m%C>Te<@B3onO-moUe=5AE;p90X;y=MF0qz)Tcz5~$e0C`fWrTP!;@oZF0ee8{2O| z;mfcMQw;{%vm9JD(e_fJ$c1aFrq%F>f)N(VHUGvPaF4Aw%%}dObr~zF$jqCY4HX^+)?c_5 zr|UY1f*Vi~IN;3jr0M2Hv4R_)wpWmdos|oc@yAq8lH6+~0_Lo^4JcO!rqi55P~^+2*&nYO zuf;$WnaY<^Kl_&jqQ1bk0<&8`D~=rxn)KinqvvKE>FNL9cB}@&)HA1cW+6)_Otg60@Pc*U3zspT9E? zZ>52$=h$0D!Qf(0=XzymYkpqV`5E>q-QkY4QjEZUy)T^XvOCp(7@mi9FSkp`zlU)T z%XWy+EiJ#^P%+Bt?2)4uo(GWc?eeFDUJYrvlzu}K?iu%?Wp$o#3crH%6Xrg+6Tc>u zr`B{G6XWAsZmA0mZiEmRxL{aKFV(DwBh8?L5AGp zU)vphLDo=}EKEPH`o<8x|3}>%5w%_mZC~{foe|A={4V30UER93xmlM7KoFf%-zs$o zF6@DQ{93qIHA;c9MD?;j;b;H@LpaB;hi?BI0FCQwnhZi*0|6zp&(Gmu2knHthzTki z1-l&wnLX^XzGiz5{J*Tns^x#dQTun|Bl4XVE&pG9bTRdvCi$5*l015NG?K(%g$DA# z_y2tl>vBFbj!LJe@=y3Yj}&3iK&>;G``Dl8lJyuaxEVE5!6WuL81J%1iWNrgOoC=P zoQ1a(>1+$E_GYXe0g`xy0U?Hp_)I2O_J_M*Uxw1T#bY~V9U>TSV7Tw8)-biYS$9VR zb8J|Trg@wlD^ZlYLvZeTlhLv82(YlfDE@VW{(4uKzY=}Apmsvk*h1{8(DSoyLg_D z=2)JOu9EJ5rW)pPzMc|5-Krf3kQjeCKEB zYyhg|&PNWaW#Hf%vGa3|hSYzO?}lTOW#5|Pp~L5L-?Jdp20;|GYVRSk*yamKWpc(R z>WiR^jY5Fqpg(dHB~=gcemC?G)&KZRdkf2(^dO*?i|jf+;v@Dy=H=~gKR5wzLvFAg zk={}e>*Y=Q-NwJW7h8`==iWbZ@8a4&`j&oQpEkrrefd^Lb^cx&UjGHli~koJ&q~3I z$>9H&DU%k*Vuj8H&pv&xmj6qgPwHYKKH~nb!`hSo)0Wr-Dw19M3P`ATl4MD5AwX)* z6v9S%yD7T>=+GcY9wA8*dsa@){N^0Bid+Z17*m=T5#dF5XLrd!8`iU#X2HiK4Oyy3 z2WrX7qxh4k;0%U6HuuzAN*5!}?27GI;oTDXy3g>Ks*@QZ z`zY;PFmDNz9EEJoktmeX!pkM*L={TpeF$?L3#D*8{OI)d!R4oDDMCl;=XVK*uyZ+g zU5h(E9LqUt6{}<|c~RUevrc9z25M5fRL)Vqi(83B$kWlp_uxyCE*uGLYS5aN1mwG7 z&mkqvkTN&|X{Jm^%!H^DKgsDrqdilkI8+3xS)QRkR){~Ilj`bJUWSK=@YOvb>qy2; z@I)htaHtUs)z#6{-`9ql3<`eudr6i(Ek?j5=W5s^P^-Eg{ovvW-gZh3l#ZK2#K5Z% zx&yQJ8*nepeE{*@K_C)Z8*U8L@-}+<4j=}3%V)@=+<=ksCF`{9WmeZlNXmaBzHQYApFs0)pc7`;h+1+TaphDby z8ZJ*yNSBR}d!}lPBy}qtPQtzZPSrj62f<*M_g7>6K3as*@G*IWKyy>W>g|wB0U2$}0-V-Go6=9Zj75nG>=};kB;kaXm%|4+4SK3c4Gb-82MXJ7G$4!z_uujR|VRBR&=AI60!!zN~79 z9S*E~W|!jC>CIR!bCag#6EWDbYjj1KtAApY<%yFG=vpH?Idx5*w(ps%TG(~h^Zexe zQQbV=IJc-7jdAHW%*fInBVRDyrq24JHQt4JudIekTpb~t2zs`_LgY>0+%jjinM~}d z{EinM>`@Z0T5kM&)-dydAIhgFR4B&4vSw^@0zy#fVLu?h_jxr5MioyQzNQ*i=Q%V`?mGzAFDJC zp9p^F-jqvSEStIQA!YaPTuBNlhIJz?r=9WMLPwW(5%hWzEwX;yy+@87!fZz`;$!xc zNRfDXu%?EO{mh1JX$cq=5bQr}`gObwW_@{6AkI_+ZMtv|VtG6;qzLTO=okyx66X+U z{&W*>U!$W3@#mfpnbg|+3qDrU3Sg-0{mJg+>(}{;3v(l45Ka=I$h&&Nl5j~k4011{1-t9ZpOZ}T0;W))jV9Iq>eKK*w z7p5!Xuv|Da%0`fj|HK}WT1PnRq)vo1l-)@`2r4^`PWCslu?0QHGDRM*o+$rjh_R#f zn|06gq);U^WQ(`Z2ubc#CC{#Z$T>0nA#sT^IKb?W+;tdBtXC3**?cNqtZaWQBPzVW zz_&PIGjPy%J?B?@&5dYRdt&|mIWf~a5(ANSifDO$`vw)V+<8CD`IAuKmIap)LYyuC zK#D$8t*q`OfA{q1t@cl%#1!6Atu`M1NIa~-(EN2p<56S{d#Ew!D0*pbj zc9@t@SVnRA42G*>%txOeHMd=Jd4iEM@wk;ElyI9vR3U^C|Mi-FnSwr5vtp^}3CI4! z=z2L>*IsnrAb{WW&Fj||KoAa=KvizY%BCM;{*{-7jk>}e)OI7k8fd7rO;xld@fkEs zp~=R;A`4#*4D=l%a~j@Q2`-r-mdRcClfp5DO?;&LY~~}-Kb8c4_LE}Kp!~V2$;h}m zfKZ6NR%PV57{QnYN&5kh!cbGECQQWzFVq#ko#>W^{mPsDONvHTbuNuS@cuI(${_2P z073}4%5bkJ&wz_}C)9pmoqH*Gkd3glXZ~4p2+JsX3i|^6-z;q%quMqcOeqPg8q^>;>T#s!hpltIXdQB1?7KFfr9ELm+EVvDM#YQH3^5I`roC&V0 z(cmTapY2?**s0M?*MzQHwVqUx-NP9~6+iFAHoPy2A8B03q6U}L$5F;ybF;JwZTC5G zD7Xc}Sx7{7RDgWt)|j;wknkU9!l&LSHrN+Gkth+Dxx*~>e+6o2JYo zSN?X--0lkfgqxK@z2Fv?0V582k?rR^;xV2yGK}wSL~54_vMwDxCMnEX(q>_s89v&y&q4IM@Es9% z16Ia4;S?`;aTj#9y3zS@Q0xM1=3>~y<39N->&Trw!mc7ErQ35yY5$8uMl^99bKQ1l zM}W7#`$kJ!XLA~RTNQ5L$M!U<8D-TK+$E)QevFUdx9|*KMv$!vh9PK z5)lovL_OV)t=ctb2$=#b_63nudJ35@k)mr@=+w$|s#>leC8mcGw}*XnMn@9bAS~nc zg?Z9A2-|9JCUa;FVJ5NZohm&~9Xovz#}rIt_$;-F^uA|b)5pC1fGY6c5fM%nW$oQ+ zCPNfoPEbpV%_Bm!!yo-a4yBl;z7w3U}6rari~v<4e$ z!lG;jCnD-v_?(%ESI4P|sr^K>xAZ&B#*ME4Wgh2IQlE^6T$v&dhoJDFdQx-!jt7xf zBd4D!8)|2_=e7QS$I=tZ;k*U`OxDbYkoItXu?;t)=V?5sHoB1*u#t9YZCD!247}+I zgu!Gtk@!7Ss@59(A^ayHh}fagkXx1j>6MLF;&8YPqN{&e1*lM6T$r$5l_Lnhyn?Sf&Ov>%EY?4$s{OR@Y9M4O)7 z42fbLNGDtP0DZrwJGJ%$rR0-LULQ(y@9{fXVQbgQMdEo=lUNdl^4GcYh`H6MUWg66 z_Aa6RWwl*d+|Dk^du;jyreh~2Qg=xiR8*aCuV7uej0`-UaJ$a@@WrY%eNZ%X81t7q zoo?PbM2Jb8ZmUHL=41o4qT!+>AUsDdQ`IEMf-a?%_3n1bvwq+iOQZNf_9`KhdjQGH zG-g|W)Wc!?5?Sl+l+(WaR7j?kBwJtK`Dc*-i0+7?1JS&lGrdmZz6nF~+Bg<=Fq!PB zrqPTNu14b@G-YXY&IBW?e|~@6WIVPC1sqhgM8U7ba=p;P;U9zW8JP2-n&B9^)WmE| zBD{^M8(Dszl06UFEy`%sz0EZ!jH!(x(c4`Z}t_6x(T1rYf%+dl$T4(lx@_fo74;g2VsZ$;q@+OevY~V8WH_4X@+qB zG+Rmtp_M$ElQhuHJN0wrKwXvB_gO`S?L+3@%GRzvI8QAjOZ}knS9L}5e4r0h*5t(j zr!7jX%QS84wvIDzaQu7iUUXU4?gquR&6-h%hmDMafg;r!yHjUt0 zK5XtjX6RqXv;$J6R9rqla7;At83Svruzd75rhN+o!?~LN@w!wd2|st%Dny}KsPh~V zglX5ATlp5?>_7B=yM3`tf13T{oMmxn|DhdO*+EVn^VnaHR(4G9M;lu*UI7+8eUb#L z_-S>V{gzi4V>uqZ1{^s_fjhia7|ENN>g=CvtIE8yFaug#L?KmS8y}uMyI|^#-wYzH zFM2$kEhSm443FPoE|z=(W2(!hUT%0%s7&KNBwK|K;)<&s4@9BDYIW@KrI23205zrL zZ@C$zF)wTa-w2tNnIqa{3QHbuMKdMxJaYe%BI2uPnkC2JIm&eW7zrkeoEk|DM5wh1 zFFP_J`Bp&&b0y26<9#T(pHW_!_k4$OUEm6%)|;^%O+hBon>r`S*Ww(=U@NP_D=jEW zy_Gf~xaf^49Nz^>S>BBEM!xQiBrv|?UUn$ZO>gL$OCn{Sz;BfPm2_2i$_*p13lS=Ja% zq7-~)hpI)*`J+Y-S+=8YD_=-%t?|d>9tSY~#%gUc4kiyv%;%S#EiKSe&ZETCZwF>< z8ILFcX3c#;w_QydeTt)hYDrn_+r&rO?((baem>Q;i$_G6(+u5HnrmC;D_IL}pjm&F zBgAZ5)JLj@f0E3`Fx-J3@siwDK)-WF4zn%3{A&B0?9NPVYyYs2u(vW@yk2m7L_{*) zx;b|~qosICh`cTV_65I8Kh z@-y|Qe`s5ZrJfYUBO;BvxQl8%59k4!jWPpb{~{0j1(^%xltkw|+utk0Rhfm_zd!YH z0q98m2|C;c?auY|xw#Sb^}E9TJE+XuJ6=tYueQqWzNlt%GVwMX!M3eODS-mk$ATFC z%x|dI)}g*G0>{E>sMlvY!}tGob0Z9g*|kf`XT#g$f7J^VGR9iTldsQ5G%v2@g4Y%-_({{m+Yh&V@y4i6kF%AW zod{vhGeVn4!QJlI^J9R!eT{m%1w9IMBaa2PSIG`}Ct|gL(>1SFS!l1S3pDQ?G=0vB zy2K%nFv0gcKje=}2h=AbspzUN(#dii)~(OrHvYet_VtJ^Zj+nXRbTSw?f*f*_`eq$ z)qi~3b`Qk+| zlGN3xcyCc8&2~|feHd;`NbWg zqxcpG*o#?}=tt&YR10 zE1NBB8#$`wg5rM!!eg#SUcIY@;tr1+%k-RWLq1((PlOCVIlmCaK{nb??AFd$gxQ1g z&bp-t%YI16qhZVD<<4?=ujJJsww^mlx-{f0&C!mb7jlQh%RJQj;DtVy5~@>&uFyDS zt$a-v!-OCi0JsK&WFnV#Me`SS9tQ)An#F8LON3HTb{*k8PdMaZ-UkGA2yH<%ZKnbk zBl@$DL4U@w>C&y0PhJ8-02lEa6f);TULR7mZruu0fxx6PU?FoiK@Om2#6=SduFM zA^ule`7NDMraP&B)-HN4QLN+PVR={#|Ag*sJNdS=-Yf9dj;@Z!uKIEyOo$6-kQ~*U zp;(LEtb#Z+VZHzKoWWSHekyQM8Au#UY9BWQ)RO&F&>`?1#4*1R^6gY1)>WuIv-WHNW}&E+2x;BR`5GUIxiGd zXd*3Yx;=(wSxd6LROdVgwDQB4YryRX8p)$6`adM++ImVBI^N41TSrnISn;LFP{jhB zqC+5wXqu9kVo8$Kk@ek(m5?Ou8}@FaFv5xjythE)6`R%uzSdST*SnKP8xCCZRVvKs zZWs^CuqPiDU2d|!g2@@>WTDQjIdLBsyG5r^zE=8@kF0mYV~c1BR|)7@lyyWZK^An+ z&hZ!c-dA0$>3dlJI0#ybAHKH<)^gcnZpQ9VCm&f1e%b}ayAHu?eEF>!64G#nDxJ6g zjwtsY^g`*YIwDgs216DdoE>b<@%11LZj}RZxiQxEi*=9Vl0WM2;pXifXbm!}tUCyC zmXfzhaKM2D?2X4Bz$Mji&I=YBfwkn!uUj2{!x^d4bl)ui81zt$TRUYvkKm)iVG_~?ixc5 zE>cyEsnr;&kD&Hb{@O&xqT2qeRRLaS$D{g?7P7#JV-CoNC@BPe*!KLpaU9EAapMzD zhxBNK5)TpPm5IVF#TdxI!>qQ=bg+pdcwt5o4(J`$eVFkL&dg9zr>X5il$AXZ7G0-5 z04Bu{I)kH#2%(45i1Mz1HEtsX;YV?mU?FMrr-VoMcarA;5MEtxx9YB)|8R#cskram z-xapBF|F6FfVj+jF{qR%#nF@PV*mX8f17XHN*C2$hc!3_Vwtc`^nxc0!J407BgoNf# zx#^l7R*~*TErr$8YMMHPM%O@BA8?u_K1kL3Rb}(wWbPcq&kIUEWR<3=(cCm@C=hg{$Tt{?s$I78fOP*O^Ea2$1jf!5DWg^f? zOdd`|j9<7hwpBL`x@2q2SfKqE`%`phWu)VfIjHE&d0uu1a{EsA(IL;_m(_)t5EEGF zrfG8xz+7_VEZ$KwD1Gs{*XHu6JK>|hpta3q-?C<QB2z!&oCg~=Ls{Lb zA)ftGK|!eG`d6+m!{*Jt1&TDIKNSRBUzUZnNG_<5ef3jIs#M%pmps){!Qpv||Hc`* z`qQ21-cfxLokX8^l6RCgV`tPU$2$^Z@ZXA@D7ng8SS#OX3ux+dbcrPfIZboAeq$)B zE^j4f%_xfvgLH|}x_Ot9gsW-sn#X}cQSC*0oj{cPh5j6+9uyIJ!x=e7w*<#XaR&3} z4>^^@$^E>qOLa7SN)|N+XkW5}t3&BRG%p-DXUMKr+Gh!$A#+f6IjZk(uY(8m{q40K zllqg-8^iIsr=DUs0|fI*E`QmQ$t#h>s~8GE2ejpSha~O3#7aqf8X!aa2uh`l-Ik$u zwHEi3IV)>4xdA{2kPn$Icu@HJEI&eR9Npz$} zeCAVyxk0g;;A@EEiVgQhI;_jR4%MMK)FxYIUq$!<(*)0GLgh-yGDp)g%FSna1x(q_ zxZfagu9W81?8fL&eRl#j>0V>1Znu^YB=NNb!t%>a0^g2gGe5b(a(w4Q-``&EM7DV8 zz-n2SEz}#GvITr`rwf$!PMQh%d@k`~yUiE>7B-(PwGb?URIl0ly3DV%rN7+ieW)sN zpm)lh;eV!#t(I%l9{A3)P?87B0S*>EUi+3Eu4c&aF>)NtfbRJ*gKF~oAG3YlmLWtW zTh9H1h1BCO@0u3y3z99-%e(a>?(ivLsfQu7&HO;1gwBZr&()U!q?}R7bEloLDNfk8 z)J~|%BfhSEic!RqghaiSI&9-}Jvk-W4m$!dcBbk5zkrf!)Y%igFekTH=a;XqrfK}y zl0Qx)pA>>jsIxw!N3A3jR@}{}W)d<0SE1%Xxl~j8))tRL-wPf01U5|V^KlL(=Ug76 z9Gf~t*qBg@B++Xib3%@5mO<1LEMFh-Q%O3N;i@t8HMz$*@W$2TclGSl5cV`1IP{it z$uwwf{;T(zD{xo;+)K-b4Jhmf7zG$(#F)$Ot9{$`DQU&pYWq^Mw0cve5D1 zBHHC^U9)XIE`9wz>I&S{gNf>QeW5& zr`lf9!XT>h`c;`*xi!Zzhp0GreQ&j54eo^Fl(J8b+xW@MR=nC;7N#ujUy+t68lG4y<8DO+-X99>R+;r7Q7MGSNMlWO21NpJo6$x9W#2VfbYW2ZbtNK+Ql*kja8 zmH>=8hT24IGm~}rHY4m#;^^M#2^(dYe|Q;Y%isgM{V3qEnNXQBn9^-ygoFD@Fw^Ia z5zvqTud#oT>hg`2yi$H_402A0xr}E;qI2V@a7Qiq@(6R6ga-uFUTm_;RDA-q7#;Av zx@nx%X2C>O)``?rBWLBE7vSM#h({8el-;tn`|0WFDTscj-+c280_cjEL^Q?yiGirB zyHpYaDqp>LF-8Az{`wV!6=zatST6m`ZZIxnf86*`kJ10Sd;7+2YSH=@G;B4!?(tLE z!ZSPl`pYk;=#u78i<=TrPfN%)N2gZ}=R||tK`yBk{d9>>B`6qlLk*fd^W!6&%@Z0) zjoYl*dRe{!GJZ`?z7b<#@NUMQ(0gL5EgqMtP#Sd5#&;bmS-s8C(JC#wn`zN6>9_<-6(FZ6ya*cx>M zS%JM!w(x zJ5c3$xmV4KZ|>O&k@TG?9vg;qU*UO}@t9ri?j8!0j1f2pec}YyI(d*9Ipgh6Im7g} zQ`#^v2MMXwmT})+y6srV&@EKd@Fc6qga{m-WUW|Y#E$hMzX5Ufy48itCRTe}#wcY7 zPs+QMG;|A-H#~{L&i0WybPJI^bn}-$bo22r>()5A*3GP^#ISqKv3-EUZG%Mtn4YZ0 zsx+vsxjHdJcqVw#k8IZ-QbnSE??4BM#**{8Ol-5nYIhMx3HrkDrxe6ieUxC`CTik5 zp7PQN0xTWAVGP05L_RrFRj8TD%LL`U8%i}iWDf01W#ZK1l9+gon$+kvc;-m)ED&j} zfah0B&ey8zx#5PH)gnRttvZ6D!xAmO46?gslH_w7k)xWmr+4gC)qWeOZ1Z)On8k@n zx+Xa=Ch|d&oH=g$i@R$S=k$T(0v-N|D8)_V@AoGIV=eQUg;rO`w zXo9_XZ75_77>~(KF-xde-VJgTbrH~kilu491;2-kqHV%w*8N3-FhN0HQa72B?|h;k zl>&!FS;U}z$T;LGC&VDEsjuEt<9P2Up=;#Hn0(K&`g}t zkFn5E4UlR#OEnb}?d7>$x=(1X^tqHdofXo31VWMI>;3Us@5U6jdk3-@5r%C}Y&p9C zYn`zvl+0nB8JB{KlPbtG`lSPbVjdMGz&WgE6^o;dHh!z--0Gf5RM4+&MfR9KvPYc9 z=Qq9itxd7AdZ+GMwI3t(Ii8ssR1VK-T=&9$a-P;Mlcy?;GB9!g)w92`_BYIh`2axrRp$9=8OJU-I}9ifb?hHXDa37(Ne92t!e zzFwZZ7NG0tJ_wsU_?1$0_kE*{>Zjkf8jFaQB*pOOOEs50h>2Z|3Z#TqNi?_>$y0&& z{J0S-yZ`XQ!#-kLpkou+P2~bfZMJpDR5KrXL%{``=#muyv(X4l&}>7`_7e`@-wA<0 zbespRa)5%S+i*2ax1o;eO|q0xbl0eTZFI&^ni2WHv?#xfEd#UD*7uuHJh_UX0!Ph%e5HLF3QrU= zE2QE2t|CUw2L4C`F8MPxhE7z2C)yeh82T8~ zd>47yDT!&32ErV{JM|fQ1n74IEANM}X@9yb1A?P_tsjOK-p{%LA-?S#3l0s~$#w(=D6`9qL0PC_@AK zJ!})Z^?;};wCc$r=r z)q&3;D5Wa7Qr0WCgY@ z#5BKK*9L=nU%UHN{AlCeHMXo)ENSoXW-EkksAGA?ZLGq((mrPH<7gGRfx0AZ6p7K@ zv8B1GL=*KNYdy10;~AN|t^-RC%nT-@E+z?)kN`)@Dn2@BEaeX*o}&NiH)eistDVyM z8Nx@6gm|HKdqzmCQ|uCAf1;OWUNCWtW=A#x7mhSj8c|Z#Sps)wPSk`0gqf@0h+sGq zOD@44gfkpbxk56>(Y<;d+`Q1uV-y{k%mcSnQHAJM{7Wu#UM!Z7?4~yZlyf(;edJ_o z%`Ie23Fka1QlJ#qE7ULQH)gHB+6PJyP2dwp{1ey_9(lVT-m55n(Z}LX^Ad=2V4BXuG9BfvPjO2C~+`PXy zMs!N1UIN_B`-_s4y^17X=ro!#3k(ho48<$4z3NFx`!9b`lc3)IL`hj6_pCt>Q0EvS4*Ugh{2(K{;fc&{%U*=zC87YTZ|`jBbOV z;lL4=gn?wh3|2JbQj>=xieY{ZeN4axo8}lv&VkoyK77s)`9KALUFo@o<-*c$m8_7Z zx}|ndiHWAh31j~~5VD&JX<@}y7-^?VWc6l=|qY5C02qTW7C1wj!B3-zR zP&kIY_g$$G+4)o}gy%|&pc0m9rUWPJ0`=*twL>3Q9z`{!H0izUP@rQfB@)M2;^0J! z*SIViYJ#<35^F(RewJwvRjl^IGg?O#0Y0>}^X6+wr*ewkaO29?IToN2;3VOXB!+I< zU(^iQ6F4C*Ks%|Jx=$QYkm+4Xu)!Q;Rpg8vIQPV=~F%5am2c z0%8T!QW$GaE6x;ZWNF{*F{1tx3RhC8GBWa5%C=Z&aAq5jo)jyjpLi^JSkYmB;H4aI|w+(yu6c3Tj@?05LfDx&n@AV%l8KO7YL&lkArkduF&Ta_zv(%d=HAO!x z39Dc3b%)t;7{VQbB8=*kf`kC37*RwKQL5#x5QWyQB4av3ZR*kpvZMbf#>MXA|D z%1!2f3gnWkv`MNdp&5u-b!E?RbpMF+*k9O-c$#5Jb@|xiMmG+t!c}k4lS= zAZ5AFs@wkJT_JgjC5^x$N#`{$WU4(X>cEC{6PO#AszA6h%K;{koWL5%M~-N!Gz}lC zv28J(m-sB!Z`+*7<_!5?*S|+Ufb+jbP1{hDgg*-Ct~XdjISbZ3XfRfeo1L??G4p4w zYA$Om@K_y#N22r)u@goprd=~Z7fhpOP+{zi-egTO*I}IVEHnOz^r$@(5Z{tNc*Bw+ z2%oZPWNO(LgpnLe@Vpu{W4PIdcDZo@Of}rv5&B-}Ap|qTGRbX&V%ktR{WlmmQ;cpp zYJj5#IBI~tZPWlq4bX=|nWZ#L`>T=$YeAeL5y@-)*yz%@o}*h5@duLY%jN1rhMAJ# zUt*dR0qzca6;)0%j5v<$t>NyPKG0t*!q%R&r+13@SPC*{yAmkxJQxgVbL*4NSMAslZQx?6)n zSXw07A~g~XPme^Oaxip>Kl+)wwRi)qQx_;rAjhd2qDLSt zftdbLfp|)#BsP?@JVpLSIRpU^jqGs4bHI$MV>8JI?=rIWR@3!B3wxvj!Ui!zG|hB% z2;c`3Kb-q!GiJlU>;9ABL&2};L~?)hgT|(|FB@b$JR=GEsUZ0Z@qPf(W-HSop!BMs zu?h4y#(3YYJVZ_cOZ?RbCEx?D(_kI3O{fA34BCEtZl;gz=4>pz}AE zVay>gjtQ-qk9r0aL>1{NE$ILoDdWwIMKO>fM)zcOY$8QXsccJHhgyftFhcdBcYe^n z%{F8c>*if(tll;i9n)*(Q$jNWjm{&xkT^8@IDQ-%}qhD)IFtc$0o zW6Py8J`4)J1)UBPgnDC}0ZK4SWW&_Tjp2dju->j2;<;U;vM8o7-3+!_>oy~Gr`=Ba zske{nYke=&yF6rTm4hrxOBi>xe>`imfejtp*0GI@hmw$3k_C-WN^%brkM)+Rdp&&P z54bgT+v3E8M+DmzYAM#uuWVWK4Z9V?Tqi^H@nN$8V%#gpA9-Fye&D*V%b>dRT2@MN zrf!y>m5>8R^_M`Eo#tYS&XJ&Ok&rqaYgw)RWDsf9Go=FlRL}>U5S=fKSy|nW&(|dC zFxgI*DpnFr85OjDPgeWK4UQPTxevx1yLI4O`_&F^qEk9jQ+{;~!R)wTvk{xS)0B@o@@fQHXgMZ#hFeA~w<5K~{ zwvIY2P!!gph|~eJ0*wH5S9ojY--D+pfa1%lR2dau8=@AAgmH-|i;_Y+iNc&gV1e>I zkpq`w@PVjaw?K+yX78e8<$!XF2d3Vl+1B+!gpD$c&V5yt*lE+Ksx}$c1Jk921)m+OY4sI?MAxY` zdaK)kYeA%~JC(FObCff=ERRu_B!jCyLqA)RP%hLhhifA=enyTJbmM zLN3;a(O;{nGgX+Ca zyWx@NN&HAXDg{6d7j&qI9l1v4sn7>kH*~9~kB>ACoFsUqIXO1XL`@D-vx8_%m{d$8 zck3*NK=BA&QH`Ag^a1=nol$1+x#qP+by8G!k2<&31SE*ms2l(+!Q~POnih$~jPODv zE9chxSPSGQ1SI5xK1lw;K>12jD1dP33aZU34$*pCtK^XQq?8a!6dUq^L?*j8ONs^U z#gq6bwW+I;xrGN~EFn-0T-C0wuHmJkf8C->9lyBxc_-zVWQ4^~G3RBqr}gCQY)wp6 zmQSmM>4B{k)3~W>scK7}cqAo}4>HG!)uxJRmNe%GGdElGR-tw&#Kiv~LW~$xrTjqD zYm-;A*7(?8TesO`Pg<~CUkK*ZiYl|TUKrdhZ}CYXy5w$_f<*~#hbDhCVl8V>QUa@8 z5uG67qzWi?I%JMOW_7HVH}b%U^gcPJci(@1eK}<6)O%L&g}%1%?QaQI`D(-ht@MSwQa$AS8@ZEke3^460UZ6(ak`HVc@DoEVTKZ7@I~A-HTPO+&FJWPStoijH%Ar+ zi%$poo!a$XH|O14`lnG-rf$V4`rceiMhwuKLCs0|L^at?qR+Az-t1sY3(S+)O4j{R zpXvm=?KOVdnclHm%Z60mlB{=zS`wjtp=Il*QhmS$l{{Y|ED_cWD=HG~@=0n-{}f$b z-P~SXoWHxeJVW1_U!iqHVT#_>3Ow{uttEqL#ImZ7$AIA(_p?V3?p)sWRm z9SLV9Jx)A zpF@E0(%^@9-YE1uogPdfz3*UpV1>$r5)Ug(4XmK*RJ|}qb8}7+dTX=s+{Ed8L!oz# zI$JBL-TW1wYXWSD#jM_GQ|V(t9|-fB*Z`hwyB4(kwL@)Hytjofju(yT}*!> zo~F7~z_P?|FPF@@3?=v3&6xZ+x7RlD8&s07kIuU$89&pc?@H(`k7d7T++Q2pi?u55cH=k@&Gol$ zG#P7_lSv87Kc@*nj%$sQ1w&EDkLPfk|#@BwZ=3|l2ut<%9!gAUO3aX zd2T#uSxz5lLKaX$kFrG#a;XmHMBG-1rjzEi`d6)jEu5)0Z{I$wBq`0D~ehQf)WqU30OO~ZWI z=c&UHigo2C9lfv^Z5VfS@vZ=;=r!zzcjo7@q<#;WXryT9>Q)Whg=O^Ls1a9zK^>I>0 zCB>5G!M>x$o26N*5R#|n)Lq*roWpzrKFI6&I?)O$8gxNei)*GdGwk7&YUU^$Lzyhy zH)?61KN!<4Am%53Jo|&^d4pQOYnAw|Ev_nqzB5}U{V9xv(w|t%daZ~4zYCn`DSfj` zQ)_c+!g|_x)pojCqBqv|7n+n`S@a zIL^5Z;-MR|*b$(mz)`agJl zy4^&YSq5$5`ilUkvCe$`>LlNU@LlAmg9Y(juX0FUe0QhD75Y=(@tvs4y!ZS10&Q!g zaUK5Vp|6K@nN-VKqT=P7yB}Yj|LN+LbKXsam^$r7n;8;!?9V;=&acBq%WDlvB61sX8VAePmasnh8BS zJ|4+ub#gw*Ec*#$d1!X|fquuzV(QFk29~R9NL@fg2p^cK*6#dtcHd5n`4cm)ZLgB7 ztF)az*X(@V)N;2aX)I6echXfEwc7sQS#z(9AHybaU1T_NC=k1=3{!oiKLKd1oiJsf z6dt`@zSag~7+Slvb9!wJ^^99EiU8Dxk30Y9$?t^nO8DW4^yJ;@$bP)1+*ez3mnD~+ z4a~ypdbt^7asy9X{QvA-Z*$zX(SKe)p8}J1Mp7fma>q^5yvfK>oT{;7)#qfUaJW5vL!<)pX!yW+wi~Wb+5ALSsv51xz1oshqx>0-V z?ktvtOFXK0!Gev&Pq6<#R>+d6(blF?1R=2XGf-1GUbt=m2-!S=5)ZYHU`*&vcZ-=~aeIG|x3b?4mZ5)-_m)GrV z!z#&~e)6H_;KI&~Y~2=$g#;f^9<7W3H18_UISeXfbYrgL)4}=Kz&Mom6YN zC*N9gl~VtG_~+V6%k_Xn53q9Oxqh><2)Vy3(9LiJ8>p%ld}-;oQ+Zq5@-k(o7ehG+ z^mQm$+!#3N_JydXAF#~N1zYHUTxOMmDq2(P8rLjAd_${bpE%cQORFgROF@h>j)o+Y zisz22)Uwx~oSc|gGCTSF^Us-KN=rqOE!-EQLcJg((O8h`RR?ToU^7BwiTb{)!e+TLRO4lUBN0f#{lB4xYbY& zOe(vcSC)BOuhHDp_hxzuOl;rco*}|A+&zmw@yt*US)SbpcBKsGc7x(y3RUL_pebJH zr-O%SRwe?%@U>>p;OF+*cW*M1qZvFO!mkzJUf`>KAjpq^t z3LdP9#`JKvJMNZCitE~zSSa($z zS)TnnMaBsYUcRh5?NF<);lY}l2V?p-I~=qSfu&~D7+&B<8I2~v~DMHuILfg zMes!MI|k3!lq)Jx;I1JAG94#fP^LG}5_3U83${`zGYV_TFM`(XV%6RNHt$IU~zRzSQ3yqoh;{``eezM5GMd)_{$Px!#27MS6 zl1@?B5Bm^!e`Ja7G}9|;z?T>yx9fX!eTz|xbP8Xj+Xj3S3g0v%IxF2ao>v}IG}3MD z7)fwr-XEIebAZ4~G8&$x@=|$`yrA=8v!)wiuThl zf!dZAg{*+?{*dK{CYVO!%k0T$EaaeKtPJwbWE*$vbnDifLu1&zTN7{PxLH%lTrBtq zp|I{==r?v@&EVTIt11C_z@l7?VQANH^RShx{$%y^6FL+6mXzjKZdhAS_eB7a0#Rf{ z@k3uzW|#O{Mjx5f2fzy6=>t#GIR18R3;XFEW#E%3+1Vt_(XMSD(P~Gsn#dI06WbWc zG6%|`F~jJQvQ3dJADN;@o`xe?EPgGu+c3%NEn`m`lG$XbLy@c?GBwH%Uyo$9BU5Cd zn5;Pcgzk@I1(7MT$e~D+0(03$j>IBr=6?upV!zD&u@KF%p;?vRpHl3pe&K zi)Nmkxc#Uu_uWA=MIEdtHHYb_{5YyUU3X?eEDh&~##hl^JGLRE_GgOKB7 zSsz+~#|yELsPGA1Nx(uf+clXilWNzK9G0f=O1&|r?0gaMuW?4qLbdv z9A!3NdEO6o*o3_Fr=nPmlDOh!^ma~P+sYwHvsI>K^v+>lXSVyL$$q@(7l%w!gp={)o*aJo@axNN=5a zSJ#P^zpLv+wcl7>C%Cnp>Q2g1^=?g2+WC8~>{NOoCgL*VH;kjnJ6ex+16aR?jiVnk&4v+fv_B{MymAakJ%|JMUpKueNGu@ zIP*ExDfWEcS*1jNW!Z&`1|Fm&(2oXU!JYP_4<8-OGwE1s2~yQu)_KZG@o!f@Vdvs) z_**F!TeE34R1*x^J}_TqSQJz*i>~j)ugk178)GICS)^mbgllpd6V4=ANv|-iL6ku% z;CYs2)t#@Igzc#PtSBdRySc@yn%Bqpa#>2A%!LX@KslC-K?x909#kQ(ud@Q286#a= zun0o-rj@dCwaJ~~hO5s0EHzXJ0i&wCGq8&?{4to5j1toex+x~})IoB8al)3OOss~W z)d3i2<4UqzTmJg=yV~?K1fhR^#^$mv)p1A`@YH+;Avri&DdEhD<$|ke0SZ5(Y7V0(LJ1kuvnw)!MScKaiJ~%yUFNoo zSn`s)*AkVP$i;%I1DlfLraI6vuxY70Z)PN@>zZmISEArWG#B6ICox@^ye%A)-F3DS z1$6~mf8jY#ZbXr?ACz0i>0QAWmQG|@%2lQpvPhl$s9<0D1__4DL~$KrCK!oR&0ejD zkodYoOrI&tDX&DCtrFqPQ!RI@L9L=duWQ4yL&<{OWX=*_S72B;c&=4c+#q*2g6##R z-(WEv%N-7i(rvxLFGUwT>fA3c@i>)xk`ip9A(j(Q1k@ch&xj>OPT`PspS_}(uC*D=h`@W^Ze`wg^AoXt^<1_s^m!`mh&g} z7fAf#4>4T}S!9(gX#k{qt{0rjiOh402`aDH*J3&MVn^|Q+{1i)IBw0Qlr)OIJ>8?d z9nB}O8wq}Cc}b(Qe~9IF4}@9{Dji|g_Cu-nP>nt+rFVanrh6?;)9`X3iICL&d`nHT z^DFSMQjs#b5@ngCLOJzJ(xOF{Kh#3rajSmx+^S^1d(SxDEro49AuDX2F0$g0sca*WXVYFj<8fJP5v_6o> zOl5dn^Ge)un%!ahgdCFrvm})Bz)Fe^{q*9TeGB5IW1lKz1!nh|T<|PfTGM4CX?5;W z15yTe;bk!^OdA3XFn+E5ed~ii67rrklkc}l-m@)}eGo%+*NeO*7o7vAMa^O7YjBtn zh007qIjGPw<{;|kfWJ2crsE-dDX+7_X&Gs2Fs^u($EI;-%YF2=oKpEW1;5PW+5Ywh z@!i;qoL@6x{_!Nul>RT9&0dn;U*JI|7viy+x1OZ{09d_-~wiG9&)(hl>28n6laxl`1s#u@yH z>A%Izf2CxS`Q$D{BAcT%HAm}oqcz4DZJ=Na_E-vb3K%j0!BuD(WQCir$p!~&kj#|a zbX#Xib{SP-p3&WAwF|5BSWcD2>iq%4MDNNHsu6ZoO?j3Z7w#l;v37zP%9eZtV^!0a3C4&gd|EUsPxu zKym#FXjEuN&@=#uD8R$+mCgA0vu>FBbDq zmb#%-sifAg4-k=H__d9T<-iljK?A8T?)rMxN$&fPog-rN_QT2jT~7bLa{4{h-bW+V7LT%OH_ z+noK$t4b`EReUAw638EY0(r7T(!5^q;z(-H;|SO(fS`&8>^< z7X)yxhv)QG!#(8PjNFlP29``mvm|nfo-upy+kyJ|b4nyImqOrLjA(EwF3j!qMoQbW z1=p76xxB?iJ40hbuM0g^3*>Pr*di-SS=tOC!Ypi-i>boW9=0gKJuGc4&P-Hx3F+y^ z9LBtW3zGW(T`{+LcBf-6#hkA)S(ipSM&u{9t2y8AWbHE&wYePSXIGI2e$2jaI-8(C zDqRruNc@_qiawX_6+7yj2egb=YN>66AEQ@i+uJGZ)wYmVGxMiy>|={lqrhCWm)JhE zjl9u5#%CFK_Hpp&uC67|lDqS>aWyf}*PVjO*Td+~*>jEw_`c!=zlH$L{_NuUs{_57 zOc?3>thZ=p7XJA6(_QQCObOX4SnJ(CEzt=v(>5?I6W(NZM>>YOv*$Fw=z?K##>2jH8A_eO9zj4*GSBB&ILos|PmHT=wlAAXs z6|pXS$losoSA8_c8#hYt1h0Jva~5zDStS;JQeS!wDEIsJHL?Hv_y7NI*LH2!c8%Qr Q7XSeN|Aj?|5&*6Q040hY;Q#;t literal 0 HcmV?d00001 From e8c20faca6d45902745241622a9326395a4d4fd8 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Thu, 15 Jun 2023 16:47:28 +0300 Subject: [PATCH 229/316] fix prom exporter port --- controllers/factory/vector/vectoragent/vectoragent_daemonset.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controllers/factory/vector/vectoragent/vectoragent_daemonset.go b/controllers/factory/vector/vectoragent/vectoragent_daemonset.go index 186e4d9f..93dcde22 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_daemonset.go +++ b/controllers/factory/vector/vectoragent/vectoragent_daemonset.go @@ -210,7 +210,7 @@ func (ctrl *Controller) VectorAgentContainer() *corev1.Container { Ports: []corev1.ContainerPort{ { Name: "prom-exporter", - ContainerPort: 9090, + ContainerPort: 9598, Protocol: "TCP", }, }, From 4d8acab7ebf90f60ff7582d849a346122a92772f Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Thu, 15 Jun 2023 16:50:16 +0300 Subject: [PATCH 230/316] release v0.0.27 --- CHANGELOG.md | 3 ++ helm/charts/vector-operator/Chart.yaml | 4 +- helm/index.yaml | 59 ++++++++++++++--------- helm/packages/vector-operator-0.0.27.tgz | Bin 0 -> 31028 bytes 4 files changed, 41 insertions(+), 25 deletions(-) create mode 100644 helm/packages/vector-operator-0.0.27.tgz diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ef99a46..38a0db61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## v0.0.27 +- **HotFix** fix prom exporter port + ## v0.0.26 - [[116]](https://github.com/kaasops/vector-operator/pull/116) **Fix** don not merge source if custom options exists diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index 98b5b71b..46daaee3 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.26 +version: 0.0.27 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.0.26" +appVersion: "v0.0.27" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/index.yaml b/helm/index.yaml index 496bc5da..797ddb29 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.0.27 + created: "2023-06-15T16:50:05.848584741+03:00" + description: A Helm chart to install Vector Operator + digest: 631e2ff02bbd7f247cb486494fd2af60c57cc551066a6a3858226551bc1745a4 + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.27.tgz + version: 0.0.27 - apiVersion: v2 appVersion: v0.0.26 - created: "2023-06-15T12:26:23.850490495+03:00" + created: "2023-06-15T16:50:05.847682292+03:00" description: A Helm chart to install Vector Operator digest: 760a2833f4c1a33466982419b079ff18d996331ebacc40cf93b0f55229cdb7db home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.0.26 - apiVersion: v2 appVersion: v0.0.25 - created: "2023-06-15T12:26:23.849596878+03:00" + created: "2023-06-15T16:50:05.846355701+03:00" description: A Helm chart to install Vector Operator digest: fd22b996b071b6d85740ccf76e85cb640fa717c2620748d206d3f4fdd44cbcc2 home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.0.25 - apiVersion: v2 appVersion: v0.0.24 - created: "2023-06-15T12:26:23.848340501+03:00" + created: "2023-06-15T16:50:05.845459393+03:00" description: A Helm chart to install Vector Operator digest: ea257e60ecde063a0d1ed52ce5e3283245b8f0e2daba58ea3a5adb0ba82d7799 home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.0.24 - apiVersion: v2 appVersion: v0.0.23 - created: "2023-06-15T12:26:23.847389511+03:00" + created: "2023-06-15T16:50:05.844293935+03:00" description: A Helm chart to install Vector Operator digest: 546d202b3b9263f789b88335263191098dfcabd5d8698105f37cad24d56a8ed0 home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.0.23 - apiVersion: v2 appVersion: v0.0.22 - created: "2023-06-15T12:26:23.84650396+03:00" + created: "2023-06-15T16:50:05.843395484+03:00" description: A Helm chart to install Vector Operator digest: bf96ddc8ac61e9d6beb8bc763fbf3fa6025d950b29d70d80de6e8a0ea45e0411 home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.0.22 - apiVersion: v2 appVersion: v0.0.21 - created: "2023-06-15T12:26:23.845612004+03:00" + created: "2023-06-15T16:50:05.842483246+03:00" description: A Helm chart to install Vector Operator digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2023-06-15T12:26:23.844336832+03:00" + created: "2023-06-15T16:50:05.841273695+03:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2023-06-15T12:26:23.843754574+03:00" + created: "2023-06-15T16:50:05.840685817+03:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2023-06-15T12:26:23.843197143+03:00" + created: "2023-06-15T16:50:05.840078254+03:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2023-06-15T12:26:23.842645392+03:00" + created: "2023-06-15T16:50:05.839517267+03:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2023-06-15T12:26:23.841687807+03:00" + created: "2023-06-15T16:50:05.838949196+03:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -146,7 +159,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2023-06-15T12:26:23.841089789+03:00" + created: "2023-06-15T16:50:05.838017851+03:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -159,7 +172,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2023-06-15T12:26:23.840515506+03:00" + created: "2023-06-15T16:50:05.837437578+03:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -172,7 +185,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2023-06-15T12:26:23.839909817+03:00" + created: "2023-06-15T16:50:05.836839752+03:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -185,7 +198,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2023-06-15T12:26:23.839326897+03:00" + created: "2023-06-15T16:50:05.836237054+03:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -198,7 +211,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2023-06-15T12:26:23.837584443+03:00" + created: "2023-06-15T16:50:05.83558227+03:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -211,7 +224,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2023-06-15T12:26:23.837059599+03:00" + created: "2023-06-15T16:50:05.833705604+03:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -224,7 +237,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2023-06-15T12:26:23.852367575+03:00" + created: "2023-06-15T16:50:05.850587162+03:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -237,7 +250,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2023-06-15T12:26:23.851897742+03:00" + created: "2023-06-15T16:50:05.850065514+03:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -250,7 +263,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2023-06-15T12:26:23.851439541+03:00" + created: "2023-06-15T16:50:05.849538538+03:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -263,7 +276,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2023-06-15T12:26:23.850973794+03:00" + created: "2023-06-15T16:50:05.849046556+03:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -276,7 +289,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2023-06-15T12:26:23.836580664+03:00" + created: "2023-06-15T16:50:05.833180486+03:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -287,4 +300,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2023-06-15T12:26:23.835987399+03:00" +generated: "2023-06-15T16:50:05.832574711+03:00" diff --git a/helm/packages/vector-operator-0.0.27.tgz b/helm/packages/vector-operator-0.0.27.tgz new file mode 100644 index 0000000000000000000000000000000000000000..f184705e8f4c7c9ed3b331a7080122753a701d63 GIT binary patch literal 31028 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�Lus+JLM@tnzE3_9;0Tm1Yp!L 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 literal 0 HcmV?d00001 From b1b8a9364f3fec3ccd14c876d5d517be956c5610 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Mon, 19 Jun 2023 14:17:03 +0300 Subject: [PATCH 231/316] merge sinks --- api/v1alpha1/vector_types.go | 3 ++ .../observability.kaasops.io_vectors.yaml | 3 ++ controllers/factory/config/config_build.go | 38 +++++++++++++++++++ .../config/configcheck/configcheck_pod.go | 9 +---- controllers/factory/config/types.go | 9 +++-- .../vector-operator/templates/vector.yaml | 1 + helm/charts/vector-operator/values.yaml | 1 + 7 files changed, 52 insertions(+), 12 deletions(-) diff --git a/api/v1alpha1/vector_types.go b/api/v1alpha1/vector_types.go index 222966ba..dc03e3e5 100644 --- a/api/v1alpha1/vector_types.go +++ b/api/v1alpha1/vector_types.go @@ -34,6 +34,9 @@ type VectorSpec struct { // Merge kubernetes sources and move selectors processing to transforms. // +optional MergeKubernetesSources bool `json:"mergeKubernetesSources,omitempty"` + // Merge kubernetes sink with equal options. + // +optional + MergeSinks bool `json:"mergeSinks,omitempty"` // Vector Aggregator // Aggregator *VectorAggregator `json:"aggregator,omitempty"` diff --git a/config/crd/bases/observability.kaasops.io_vectors.yaml b/config/crd/bases/observability.kaasops.io_vectors.yaml index 910673df..a153b615 100644 --- a/config/crd/bases/observability.kaasops.io_vectors.yaml +++ b/config/crd/bases/observability.kaasops.io_vectors.yaml @@ -4144,6 +4144,9 @@ spec: description: Merge kubernetes sources and move selectors processing to transforms. type: boolean + mergeSinks: + description: Merge kubernetes sink with equal options. + type: boolean type: object status: description: VectorStatus defines the observed state of Vector diff --git a/controllers/factory/config/config_build.go b/controllers/factory/config/config_build.go index f5845237..b7f62954 100644 --- a/controllers/factory/config/config_build.go +++ b/controllers/factory/config/config_build.go @@ -21,10 +21,12 @@ import ( "encoding/json" "errors" "fmt" + "sort" "strings" vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" "github.com/kaasops/vector-operator/controllers/factory/pipeline" + "github.com/kaasops/vector-operator/controllers/factory/utils/hash" "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" "github.com/kaasops/vector-operator/controllers/factory/vector/vectoragent" "github.com/mitchellh/mapstructure" @@ -131,6 +133,12 @@ func (b *Builder) generateVectorConfig() (*VectorConfig, error) { } } + if b.vaCtrl.Vector.Spec.MergeSinks { + if err := b.mergeSyncs(vectorConfig); err != nil { + return nil, err + } + } + return vectorConfig, nil } @@ -269,6 +277,11 @@ func getSinks(pipeline pipeline.Pipeline) ([]*Sink, error) { for i, inputName := range sink.Inputs { sink.Inputs[i] = addPrefix(pipeline.GetNamespace(), pipeline.GetName(), inputName) } + optbyte, err := json.Marshal(sink.Options) + if err != nil { + return nil, err + } + sink.OptionsHash = fmt.Sprint(hash.Get(optbyte)) sinks = append(sinks, sink) } return sinks, nil @@ -373,6 +386,31 @@ func (b *Builder) mergeKubernetesSources(config *VectorConfig) error { return nil } +func (b *Builder) mergeSyncs(config *VectorConfig) error { + uniqOpts := make(map[string]*Sink) + var mergedSinks []*Sink + + for _, sink := range config.Sinks { + v, ok := uniqOpts[sink.OptionsHash] + if ok { + if sink.Type == v.Type { + // If sink spec already exists rename and merge inputs + v.Name = v.OptionsHash + v.Inputs = append(v.Inputs, sink.Inputs...) + sort.Strings(v.Inputs) + continue + } + } + uniqOpts[sink.OptionsHash] = sink + mergedSinks = append(mergedSinks, sink) + } + + if len(mergedSinks) > 0 { + config.Sinks = mergedSinks + } + return nil +} + func generateVrlFilter(selector string, selectorType string) string { buffer := new(bytes.Buffer) diff --git a/controllers/factory/config/configcheck/configcheck_pod.go b/controllers/factory/config/configcheck/configcheck_pod.go index 1160fbf1..b892953c 100644 --- a/controllers/factory/config/configcheck/configcheck_pod.go +++ b/controllers/factory/config/configcheck/configcheck_pod.go @@ -49,14 +49,7 @@ func (cc *ConfigCheck) createVectorConfigCheckPod() *corev1.Pod { Args: []string{"validate", "/etc/vector/*.json"}, Env: cc.generateVectorConfigCheckEnvs(), SecurityContext: cc.ContainerSecurityContext, - Ports: []corev1.ContainerPort{ - { - Name: "prom-exporter", - ContainerPort: 9090, - Protocol: "TCP", - }, - }, - VolumeMounts: cc.generateVectorConfigCheckVolumeMounts(), + VolumeMounts: cc.generateVectorConfigCheckVolumeMounts(), }, }, RestartPolicy: "Never", diff --git a/controllers/factory/config/types.go b/controllers/factory/config/types.go index 97f5c0c4..2e5b3b54 100644 --- a/controllers/factory/config/types.go +++ b/controllers/factory/config/types.go @@ -42,10 +42,11 @@ type Transform struct { } type Sink struct { - Name string - Type string `mapper:"type"` - Inputs []string `mapper:"inputs"` - Options map[string]interface{} `mapstructure:",remain"` + Name string + Type string `mapper:"type"` + Inputs []string `mapper:"inputs"` + Options map[string]interface{} `mapstructure:",remain"` + OptionsHash string } type ConfigComponent interface { diff --git a/helm/charts/vector-operator/templates/vector.yaml b/helm/charts/vector-operator/templates/vector.yaml index a31edf4a..9023060b 100644 --- a/helm/charts/vector-operator/templates/vector.yaml +++ b/helm/charts/vector-operator/templates/vector.yaml @@ -6,6 +6,7 @@ metadata: namespace: {{ .Release.Namespace }} spec: mergeKubernetesSources: {{ .Values.vector.mergeKubernetesSources}} + mergeSinks: {{ .Values.vector.mergeSinks}} {{- with .Values.vector.agent }} agent: {{ toYaml . | indent 4 }} diff --git a/helm/charts/vector-operator/values.yaml b/helm/charts/vector-operator/values.yaml index 96539728..cbe0a66e 100644 --- a/helm/charts/vector-operator/values.yaml +++ b/helm/charts/vector-operator/values.yaml @@ -73,6 +73,7 @@ vector: enable: false name: "vector" mergeKubernetesSources: false + mergeSinks: false # agent: # image: timberio/vector:0.24.0-distroless-libc # env: From 7e2f1ff1ce1016ad60178582599d42b67c0b8365 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Wed, 21 Jun 2023 15:42:37 +0300 Subject: [PATCH 232/316] release v0.0.28 --- CHANGELOG.md | 3 + helm/charts/vector-operator/Chart.yaml | 4 +- .../observability.kaasops.io_vectors.yaml | 3 + helm/index.yaml | 61 +++++++++++------- helm/packages/vector-operator-0.0.28.tgz | Bin 0 -> 31072 bytes 5 files changed, 45 insertions(+), 26 deletions(-) create mode 100644 helm/packages/vector-operator-0.0.28.tgz diff --git a/CHANGELOG.md b/CHANGELOG.md index 38a0db61..a8cfbd8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## v0.0.28 +- [[118]](https://github.com/kaasops/vector-operator/pull/118) **Feature** Merge sinks with equal options + ## v0.0.27 - **HotFix** fix prom exporter port diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index 46daaee3..f6d59dc6 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.27 +version: 0.0.28 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.0.27" +appVersion: "v0.0.28" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml index 910673df..a153b615 100644 --- a/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml +++ b/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml @@ -4144,6 +4144,9 @@ spec: description: Merge kubernetes sources and move selectors processing to transforms. type: boolean + mergeSinks: + description: Merge kubernetes sink with equal options. + type: boolean type: object status: description: VectorStatus defines the observed state of Vector diff --git a/helm/index.yaml b/helm/index.yaml index 797ddb29..ec1805e1 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.0.28 + created: "2023-06-21T15:41:08.889736338+03:00" + description: A Helm chart to install Vector Operator + digest: af856d41314313e04f15e7143409a9c564c6ca610b0d2eaec3112add8573e668 + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.28.tgz + version: 0.0.28 - apiVersion: v2 appVersion: v0.0.27 - created: "2023-06-15T16:50:05.848584741+03:00" + created: "2023-06-21T15:41:08.888834241+03:00" description: A Helm chart to install Vector Operator digest: 631e2ff02bbd7f247cb486494fd2af60c57cc551066a6a3858226551bc1745a4 home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.0.27 - apiVersion: v2 appVersion: v0.0.26 - created: "2023-06-15T16:50:05.847682292+03:00" + created: "2023-06-21T15:41:08.88783507+03:00" description: A Helm chart to install Vector Operator digest: 760a2833f4c1a33466982419b079ff18d996331ebacc40cf93b0f55229cdb7db home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.0.26 - apiVersion: v2 appVersion: v0.0.25 - created: "2023-06-15T16:50:05.846355701+03:00" + created: "2023-06-21T15:41:08.886472038+03:00" description: A Helm chart to install Vector Operator digest: fd22b996b071b6d85740ccf76e85cb640fa717c2620748d206d3f4fdd44cbcc2 home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.0.25 - apiVersion: v2 appVersion: v0.0.24 - created: "2023-06-15T16:50:05.845459393+03:00" + created: "2023-06-21T15:41:08.885379661+03:00" description: A Helm chart to install Vector Operator digest: ea257e60ecde063a0d1ed52ce5e3283245b8f0e2daba58ea3a5adb0ba82d7799 home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.0.24 - apiVersion: v2 appVersion: v0.0.23 - created: "2023-06-15T16:50:05.844293935+03:00" + created: "2023-06-21T15:41:08.884130179+03:00" description: A Helm chart to install Vector Operator digest: 546d202b3b9263f789b88335263191098dfcabd5d8698105f37cad24d56a8ed0 home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.0.23 - apiVersion: v2 appVersion: v0.0.22 - created: "2023-06-15T16:50:05.843395484+03:00" + created: "2023-06-21T15:41:08.883121475+03:00" description: A Helm chart to install Vector Operator digest: bf96ddc8ac61e9d6beb8bc763fbf3fa6025d950b29d70d80de6e8a0ea45e0411 home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.0.22 - apiVersion: v2 appVersion: v0.0.21 - created: "2023-06-15T16:50:05.842483246+03:00" + created: "2023-06-21T15:41:08.881779457+03:00" description: A Helm chart to install Vector Operator digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2023-06-15T16:50:05.841273695+03:00" + created: "2023-06-21T15:41:08.880872969+03:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2023-06-15T16:50:05.840685817+03:00" + created: "2023-06-21T15:41:08.880245588+03:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2023-06-15T16:50:05.840078254+03:00" + created: "2023-06-21T15:41:08.878989165+03:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2023-06-15T16:50:05.839517267+03:00" + created: "2023-06-21T15:41:08.878359896+03:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -146,7 +159,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2023-06-15T16:50:05.838949196+03:00" + created: "2023-06-21T15:41:08.877777085+03:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -159,7 +172,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2023-06-15T16:50:05.838017851+03:00" + created: "2023-06-21T15:41:08.877186609+03:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -172,7 +185,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2023-06-15T16:50:05.837437578+03:00" + created: "2023-06-21T15:41:08.876593719+03:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -185,7 +198,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2023-06-15T16:50:05.836839752+03:00" + created: "2023-06-21T15:41:08.875668738+03:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -198,7 +211,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2023-06-15T16:50:05.836237054+03:00" + created: "2023-06-21T15:41:08.874998669+03:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -211,7 +224,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2023-06-15T16:50:05.83558227+03:00" + created: "2023-06-21T15:41:08.874332656+03:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -224,7 +237,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2023-06-15T16:50:05.833705604+03:00" + created: "2023-06-21T15:41:08.873797522+03:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -237,7 +250,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2023-06-15T16:50:05.850587162+03:00" + created: "2023-06-21T15:41:08.892271845+03:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -250,7 +263,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2023-06-15T16:50:05.850065514+03:00" + created: "2023-06-21T15:41:08.891746593+03:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -263,7 +276,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2023-06-15T16:50:05.849538538+03:00" + created: "2023-06-21T15:41:08.89117635+03:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -276,7 +289,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2023-06-15T16:50:05.849046556+03:00" + created: "2023-06-21T15:41:08.890301789+03:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -289,7 +302,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2023-06-15T16:50:05.833180486+03:00" + created: "2023-06-21T15:41:08.873209435+03:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -300,4 +313,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2023-06-15T16:50:05.832574711+03:00" +generated: "2023-06-21T15:41:08.872531089+03:00" diff --git a/helm/packages/vector-operator-0.0.28.tgz b/helm/packages/vector-operator-0.0.28.tgz new file mode 100644 index 0000000000000000000000000000000000000000..d20e4fa5b54aafe7b5b6270af3f2424768ae4aac GIT binary patch literal 31072 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 Date: Mon, 28 Aug 2023 17:40:25 +0300 Subject: [PATCH 233/316] feat: add nodeSelector to helm chart --- helm/charts/vector-operator/templates/deployment.yaml | 4 ++++ helm/charts/vector-operator/values.yaml | 2 ++ 2 files changed, 6 insertions(+) diff --git a/helm/charts/vector-operator/templates/deployment.yaml b/helm/charts/vector-operator/templates/deployment.yaml index bce2b97f..18ef83e1 100644 --- a/helm/charts/vector-operator/templates/deployment.yaml +++ b/helm/charts/vector-operator/templates/deployment.yaml @@ -26,6 +26,10 @@ spec: labels: {{- include "chart.selectorLabels" . | nindent 8 }} spec: + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} {{- with .Values.imagePullSecrets }} imagePullSecrets: {{- toYaml . | nindent 8 }} diff --git a/helm/charts/vector-operator/values.yaml b/helm/charts/vector-operator/values.yaml index cbe0a66e..72599c3f 100644 --- a/helm/charts/vector-operator/values.yaml +++ b/helm/charts/vector-operator/values.yaml @@ -21,6 +21,8 @@ strategy: {} imagePullSecrets: [] +nodeSelector: {} + securityContext: {} # allowPrivilegeEscalation: false # runAsGroup: 1000 From 4aa44f1c81c07ef75bf132827518877f58736106 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Wed, 30 Aug 2023 22:05:37 +0300 Subject: [PATCH 234/316] release v0.0.29 --- CHANGELOG.md | 3 ++ helm/charts/vector-operator/Chart.yaml | 4 +- helm/index.yaml | 63 ++++++++++++++--------- helm/packages/vector-operator-0.0.29.tgz | Bin 0 -> 31091 bytes 4 files changed, 43 insertions(+), 27 deletions(-) create mode 100644 helm/packages/vector-operator-0.0.29.tgz diff --git a/CHANGELOG.md b/CHANGELOG.md index a8cfbd8b..cfffdcbd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## v0.0.29 +- [[121]](https://github.com/kaasops/vector-operator/pull/121) **Feature** Add nodeSelector for operator deployment to helm chart + ## v0.0.28 - [[118]](https://github.com/kaasops/vector-operator/pull/118) **Feature** Merge sinks with equal options diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index f6d59dc6..7cb67f63 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.28 +version: 0.0.29 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.0.28" +appVersion: "v0.0.29" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/index.yaml b/helm/index.yaml index ec1805e1..3a1365e1 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.0.29 + created: "2023-08-30T22:03:26.195874+03:00" + description: A Helm chart to install Vector Operator + digest: 0f025fc3a924b37b8c4131c4d8cfa437d2d4e557ab9476ed3e69a00232c7dca6 + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.29.tgz + version: 0.0.29 - apiVersion: v2 appVersion: v0.0.28 - created: "2023-06-21T15:41:08.889736338+03:00" + created: "2023-08-30T22:03:26.194131+03:00" description: A Helm chart to install Vector Operator digest: af856d41314313e04f15e7143409a9c564c6ca610b0d2eaec3112add8573e668 home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.0.28 - apiVersion: v2 appVersion: v0.0.27 - created: "2023-06-21T15:41:08.888834241+03:00" + created: "2023-08-30T22:03:26.190078+03:00" description: A Helm chart to install Vector Operator digest: 631e2ff02bbd7f247cb486494fd2af60c57cc551066a6a3858226551bc1745a4 home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.0.27 - apiVersion: v2 appVersion: v0.0.26 - created: "2023-06-21T15:41:08.88783507+03:00" + created: "2023-08-30T22:03:26.186741+03:00" description: A Helm chart to install Vector Operator digest: 760a2833f4c1a33466982419b079ff18d996331ebacc40cf93b0f55229cdb7db home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.0.26 - apiVersion: v2 appVersion: v0.0.25 - created: "2023-06-21T15:41:08.886472038+03:00" + created: "2023-08-30T22:03:26.183413+03:00" description: A Helm chart to install Vector Operator digest: fd22b996b071b6d85740ccf76e85cb640fa717c2620748d206d3f4fdd44cbcc2 home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.0.25 - apiVersion: v2 appVersion: v0.0.24 - created: "2023-06-21T15:41:08.885379661+03:00" + created: "2023-08-30T22:03:26.178805+03:00" description: A Helm chart to install Vector Operator digest: ea257e60ecde063a0d1ed52ce5e3283245b8f0e2daba58ea3a5adb0ba82d7799 home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.0.24 - apiVersion: v2 appVersion: v0.0.23 - created: "2023-06-21T15:41:08.884130179+03:00" + created: "2023-08-30T22:03:26.174937+03:00" description: A Helm chart to install Vector Operator digest: 546d202b3b9263f789b88335263191098dfcabd5d8698105f37cad24d56a8ed0 home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.0.23 - apiVersion: v2 appVersion: v0.0.22 - created: "2023-06-21T15:41:08.883121475+03:00" + created: "2023-08-30T22:03:26.172384+03:00" description: A Helm chart to install Vector Operator digest: bf96ddc8ac61e9d6beb8bc763fbf3fa6025d950b29d70d80de6e8a0ea45e0411 home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.0.22 - apiVersion: v2 appVersion: v0.0.21 - created: "2023-06-21T15:41:08.881779457+03:00" + created: "2023-08-30T22:03:26.167631+03:00" description: A Helm chart to install Vector Operator digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2023-06-21T15:41:08.880872969+03:00" + created: "2023-08-30T22:03:26.163838+03:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2023-06-21T15:41:08.880245588+03:00" + created: "2023-08-30T22:03:26.162047+03:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2023-06-21T15:41:08.878989165+03:00" + created: "2023-08-30T22:03:26.1598+03:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -146,7 +159,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2023-06-21T15:41:08.878359896+03:00" + created: "2023-08-30T22:03:26.157854+03:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -159,7 +172,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2023-06-21T15:41:08.877777085+03:00" + created: "2023-08-30T22:03:26.156056+03:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -172,7 +185,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2023-06-21T15:41:08.877186609+03:00" + created: "2023-08-30T22:03:26.148554+03:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -185,7 +198,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2023-06-21T15:41:08.876593719+03:00" + created: "2023-08-30T22:03:26.145246+03:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -198,7 +211,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2023-06-21T15:41:08.875668738+03:00" + created: "2023-08-30T22:03:26.142239+03:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -211,7 +224,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2023-06-21T15:41:08.874998669+03:00" + created: "2023-08-30T22:03:26.139731+03:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -224,7 +237,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2023-06-21T15:41:08.874332656+03:00" + created: "2023-08-30T22:03:26.137808+03:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -237,7 +250,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2023-06-21T15:41:08.873797522+03:00" + created: "2023-08-30T22:03:26.135614+03:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -250,7 +263,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2023-06-21T15:41:08.892271845+03:00" + created: "2023-08-30T22:03:26.20103+03:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -263,7 +276,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2023-06-21T15:41:08.891746593+03:00" + created: "2023-08-30T22:03:26.199897+03:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -276,7 +289,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2023-06-21T15:41:08.89117635+03:00" + created: "2023-08-30T22:03:26.198726+03:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -289,7 +302,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2023-06-21T15:41:08.890301789+03:00" + created: "2023-08-30T22:03:26.197256+03:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -302,7 +315,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2023-06-21T15:41:08.873209435+03:00" + created: "2023-08-30T22:03:26.131117+03:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -313,4 +326,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2023-06-21T15:41:08.872531089+03:00" +generated: "2023-08-30T22:03:26.126164+03:00" diff --git a/helm/packages/vector-operator-0.0.29.tgz b/helm/packages/vector-operator-0.0.29.tgz new file mode 100644 index 0000000000000000000000000000000000000000..92c27da5e1f5f8bf26f6000a9bd07600546487c3 GIT binary patch literal 31091 zcmaI7b95z7)Ha%lGnrTu>%_Kgb7E&=8z;6iv2EL$7!%vJ?d06NzxVs@{qOc#wV$VV zcXh4RRl9oc>Rp79u;^g_bAYJ8XbdHl8I2|7*kwJq*o;`z7=NpK__E1%_)B1*s0UR!Ie$DXpyg5kO^17Lb#A4`vdC!s8_j(yj zVfcj0x~zuJ&?W{HXq)K^K|AD7Kb#T6jZ30#wpJBlc)_PsiB0?KDmc z>rTFq1^5XCD2Cgxu!b%vLM_LnIF_IW+B_7sk4<20AhYr7^4#ZWD7P4UKQlbKw;lU@7}wQ?OE3% zq0&iY282BT>;*_?*Gx_qt3>=6fER=H4spCqRgr?6eVXzZ4S7T!ivYCkz(uQ>9eaL; z{L9FM?c3knG&5%w3fj@k-|XxXRg&NCBpo+OGz!52kjctFB*d!H$LT2hbJ{l;iyoN| z!Y3vq3Lt-A*f$#&x@#p4Q&NOLLi%*7RDQAwmK%aph_0tv~mT2jm;5204p(3pqg zX}_8~(tqR^;Zi^y!`w1sO5V4=VhK`y->DwK+W+DGxd37$^qRVcC=SPo{LpBq(|&Sc z8RQU)Uyq}tP|nN)yNqti2ZC-ki&MG^~!`jg>Wb|#8Ric zM3U$?tR15-ndG%x{30VURR;0J`|;xragxu}ec0JELDJ>R-9DU?9;P61^MFbHuGO`F z?p{mGLwuuXO}&(6!oG_1c_eYuiPF#Td6YDh)1Q&v&l1wg%T)~BMYX=Z)M2H3&%5`m zReUcCCsyT$T)w#|T(KmaP}Bc9qC}nL7m77e+W=gBBfO!tKFHx@4DlK^lwH2Ne7`>( z4=*#B=pQuZO(OR+J{0Js64nWXTRod~&fR1J=2yhLHXoBo)L@x|&1#Oq9Ks>v>^+_J zkujz-jDy&Ssiy-N*?Ixu!MWDog6MFC;tMtN*{K7LrXC+}v129|VOlnIZ-ow@F|y+Z zXxGhNNecx;oU#jW2_lW1rRsxf=0`WJJj$3XSn>~}6Irm-893f@^(L{d)j}|8woCWb zIQ3yUJJA{iJO-GxrcFyNV!Pk!>A4@((#89R=$&2c0WGeGn6&W@HQHK)B3r_ z(Z8w!&tMoY5WVPEN;*%Z!pE%z&D)-9KM+$0TN!SKq@ie=OMwM zhLMI^(7e*fL@dCI7=ZcD`|ejru=~D;SIFZ<&KnS@qzo(N$QK`F3doB}&8g(JHrF8k z-7PM8m>SMCrEyQ#p{+875Vms^A<@BjGt+)vSeoL2#=6EeFn zjSOXhgU>ln@p1?;rKyNdcYo^ewZ)PzxSi%psL&l!nT8i%HI40j&pN(3O*cS!eT9Ec zD0Un4k{8JRkP?fk{rS1r)iM2qLGLS~D@Aj!;*aKnF&sfE+*1M+K$gSLPC4aJ)rU%t zgT)FU>QxD#HZ76hDFK8-^>NKG2X!fy3Whddj1L@S@H|u+^^%az%s5|p#yG|!(~c*K zWlG7W#W7DrQ_IZMBbVoZ8yEW%IqPTP<*MLnd@L)Px3SiH1bJH=KSVrojDrSsP9 zUZs~zXwqD}OqDsF{P)v%R$PX**}qfW>=#Q!$Jtc~@4Xs@IR4YQN83P@7_q`li)DP> zj;7JkwWh#{JqL^&jtmW@kGe7?n)yI!t(TXhcwanraUG`ClNoZ$ar}I~cZBx0!`KfZ zk~4~TUPViameD=SUXT$_X*4^6uxbr z>jT$(V(!g%X8dVzlh5P^yL}pXm`w4Tfeh8^V(c_Q0Hl)+qlL)zdXN&{a5S!+D^qjF zR%O(0xTNzmj`t>&;6Q$mb?Y`j83~tgECXEc4N=GKFjio6OWFtd&p~ds)GXDYd^DUD z;++NfjI(hhZn%srwU_j;3B+NAy9R7W1?VEe$|jnIDNb~=35nkY_plJC`(^^X470&L z&6Ydu1?E7M6r1k}!`Dj?qh^N^_n7_yO?>E|I>>M!h0CoHoxjVrAvsj;+Lcmvw~gP!1l`0>aAuWMSqOrl#M)M7*j z?NAw~KiLct;9yWe{$h(5lGXi5B!2<*)^T7TZn+)ovmrb$6v z&+i!wr?*>240>KHt6!$dw$QWz=jS_{VXDnFecq*n=R7Es{YRTw-A?mxvyK}GTP1VU z|JzZ?+%2ISj&-3UogD#UMY4Uulu=CF3`ukAW_QKm?iSRZ%6$C&dJyFOunlxE*|`08>VR9_FeHF)b`qjqL9w{CXuBZSQ`T+W&5|a&Zna~~a39kA@~$4S z(>&wH|F(6c?={m1@_t#M1o6o@#WGDYD}_7C|B*X`+pw-)qBu+&Y$B3E@DOXlm$pbY z)WrRh4MT&yZqyHp08uX#{K4)2=Yi)0ONF-JfW~ZAyTp5gSp8#YDI^O=+;D`O zN3^N%5x&cJPrp*u{HWH%bN!FR8>euS;K=G!i>&$!OB0;cOO@%g$pg@8Cza<-jv}V+ zm5Za*;h2HAkv^m)>sa2ftEm6%TiPYVA}L$o+vY3~jaTs2@jE$t>o}Le^=M;+hr-U^2vLO_;9}c#~`i@8K_*FXCKD*M`A@(bY@=@uYYKzyo-M5BF`=j zd^cTbqaeShzH>Q^ueOuzo%$h^bfT4XGSOM03BKABVo0S6gI6xsv;AylwM}}~ZhaL? z-mO`ql1>fGQT1~?xO1nCmUOHk({W*fWgr5u@^4fDYC_5@?e083j+I#m%h%_vANTp} z_I-D_<*LTku7;szJ21sf{i>SbA;-?w-N^;scT>0PZPClq?cwt6bSfkA4)^(OVxfZ& zM=_8YpC@*273YKuT`ZaGPhN1ssUTYz-(OXY7HTu6cK2wO zizRuMw)R4w1f8=z^o=4JD_hxZ7IdoacpW6`DNtpZS@G>Liw7{0yh!A zF4T{^0OPH3GTv+_e_PqZGgeyPOlU8i$+ZU#5N9nRe1$U-i+##-a>81stC3SbXVma+ zwj1X9=lgjB$oI};?(^w^L*Ivg%y(*rC@}{se&67MAy7YBLC_9-iI|8QUb3tL2OQ1# zq1x~}r*Oz_@5PXhTqEm)0Grt?$N1V0km?`44P3MFr{QBaB+HRz?bGnFTJfX5sMS!b zSj;8Xbu|PW`UP7X1^PZSfu4fC5)Cyy09waN$8e&I?4);JxUgR-Tykrh3+3%kQ+j7x zE}t8ey*A5!44-?EbEfaZCs-wWFTTl`1l4u~=m2k`7bpdmOtePOu&)S1agJ|WY;iRu z>qm7eN2R+L9zm@|M8ia9!+ zitYNGxD(Kg2Nb~%In_68U1x!6x}VF#LH7$O1s@>IRkLGg87ar;qO}a9{{1N7C8UKNJ0Gt-Ut9b7_r8ZtcCO~5rCZAZfe2OXZiC-j& zmQ{GpFgbzi!@xiBv!;N`^Its1?vG7XU197|6iAH=Ifg5r&IDcxU=DFY*8Ar!;vg#) za1JVo;#z;>qwD0{?gj3B$){E)vlO)K8v*00jFP@{+&u#JCV2kgLru`owWVP8>~r;` zl2+d1A&&*@0fD|^nkxr+58~;EgQVpQ9MZkb#39I><6)=IrLL2}Fn3vbelBAL8LMm% zW$fT6IXlQ0s1~Upew_)bk?*lR_0`YuVLSDmnoUoKnKVzLYbMy#haH{M-pgqRIE_(m z)3kGZq$?6SkUM1q06Dah;-0nlahfc>5XEG4E_rqrEf_934|;sFEl4q>w9k-w zvAAuo{xcwKco>cM+`W*QB^=KKTF~B4OjrsIE)!*{OR2c}+t5#H&W>J3kC0+lxDutx1!g02+lkui9_D+3TDQ91qpb+;8$*y6zXsZF!ObSSr;ZptY$&C0j z@r5Q)*i*1PJ#9XqLEQ`EAWOfZstSl&Q<;GK3_n`vzjzr-k}Y=`XSe#A)v3h~oXHM? z3!0AnHuoojE^8WMtfc{$F`7K68F7{d(A0|Z%+VgDGqOrmH(Y(=VE%HwMuLupHwPq> z1d{Yh@+92`_jY79hBwoKYY7PHwdz0RSz8zDdR27tGE4P^pq!4eM-=b1!VwcEJP@81 zZvTXpSoD_aWP$PIHh|w^8{Z1XqX}W5WlG3UO93J?GqMcc6lnP%H)zq)pHTGDES|9 zDfp8cIt)=Im`*A6rOz=$Xi67R8p6iyt2lp)!mtk@YGTRGw+f-RmyU-oqU3u3FHOTl zW2-ZinT~>UAzW-Y^iqQIY_3-Sc-}Mtl;dZn!lqYL(bYk6CSZoxXuq|IkvVJa^+)q9 zUi23-Kgb!t?<4(It_2Peu80PTC#8lq5mvwi^FB%bCH|i8UuHmCRz8H`^-j|mnz=!p?+-&}F-;*7YW1;tPb;p4Fx|O2u z^RiG1;*)s$8u|(q&te+!P*WB~(T2}96{xPK0R&`>vTxcdpX?UiMHajS7y|QjQr6SY zIPlEW?garH|09kXm3nb8I={r4T_eH_=yfQ_?ZlTszUTRL(4rp%dO0Hf{KwLyU-S9I ziYS2A7NphKjK|7F)i*j)%dh3eR? zvCpnoc|bZCJFSnd&$AZ0)XjfLmGpD2n9X0ycx?PyCN zw&av*O)2K|qapR?SrAM+EK=0Qivq!GRl2Q1eI$1))9umLyL^X`g=G7{(b{&Hh*;0z zAR0XmUkNze&s~#4;uL=b=%$_T?xx{!K*$3g?o}WGsP}PNuJDdJ$m)zq+NxU%d}SSe zzXe0dURlOl2|5E(G)Vl(Fr0LkiZXd_h3d#!k1KvX{Rv)WjL@eFe)~D`J-igs6qAk% z3(~Ml@VWviu^(zTZxmaISE6iJv3MwSuBnV3NdvVAyvXZ9-m=*-_`ImfXFzokVE*kz zSeQhqP_Z~>dQdsS7)gnnOr`Y^E+$z%MOnDePb;%oRH}`b7af5cY&*T6|CXAm^5bXH z@waI2L*ljOf#I+bb5G|el5A6``CR3KoGRobu*Ll%>J%;pOiySjBW{}_9n(%HW9UEk(5a?cvg zFpi)5Ont)FVMf}#9DbCRE5)8v?+KfBwIfD*TrfoERZV$os96tG_Sniqx~=HwNf3td zx5DDl!U{7(->lC$wG#R}a9kJlneOV3Gt_irP{f~pA9UbYqGO=_Hjz`4!@NcKA<{uX zGr|H(VKHC2&GC9kwQ(sG%DK3>D2k=w`;+j8R?3<5;(@JiVEr>>VuqfbEvt+hM~8-X z6sURr_CA>lnS3;erLItO>P7~U2Q8-bSAOrJl55ixxrE<`#55a^xLvC%sN+>I7=8FS z9_=PX=Gm;LO#E28tO=yfw}sa#=}FM6k?#~@pS_oquBzU2%06|m zKV~v_gTc-|*VTxe`;TRkoQO?!g5eQx`lwLTa$3-_>-~(wjHPR(?zS*tS9H2dlfX^jhaRfirN zAs4I{LsRG*TKq%fly~BTxTf8I;%-}9v&P%Hpps+Ev!Jr2?t^s9lg~F76D@7m+Q>)l zHc@=@sVTU=W#%5n#@JRvN(M68AX$+kr5%cC(;qvTdHXxg#r5F+;EBZu;uT_oj-9T5 z!USeJDMgIj>RYKER1OlUvwo0Gi=19jRaeSkIqil5+mqUfktoZSrahl@TRRS!bhxO- zq7Eu6Sy3!^P;Ukp+?iN3irLUxq3c#-!xO>m+u4?|N1i|h4$E$3B?LnX=_&tEL&EN^hZiWNoQkHhJxr;5 zHSrF(B_2Fmn$ou8Mo_0KYEudhZ;p`Y)Rs>9imLPGO%#P!o#PTrD8>&&u%jjS#t*ONG5@CY@PJMciu4TyAAOawh#2?(1x7h4Rd$S!og1 zq|5ZH{6~})*VZGI7T+LEy38IjgI(#YiOJ8PufPZu{wjtdF#CB8?e(4jv157apZ<2u zFqMWWpb{9CHQ4y4-k^QYv=o?Rdc`#pHRGXLBz0dM#Awxt*FoV%m%#U+GS)NTSYcwY zABXpf>HJF;qQVemU(X~r-GV@kSp4&{2igSAvt*~t33efbLmSl(zdX9=h?u<>8_-=A zI|VGTh=imR;Ea)qVvbk17e6$Y*IZ}jW>8|u*+g3*U8>=)+>}>aV$tacOHcC^VwP@G z$fmhNI)_$M{AoE2zSge*HL4ya@TYS&N9ISgy$#Hz9<^dnaJQGR*a>lRO5UdSd}a_i zF~UQf;?;qYotG_n!vbyVrR$ZLPL}9}>($LF*g~O;TO&egPxT=S_9NT4pMTw)HK(8q zhxm{Xbd2Gl2+^!ewkjVOP_2 zn9N5Mz&JI&0`M~LCqJ~lLO4G5^6pa?mHbPJH9q>b>G)K3Ohy04t5Hi(>k!Ht_6gK# zkLjD1j*juN8tMhbWy9~_NB>D58QBXWfA3n&&Lx^P><$!1OM)w{m-+mXnA+MNly2Ih z`|ro1G5cCWHvKEJ*Q-0KO$TKLi7Zz7W!z?fgFm#;Ed^{csdYTxr@5po`Un^y1f9x7 zhG|f;2PX_}KNE?s$;eXS8trT@VDj@D1?Y&Y0xXjGc;8=A3T9nH69!JU&4qr_!M4*B zK;;!gV+NVx(!mx$88#9{VFt|vY+DpS^)`QD<4k~})fczmX(aj~GhqKSi!5t9{nT;d2fQQZbyxL(T3gVWgM{nvWnHf8~(iTbkVkB41 z^oKz_qyC>om8vGR{E#C0;exxreM{kK1{@E?2!KX$$y5h2gtM6-r%D>m16zqQzYo8J zAb}8lVLkcZ zDoSpqH3>@b<^JJirBhUf3J&toK;`V`@`sX*6rHUctG=b?w0|?#9$yzdU-Ae!rHV=B zi^ZLkvrYDU+tV1X-UBc(N-Rnipg|m88mBVI1y|3ae&<-uYu`p|a+t@kZYxU(u9Eq= zy8S9xdEb?b6!dGiKacYkUQ!$%7Of@f=KtNN6>usP>K;otgQ)o8ngVtXy~xSmpu z@Fe9!`yQkwu+Q!@!@i|AQNXzk>;4DbQ`!1t>yC)3^UPRwUT*H>k?0Ws_c??jE7+nh zkouQsYTH$>_nX!0@6Epyw~+j0Sd8Nk%M|1!f8W%fjvpK`ZH`{fJixOt@Qf8JujdBg zuK2|{dHGwb0A9c|6x^@RUw>zHuS4$+|9oxgwvV5w_7FsDyQ2)Q%Yl+Q!T$(EtAgsz zyy*7ZQku|imea9-y@K`V8h0D0SZXh6_eP79$<8K?tz{2owJ8|I&mm=KovE zTnN_u%jDJn*W5+EzF^HtdR*Yla}{u+q?)>FWp36cFO;*ELsB&4f~Pp}e= zc_=pKqPVxs|A8YnXFfu-xd`!vx<5NB$z9Ay=T}|D|N6YT<^S~i<$}1b$9&S#YAH?Z z|8)M+S}D!dXXi~Kb8i~)=xxs5OM43|AqvyAXD^9NVI}@1<6x+#%siV(Kb>hC zhIH?u(BoSNnDL&TN>*q&2PdmJ;1$B2bfBJ>z%`v9Yl!QAUHbVXlGdCTBOOVf=70?g zBwGKu62?+gs7%f??qYbzH35S9+3}*G#)S>X z1To|g4woa1wPYyBKu+IPo~WAjz3c91P6t`l|IP78>ssnL@gW4b(kmKA|d?lSvi zLETBOzdK(scGd(OSbX7IZXyD1dv6!#xAYxvRjRt)MZI~@e{`<4Q3d>n z5D^={p-2Dmu|7}8wV9MnHcn39ca&OSgao7lEU=o#Ttbj?JK&lvA@f$(eHw^V8S*|S zODF?R#{>(0l&?_+#xR>#MKhZ(h-1$;Dna9$Oe;ZO%!y+=ckyNXcvS);?!ReKOi%|+ zNB3gzs-Kyfk-4u55@LnXr2ba;OV0|{2>$?iDcq&BM7HCgo zg}YPk!{h*T`x&$jJl+JK4gJq>Bh!1{roBy8_JLzKe7?M2xK)Y;80CxNG^z{YG>H}k z7?zE~(Q2jqHjTnnEi-7`p2BOZ|35F|qnR(yyB)h*F~OxoICqN9{<$B9;F(39D?uo{ z89SEaXa%}0O*$&196C(Q_ix!_6@EdFgLn*ah9VP1uL@z!zN3_**tsH}hhxVO?Iu%X zOGUJ1XU`WU!+Zsp_nI3k+b#P$%J6?waFlQWptm&}d@!t-)-V-K%cDz<$k=!I0o^CR zHl;ai8vKcOILVpDB>88C{R_C*N|hespi$VJ@ajdqS)9)L@%E+NjK5hf5$&IZ0E=)@ z)CfrxTHG$k*MzM&AJ7wRx=IY2GOckfD^`cpWBVK`fHVY{N9_rInaA_rr&z|7GD zaQsTCS?WC#tM4$?li}<9Jj|ABx|6<}ViZ}Lv^-v67aaKM?(w6I7Pz?FK-ambSL3hl z^7791|E)`b?yqNyr%t1`!7d*^Hn|=t!2=;WYK`0ZO;{wy?(WNK!tQSG*(?1C z^ay;e1<}1L+@H{CBpL8b07dP&MVw?h*{}GJOWXVlbDSw+(eWxLh6Q6hx(t|~J}Ase zQ0ped)HM6^+W@9o_eIDOwAAA{6G@$bO5*Z+w^lc#B+i6V8!T#T=Xuse@@E($zFGKJ z!9JamB1IR=j>v`8(2W^un}_$%`k%&uRAav?9--% z?=|SSC4+%6*R=-Wa+^-WNc?{Gkw;z|F#G^Z6RVn+;h`bTr$nTiN21siya(2!)5Q~F z7z-D$8TqT{S|3;UA-R-L>nhBe*1atPL5B^}?#nd-LD!&!m1Q5GJ#!%6#u~5rFyD0JjM|wbWFZswm`2>ISo4ZvqNQ5>+fiy*)ZvglFInVJM zsqrNg$O*l~48#5#;{Mni;bI^I4p~;i><6zHrl2CUA333{Q_2kJE8dwChZk=6&l8$n z6o&`Lp@WtG&tuBdxBI$shWq70H8lJeFTM9`B8Mz6oJYxAmAJnc&BBb<_`^bqVBAn+ zcF}b6rvA3VdySEWF1pEMspd^RoPNH`R=))cS%2!oMz$j$u(}L-2qFy1z0T3QDb4=5RYs0PrENVK&p8#$?gZ|cYO+q;&ogK0$GgRQ8#4L&Aa`3c!WzV ztWQ8)Hy_wTTH#vYX$OTpg60+rHLS7Nv;W*rlVDcljg8}1f_VDJ&#>wqY~7w_Ytn&F z0lb0kj?ZuCx`0pn%#YmQnt;wg`$q^1OTZ_J*;X@(S|PT;Z$bguh~U;O z;n)o5omrDUu7-7~S^Wp>Mm536KkJsfYGRiEUky|fika`g3ZcW}(yI9xwhbTd7g#jT zEY7WnYfqV;C^B6t`$xi7=O2UtFL8Wo;{fM4OzK9cx4Qm%&4Z7A_2 zjAEtn|6ph(5F)`>bluL1boVo?RsTOoSy~Y9?9{Uv_=kIQ%if5>P^0igzEu4Gt6J6j z-%}I@yQ7+@u($uJ3TPpNfBDPD3*zv7=0eNUUxOisT?_gDgSD?{`G2G6tR7~POWA ztVK6^5@k=G(xwa-GvIGtuNbPIo-OnhXu!LnZxy~qeSjHgvj3pmE83=ZZIo^4`K zv8wC$U>PbF2UkaBIBJzK1tWs+gs_IN->9Fdb7FCUQip)S9}FXf=XZtYft)uJWzOU- z%JPMpB=`V6Qp;qHhE~S490K-DL>AHIbFw5%vhYJ{9z=`M%%H%3s3i1{yOR+lD;2fir&%#PQx3bkp0SEeg^J%EHcR5o28@vzRAIVVB#69uDh zm`9ovhmt_;=Ud>%S*$LQYF@n*xMv*ORMruoj%>mdohynIg%(m%Ru(SPZ@s5gGhNQ# znLp==XVh0n@e8+KW?si52_YtzV|{-s&e$0uSiD>zKoDokKCA4A?{6=Mh@V0;`^DBT z0u4R^Hm}Chu;-HDaIcee5U?J#cg15!;KyR_o@Ij;LyRl>N5v>;`y{HqcJj1(1p%t7 z;oq3iGljF0dL-&8W_8v08asu3q|8!{$?cfy`uE(!-5lKy)$IY_KKiUP`5vS2%9Uk8 z3sectCqn2V6-(0)$R(g+iI7-o5^ck(6v;qcgi?r;Q~^mZm$H{huM6(4hD>{)8Bgq zr{oo!VZL|o7AAva$XQVn638CbR%U5?7RkIpD&~jGM_e96T-;Gy(?LHL3#eWN07t9T zvY6Z`%+s;fP3CYeD@4)P==i4t8tKJ=VvYFUE`?}8Hn&xF-!o;OYkGdHMiJvc{wP~Q z^g=Qn!g*Ut9?tIUL6>vahVd0+TdSv=M_Gk+-hy_tUx>!8qW^x z6Z*zYVr!8^bY%>s2n#(&QohZjyi^_sudSE$I7$TKG_;|6!@5<0>A?*-sAAbA zt4UhTYcm$X%u)O5Mo8mB=vzKv>)R9Gq@DvA^YRFN+QsjthyTF?O;6T(3( zyajL<-MUNXHJ4WQA$!k&Nsr9LS9hT5^ui??}OObCXk$4UDsxnwGZtLSv zD(P>m25`Ag_@W$iy~)-3c))zXX&Q%O)Jj&;q+8%_fu(@Gp;QgEHe9<0(Cq^h1fphM zvojOV%_Tx?f&h;1(@DMbbEahEoFs1l!_7CoTP*1?pf@%_gh~WYU5Pe5us?C#)HncQ zg!<#^c8voT{#vp@SWJ3gg|fAb#lv$|F%@#ji*j6D>uYA618FX+F1wec5s`EPZavV^ zhGZglsUhZyUNJ4|^c+>4rAM5ym+Yhy2F=qrFkEr$#D%k?TNJ(#PvegqB{u<_a(MbvjqtJE&LJ)yh? zv7s!reR(}#zVl2cqt=r7?`zd!)f%T&#fLGz;O2GtK`{UUdJIkDn@i<%CxF0yqGIL+ zX+cGIggBI%VY%&@{YY$Bx1Jo?8|V#ZT|6BX#F$pYidY1_*Lm zClAZaUvdw&Cu!ApEUf&61n$O1)l|;@Q5Ja2J(#}I7L}sCl-VAG-c8Z>tDJx65%vx{ zC^gOit!qaXzkAw1ahS$Vr+=Xr0x4lem=OTsY1=4F>GJA{_&r81=PCkQYQ9qGBBH3y z;XU=Q3hhPqnW=XW+#qRFnpl1++Jz4D#@pvBYF56;LJ#Tmz)`9zm1{gHvBM@FQD@?7n=%+Lll{KqZQY8weHspEdzsKU5e72q8F-AD$ash|yXh<(-?uQJokgd3I7W@KyV$ zz{q%@Cz8FF&n)p7&g2*%hZYiG;_n&1cPdX+Y_&wfL;i3KiAjN@HeN9ULepGvDe(5f zxI|}Ah?e|V>!k$G8PV9TyRP%|RKtZYt;!kFh?snHvY|a~uZo4b>{2kK{T4zflpj{I zA~%_|yA+s{frYZf;oo*8vkX#{-vktFh`##|0o0gj8KvM$LB24fB=$oYO94ewgp%2F zsfnx;ScC^!h`&8~`$ps8PjmtEI;Dpd^0ka=J%~Bj>ZN8t4A=h}>i69(wNwCU?^V{uGJW@K5wf^FwK@zHaH7v9&RxWwjG(m)L23F%6s{HBAKGx zkmHYh(t`>_@dH?c+i#11-E`BB4_pN>xH(9xuB(Ua3BHw24nw@xi~B)$%nQC4Va7N^LA=rU62udo`LZBrq$pcV;I6I;!+)OFV}ix>vFho&9M9 zWjeRQYaY7Fp6FaCV!LNlCSm(rQ*xCtY&SSR;&BN?ej?}EmHOgyY6aU__V#rTAbAo5 ztqyXhh)4xq7EjB*Bga$ovf`&AG}ZZYi&K5%dLGaIxFp+Gw`aHTeKG$I7R2uSsBxin zB(=4lZcbTNtk(4yB1tBHR_0)%Ags>-%*3^bIs>rM9pUx*J3&hVkvL2K&QCz#mY}hg!RMTi ziC!CL?`h-y&5)Khzi27wbF)p0B>c*8-R$h}@$hz8YibKP}gnfRaJ9p8D5D^U4jRWF9ojoZvDIK=Ms-u?TJg2!D$o4Vt$-%no*Ixu+qkjAPo6OOr)bWHNM_P6B7FiD(bIqv8`)l_(7ey za{e_AkNBD!s0_k#q;Vv~aTCl-0jI%aM+s$bxg#*ULp4&WI17{8lfef44}HOR8z@y9 z-1ZFcPW->GAn`tPTM0dQl}PAJBEyv>$hBmVDg1ccZ?WGQa(!XDfWQ~bfI1i9xK)Qc z5~ea5l^~W^z5Yu4Kn%7Yht0V+J#GbE$BR~zO}~BsGjrP)TBt|7D{!!=p2~L#S&@D9 zE~qj{2oJdh`R-xnA#4~S*V|#*x9wYXmMOeFhJ^-s8^vuVMThoS01>o%ZRkbS-wydf z-4%R@(;36Z3d(Dc7#Pq8mcZbt&Gs;kXo0w}HUK+zG$WV4Z%^Fw zYj2evBOC)by*+WU4E_Ft+{;wd@Dj&2)vS_^TXW~Ley@tTt+(m4C+YVob1CH?zTK)a zK7kzMy$iS)$4n*4sL=I4Ro4#h_O!wzGB@H|?fXAm~Jhoju{~||55`;)= z<`?cVtl(YS_6$SrGCa1+`QVr8UWyr7_W8-2&E{+!p@$@|bkyN`anit8(KAqE5FbM4 zC~0CQAm$PadG%kW8a*<9%fwToc##06ZUXR9_Ssf$_0wxXqCEIICDkrnr6Wnd<11+y z$ZzD^q`Ch2h@{y*9MB|r(FG>~HH^V;3MEBo=rqHHX?~pxg`XH5Im*Zqmcr89p+TaD zdj^W-cl*Va>IUTx{ThHz!n*-4Bx6vlnA_*U4AMEbFg9K%$iWXvukALOAc?Q@UYHB?VL}rADC%qBM9(2zEMb6fG2h+s8JGK;_#aV^$H8cGA z%jCG0q`f~-D9|H`tm_1vh^HRr*sp)(qMI44W~01FCeEh#kC#jK!>u{vG!~3O7f@kF zVEOe?v>8ne$5atUl`FpXIUu&&RoE-2as7m#Du}B&HE#P#BwPJ`vyAtO(o6!WwB&DZuYc!plmaL=P3dh{k8*-0_=x__h88N=GhRDC%vI4H2&xwO8;7Vd1Klkl<|W1AeOyNiC*qg$$_>`>W8DKZI1QhLH%RzR z_G0bq-*+mY)bu}|RPp0Ild?;GRYl@t`$QXOUk1%asM^SO@d2p z1GSkwhoFMNr7f_SBhs$nm#f>#!y&8gU-EO*{OQ$;`P*_PELc`jnRh`CL|Cs=e_Ofy z<{y^?j43%Q>MYFaZd*~M2sTlxy6G|9y~LGoS-29mftw4Xq4r6uFAqDi*jG_m+^qf5 zb#$Tj8iWCy>G@VCA|*l7wnKF90%oPos0%zW(*TW3>~FRIB#Vm9(9Ko_pm$`LCT#e# zR7g!gbYK&m#o&s&9i87IWm^EHJz-J0<+vMOm==Zi;WP2Iuj(9HK&tU9;4ki}G}4fil)0@VzHI;%Z$d~D zX9tx7biW6H+4trjNR&;?)EIPSZ_PFZe61g1ODTppWui(s7QDG?vk4bui0pc6=l!hx zfJ*>7AAE$34#U@Cc{+JTN9|t4zAhuY#*TJXK7CyJ)>eYZ9R@eahHMLa65Msmjv0}y*5iO){?U=I@3*%az zECJH-pbgygcIvUUYh$kvm`~{qCOa3vP5Y~v?r4CDqx842?1#V~!Fx#_c*wv}f|>bthWSLFtiA z$zl-7BC<3Ju~Mc2EO|ftJqke2K^+hB!Rv$36*h|SshuQfxIPSc;$d)39>le6d0=Yy zcb+^&Lk{_W0zf#w$KUX)4YfnFM8lh59O*bYtf|w z(FGWtkE&aw;sV|5@=q%(_#Gpi7YZsgkrp-G9z(ONCD~4@bDjfQ`C-g8;PwNJi)aZ~3FujrwL~gG7IeqX2^aX@S6y`UJ*N{KxUP72O-W<@lq#Dk7!D1t@mYn%@qr-1F zBUPI2yQRk^R$d!3A~B*2y_mi@-Fo_a0iuAr6|O-oSE?DQ!jJUX*t$nK!SP#`gbUEG z^#+9(C*aVfHvbYndd#ULK}HXR4k6DG@!WMa9o1b~@Q8nyKJT3yfwmRBj z@jI3yw;l6(K@oJQpH!kS=kjRlP(?`S;^Embc=@C!P-J(Fi30BFrljgf<`ga>z4 zxUX&90NJ0I?e-ad6hseuriQJ)CS71)q0z~6@y?qnvyM|^vjKJHf|b~HHct&<#zWU2 zgsymj9V5J- zez|{)_CL&p`X7^XKyFD(&kSXAlIME;XT-eVrMnglv-=I>?SsaoQ^I!4(W5SZr*1tH zXJ*d7v*I1-hgFMsn&mvD<&9?!lMm)OIG`PjT0UI^t?lFTHM^Ca57;g?@%7v~0gqio zH-D@gs zE9Hd=V4CwhvJjd25$`?O9k~pCD*@lec5l`>|3BnGx}3O(Dh|mSc~L> z3fWaZwWLbLeRauGEfpM|r}%H2p{qaLneH9AljtS-ypz16v>7|2UOC>A7=!;-6LyY#5|ZjMgoPOkF}furA;JhKb zT4|ppe1^Y`d|n$)&^`4O!x4FDN%);FgLusS5NF>d? z3H4@gsm&RPx+wXRM18_ARv&s`7}holh}}c#@E)vr>m`e6A;_B->&~AjZx# zz5f?ba*aBBq8H}m_Uiod_0=?uKU?z0iR6<)kO}qHXAG#7g!&bC3#ple48T=i^PpU+ zseNmU$D!|qj(Y+druO-GhmvzHk5P_Iog!>Zs6~?KHIO+W$BtzXbp*@1BYrALr!rjC zhQ22Ecn4m)n!>K0of^WPW&`)V<$N*?TATmsz4{8=xt|AV*|32nhp=F^S->*;y&!pg zzY!V1q(B*>ss4E{{b!!=kV_Ui-n)o)`C8j-n~zIhe~7vQH+3PGgh{HQ@2q&zHvL|T zVETT>4c@0{>sZRPvpqp6(`TtK?1xirE@@#9Rk?dr=2mXaam*nq&R^eKtyqma;W(x2 zljAmFGP4!0ww8q{i@R5(VTy+5>%y@4%=im!tV8E)V5| z4<=b$t<9$yz)%`X{g+^gv-)(#QtR#YnoPYkTf-7U!VY$_p6D4R*H79`5+plqImO}f z>2~r`#qR-FM(Eh7KoQcEg*5gUHIpR(qmH3A5!=jU9lp&7yOTJ&_j|d1C}LB*1GNUZlEwqam-99~*<57h*2sS&``6I4b;6 zOQAf%+$H@30-P7?tTI)fKrKcOe6MaAXSH51k(G5Kb=AmOdFS=;@G`_BiA~CGS=;^e z^z;-&zteBN`33=WMNA@^;_k#iRMuT82?3R_Uc8v1|2TjB3c`vrDKspX{$)QH7qUNY ze5l9hf8D)(V>h*Ed8e*FJNbz|=Hs#vcgf|+J@3F>IzbC7S-p3iHetp&K4+v$_`CN6OAR;lk zC|Q{|y%yOL5B#rs3za#NR&f&+`Tudr1@S>5n@)-n9YEJ129Q?!u}N^RL2v|_7ZWN} z7w^fcf`@PEc_==hHqi^c9}Bid-2nNTI|4fFtDAr$^_>V|(zsKc-;=oH{(j})MomJQnqC&<0A(DnYT25q69-f8snZjySh=nb z2)snS=j%+Mv!Z!g_;EM(}`SJVh3tH|^bI0DI9 zvBZcS>qUNh#M$fS3YT@P_NI(c$`FB+cPnY=)=%CDBnmq_MC#D3kL;maxCEkGh=*CX z+R3$UW<4c_-D{2=0vv7|EDFH%WDcv+pt|Pj#0=q?;7K>KU2{kk3HRQC4ib$e7j&7} zW{Ej>5l9L8!U(6-i>-zz!MaV<#05MRq!IM6bcBX6^sXlI$(gG9nyG?JP{F&QRKxqs zp?#@LoO)am6VKsDjedh?jug)Vk=6=$VYTE!t-665ZkSmu5;WYZBPcp7(ele6yK5#% zKF1L`a;!a_W3Q_A+dyTTue-!7PE67@$$>GE50d1}bK76sU86Xs49^9LGR>C8bTS1XlaQ6+f$Eki=^ zh=RS&SyHWvg7j7*-MDF@Ub3#!kpr4wCte%svj&XEvnc%1Bp0fU$#!HKq=Qr_Y84nXCJ^hLp^&A zHVV#(IV`@dIXdnB;hlEdWk*siI( zXroQos(H71U=kJdYg>^$=8x?h}G zZ8LeA;^vj7G)$UW^Q7wVuE9pt(Rn*MZ%5~CN9*srz1GfK;Z#Ph@w7lYdO$VvA^xw+ zitB2zznP-iM^gd*p_xUsOC0^jB&KR5+xU-x_3O(`pn}q zUC?pfm;2QYdrUtxToc zZ@@wAT<15&S6y>f_wXgxyc^bIJtI~jnwE(4!N;}qXsbh73yeCLP7e;O2RlTqDoGY{ zKKU0!Sj1z!VO3vqAweLl42bWfu5T){wIN*s!}^bn3rD&=wI_g1$EVGrm5=OSzd&@cfprVmtfS9+>9 zZ;8x$3on@2{$8)aG;8pc_Gu_QQOK;2hU@!^7}Xp2BMroKd7c`Q{0l$eux+1Y#m{;h zT>rD)>|_l+U)4oep@&=j;|Y&NU&X<8?o-qo`b1)VoEWPhg5ZF?q&EG^=I?jwU-vmG&8bU`(o_!4>i(XbQ z#iw0%)C-D*{*6HGVy_!B!8<+htui4w`vc7CPZkfOc*J)m-UIZ@??d z1FU;lZA0~|0Y-n*Eu07)>O&(aLj(FfY!kcnfT$_7?xkA@)l(?*>CmV;`e-ZZICZjn zx{}nJcje4hMmNZ$ROP76@HOfQYN@Qk7gO>zu$O1Q_m1m=Q*k`U$rsC(4O0 zm3R~JEik8gpXVB#3fj%)-j z9BHOBqNJ>|1n$n9s0jxMGhe|G!Eh#)T!K9aXE>sAg=CJSd-XcFd7+!fC^|Bk2Y#ud z3em0jmt5w&SS%sgO>YJ$=Wb^E$jjF1TgaLi1d3M-r!=yY3t^~&a^vuW-|VD|(M?$; zLAxL?5=>KcCUW!HL?kH}5a}>MpN1LM>A9on`5)EFP&O&bIUJ zS0w}@>5Rm{%}vk2X7$KOZg;`W`-@{lr(_x=z}>vRC`s9?Nb-eFqbak%;LyNOf)d-S zo|Lrz@&`2u>g_*H{@@f&`r}{J%@?3X{iX4io0d`5ll{vLkMA-PP4%>jzj(=l)d3ME zl|BdMka0p|%}t^2Ir*z~U%@i^4T6RPM_3XDk^wVV(Tqz?9*!u6`8f$`&1^&E*JQi4O4 zlZh4U%u~RyL%4vQt!gfh%fqn5+D4JZKsv~;ikUgyZhA&J51d7%NT)YL0M6MPkmM2@ zNTmC#Ml%wC}fIq5^*MEUPQ2Z7|n8iPgrPX z5>mUoo|jPtkVAwKN6`|q1u2m({6;7o!`}O@)QIeSsujX>rA1H)OEpu1lXZcHbk*9S zk1LO&no^o{UUsObV=5&Q$5`Uti59PMSv1rHYr!NsL0n;$X%JPc_QNykqKW_?TH1LF zwWL!yMQ^xq^Pzi97@JA9uH|;NKh8zf-5Er1GR7^c2j;NRET}iOLSK?h2<`h){ z@i2dFa&=hnlOjr(q&ab+WXu8;OdphLse)*#<%IQK5GktoXno%*CTygxFvAYA0-#-D zV;O2!jC=*0JB}OGaVr8tsVCGEk#)Al0^8 z4}vLPo3fHtqf**U4n7%^Ne+i7=Sk8dRzNLL^uUb+GLAcY2XD3SJ{-wAx$WK}oIfI6U2Sgs9%#Ie|cJ81)pYA7i%CvRp5k za!K=elHpvgwC(A*HnU|Gb)SfwA;Cc*RR!Hv+Y2zA)=g>rb7mQ zQM=W!Y$)lkVATBrcmRZm24vUShl8vzy7i9s**;Ie6?TGyP&dmvw`A^QdV^K6&uEZV zDvXJgySc2Tj0^TH^^2h~s8GBgBgWap;DnTpF(Y@|u=ham5ZNrxm2m|akqY|m{!qyf zgUKE;&NMUCG>>$4Lw`Su+cc>u`e8|!d%3#~v*UgUcL<6ws#6LQ0+?b%5k*9)mh&3l z>vQ{67m}IM6w7Ey1QHyey@kX{QW_6~$(n7%L4LFzJV&f_VL$qbeL2YzY>aR7f<-za zm&%nD--gI#usC3o5!x0>=QCLtb`79T;%a-UVV!^OKo@)S!Nfkl$e9WP|6Ju2$J_UR@t zH!xL!aAlSQOdvUdHIk1U(Nt*~Ay#ACVmdGJS*+i-K9lts3cs#@kA48>ze7#iP?Ll| z3g~VySVTDs);(x2R*vhPv$Qb_XRT^39Ts@3j=>{QdWhHwBNWrNnV<`%Q8TD8_Qqhc zCYkFn&Uuy@|3rG!oC%0;$sdAYNfCrk*)%e>>O+Q^lHp%sniM_U9rh}!%$`sIvF>k_RvrqMT}sAnwp3~+ z!@y}Nx;j8DTzARI1LcLVaodH|GQ5yRE?&~bvMG7!-kZ+oS48T5xU z!kV)BvNtLDHEpYC(0oN5ObH2wj z*WUio&)luW8)%%mp3(&JoVp=;1kw_S=^qt{r&LN}LpjS+6mFD55CBoj4mUgp%&0mx zlY9s+BkMLt*8?r=kqQVK#0=3i)72q>A56kHQK!0P558cW`-^d9%e{-+5rF2f z-mV$qxm}~OD5fyo>}|8wZAM(D-A?+cw~y+pyBFLp57}DfAj{Gc#$6pA&suL_LkG8Y zY$M~LBqWw(K_irsJOIUGy=CfN@4xW}+?u*=abo&M1l!ivQmk88*|Pc@b}ROCoea^( zhs_3vajzhMM?&Z#*RUzq^ zWJkvj5KMF@{sKUF2+up|%}Dg`gjB$=t))&26os`YA})YdPa}Zq3UAE(d+-$Xp!l*X zRYnEahN#6NVO%20qNLDHqJGXGut0^L$bri-_&`*z+mm2J8cVlxpCFaCv^|KYE2C+m z@Fc$8v(bYug^Dl}SUt}}fxjE{*t|1?vR+UyssjeSq_do$izQ}^Bo6$euPmdw`=!Lu zQhR$bQk*9t@<5;^7ZRm7T2fX5VKEEr*-5lY%-%)G$^qpT4@|v9v#s4igtan^-hEY- z*lE(Jsx}$c1Jk92g^(SqX>}EXMAxY`daK)kW& z33%2+OluHnC#)Rj%ml=HM?B4Po>F0f3Cy0^8C+jdcuQ2&S3fCOz0~~63nX#AAX3pn ztVn0Xi(>XP^<)N>kUJ=A$kj%*R{YJokhS7)=+;t&cwLAxp7Y$qa@#Nc8(9}1QoG~5 zx!(&Wg*Q+!y!f16x*vXc)2|TELG|9I-SEisBz~j;l>(rK3p!N9j(j5vROowGH}tEg zkB>C&IZ5zJeR6D?iJBawW(U!jFsYbG?$=okf#MOmq8d8~=mYqDI-|_sbIqMa^-@%K zk2<&31SE*ms2l(+!Q~POnih$~jPODvEAQ6(SPK*;1SI5xK1kuhK>12tD1dP33aTwA z4$*pCtK^XQq?8a!6dUq^L?*j8ONs^U#FO|awW+I;xkUhDEFn-0T-C0wuHmJkf8C;s zi(g#*yq9uJGQwi0nDesQ(|U4twkD=3%coVs^uX4Nsohjgs@jq#0ZB>ZgUqpFwW(s7 zCCxd)%+D5sRj6GGG4Vf$5F-XvDL)WyZSrc?Y9EJd>o$ArNehyJf*c=d>|uFGliMP5d9bn)q{;XPyH(V0bYI^L)d?WIi;) zq?HMMhM-LrCBqk-8y_l1>L2MOY9McrP+POg5Gxq#Uf5tU6iY0K3^A+LWzp(A#jV`) zXTH*P$^9$^ixS)pP5x-aT2`Z^1XjBuIzhxs6;SGQ$Q*&p>R2sr6o3&Kd~!_hzW@IE za>&%F_pIOxf7hqaFZ05d0x*2~Q{k$ufPY+0n4!0uQ3svCMMNso_$3wh;e}b&UacR? zZ!fNp`Lu_2*KYIOWc2MMX)8TBM&E7gyeuM8Ik#de?srk}Wjix+Q-`wf-C-~cFzqW| zphp9Dt#vr`j%H`*iisj8RVZ8!eMy?$c{DmlV*>!)0GT7)BwaH(d+9b!(!1U%eiqSg z1MjwR2Z)wWYy;15ECN>O_+A3ckI@(A(giz>ztv6M5teA*!rnd zA8&sPXbgf+v8iUj+7lG@TgMVD7Mw^tYE@2)P-(6{DSsH-SU(L1NWLnl=y8B8OV zRed}*j32YAk{7@+du(XejLNEptWN4kI5X*Sin8%gET|4TX8MObA~9+70{D$CU04pG z)1VWqS=s`B{gfm7R*9yeotM?;aBYS;1Q;(3eu(F_LeJCb!4%T_4yFfIs7xpcu);WC z1y!fq!W{L@IYsEL&BpT+r}GVk-Zkp1Q&PMBDQVvA62HA%GVd~!+-Emq3gg^C+a!#8>kDf!yONPSr8?W7l6*sS-ZjbinI?T# zLT`C2yG7&f+Soy?RdKf)&w;3~zkQ?5ShJi=N?86mO$hR2b5!zxvAWl+qSl70pGsDJ zLSid!i^noU10VVV(TR4zQE|?SXTP5mFn%C-I zP6b;yQ*YkBgDRB}pAf)bhMO}~XS`+BCzA=7^$!24bb&ZILO&4GKMtiia~etXxa4Kc zKSefh2f*>y1%eEP6GcVI(fXQ(`LfSbk0TW8%1t_YVJAApz#GGQR@!#@6kUf^ce8g7 zMq-sFl-(bf1!3o0?K+(wPT74(%@3`^;l#PMcCd4Hb|cB&-UG1CtMmM4%a%+)lWQp1 z_uI@rt(gckjqHH`QxORo&ivu=8s}%|`tIWHy6zv_A}3;3NfCW6e;CznM_6CC_{Nj%sg~W~o9*o|;p4ZJ+QC^9}eQujlJT zE2wDD1z|0&nbOQ~fLE%Xqi_smvUK05rGfrnOuK-XpZxLc4}s?mY5}iR;r2zGr?ppYr>iA;V{K2~MJa^Fsn;WwIc7qw zYu!(FU>xQL=uk%4(avU|Z_Zp&FjsWaeSW8x>Q$LD=j+}RIg{Zu^s^qCcgpp1$8!VU zp;tjLMY?V|yg-GiLkN=PjZ(g8_9Kqt{0xfe1ysMc!vW+(X;F*IsEI?l%1nWdcNaJC zq(J}j^{JPpl0?7w{OeQi2KsopX@e|!K^|-Ckq+_r_&nA-6TybX``6<7)fhoaywhRs zil4(q_Aj5_=xSG#w^%<}bBs^_2aiv;n@BUuUYpo`5#Th|nQvH~zPxVdGk&_C?`}K!20Bg~{U$m>6gtk<6J3+}UhtU^ z^_c5qgFX7iHd>6E;Gq5nqjP}Dp-!r`+>`IDxk{=3K76~j(sDf@(F3epd9L5AEJE%t z3v@Fa!3L_TC0|+k?Nr_scf3s5`D`c$fj$leiyH$c-98Z2^aGapg>=Wl&ZD|!{?-ax+<7h}Ssd(EGo;qt=DL7>U%Ri1tzv{anBH88Sb9N z?|5dY$1Klo1-n*;bGt$DFNLae1ke;O^wYt^G%FJUVe&8ubR@ZPI}>RYg(G>&6fh6j zgC(G65VMR-IJfRS^U~oJS@5t%x$#`0K*57G(U>0YbjSS)krkwIY=Odj%aIKF?J7gp zH^g3J{7Tl9tKxybqeKH1kAY@`Swr=rGl~((YucqYW{YauUB^uDWWjF5{RvJp1`p58 z0bVm%7kcm^hx0fl^;iC#xZ0{ka?|I%Hb_vDPZN&o{4_7CKMk%4AgDb1mWlyanr`~ zD~@1S+{UeF$8EnpD!15Zg0Lll;iY#72gd4YL6+((M3HR6qVHPlDC4o)wCW8_`aYAL zEHq}~j~5&@`N<*!7opz;AWIx{8T4jUNIFGfKkP%`{gEZQ-Au2j0bgQ-+^+A@^({s% z(kXnAZX57TD16h5=&W?xcwTu-(MY$oVV%-(IRq#>$nl=dlmJA$0j znm}Az{z~R`#8!Tz3y}=nf&$_L#=8 zl_k^q-#B5;m4E||ct;tZ2x#{pd?>qeNV?GV`RsB;Y$oX4ADzL*WCA%)KR$B_@fq!g zx$xgbGk~9|VGq4cK9cje@tCjm7L6?b+IW=Ug;&xh37U<+p-&)5byZA8X+45dYVLzL zj?!)I-Q&I6b=lky_Cq9#HUenU744_r1GOzL3RwZ&{V~f8O)!ndm)V`sSja)eSQ+G< z$u{oT>DH~;hsLmbwU^Og8oMjx5f8^9Xg=_60mIR18R3;XFE zW#E%3+1Vt_(XQhlG$XbLy@c?GBwH%-;89nBU5Cdn5;N`hwhJL1(7MT$e~D9o8z!1+@@B9NiW|{T>{go*o>y(3o{w>+1UC&t$Qp zNMs7(U_IVaRmS;fVk9s(WVvQ8mu~D~7R@|6b%~DN@?Fi>>q#7d#_acaS_GPX*IH1B z*8Xp5)ADSY5q&gfHy5q;g{l-g1RE!5#!^4kfC;$5Ew}-!+n*aHyqaThwxqp0n zbaZ(5@vm?H@?rMlui4S3k43$_g@1iI{3w3>6)EKC=!btC@vtp_Dl4iJS)Raa{+?AO zsoDyfoBrMPM`OyMd0zUOajx_h^~fLlc(;1waG!uzJuU_J6jN%%n?UjWh&+PjtL-oF zgg;_42~R$I@bs%YEv(YMwDYSi?VMZc=ia(bsMaJ1MbvdtV|$3YPE&M2`jGZ@o%_1Z zkwe(mb?)mr_jR57y3Va_U)R~+_H~`ewy*2N%HP*@qS_Z$*9mTIr@E7}RJ~ghly?4+ zD?625h>18)5?L432YD-ynyPJFAq)uPwW9(ZGY01p3iHEV$Et z^x>m}c_tleEkUYU$U0A1DgMXRPuRKm5Z)`rVrw?dhH8RA+Xv>W42y#5WzqGW_B4TFyV%r#)LCTR?;g>YY=6S3V5ESS#|GgCgF3`epZwdy4~F3Rn6;Te6=bi zPZmN2BcNPMOvL-0pP<`v<`IA;YV@V8oC-B);^J^TAM!rp(_ ziyNTlgX7qw!fr6n2RdRcteaBSQD~Re*h~)>L$GR|Cy7u-i#S+SM69!rAPe;x9~_ph z4^7U!F)T&g1LBUOCJE!zl;S2+6`8OfI<*(H%8DDqJj|ALUS%u9StvK7@0~!qr-ykk z9wbwEe2VlJ1f&@zpL*z?g^8jvh+XEij9BrKyVnwxnaJgmt0S9|$GO%f>Ja1+s zsOy?)Ay=Z{MKl-R<|i>-n7k_-lif|W76o+$TYuptPi{q#vcD;}j??>sFD;$Os+8+Y zFJzHA`BA~X@(mIUnTg^$#7r;}r<%Q55h3w)hnPN7m|b3pGFvCYnWtLrRD)VafnGO; zWrvalUz6EOd|iQI;o!MeQE`LZ;Rv=DlzxN7bS!r`C`$L~4gM&);8FX2d4oa?Tqd{dLKxR&8THe;WGGt+PtcLj7#(kUl}<~>yQgaXgHpAYSMkWjmrB&S zz^%A9MYE(Su$@|daV1T7 z-dxwl(fgMde^Z#qUE?~iXQE1;C1SOBX751a7k`ZDTFN4;WJv=cJ#f8XS59P}TTD=S z&0dMs!iycn`|$wt@%gwlS5nd_`VMrD_UCA>z}HCdOUo-7o&94hw+A59a!~0Av$h{f zy@zV_Nh!UDqclBeahisQ3rU2e?)EJ;$u6(K!%9WUCaS4*tR!1iN~&D&e`fe24aC?eE(T?c81r z*mHJZi}|ZxXsnRwaKdQM^fb)$6li@QkD1EwxZ#z!<21X&_6a#A17=Am=Yf?J9s2p~ zlKln5O~*b}$_mWx3%TT3w6vznM$+osr3R!7Zsuh%D@+>#4KRMK{eA0$KN0et4U_N7 zB=6ak$v%joy5mLOl8erP)1qdx^EEh3i9%&2p&V3b8FLVIa=@P(0@LxBy^%Lr;k1mj zH5k`C%VX2Hx8=TiTTZF`$AVww@oazl1M%J1>zv;(VgB(f&6NHxo6p~v%vxPw{Ueim zvijenv4P^5P8WcJB~vy9{a99{dPqWr*ro0l(NP*kD}PuIitDZf`>@E8+gXrolcl~^ z1eZweAh9ocP1<43r~#XxpW8*9YMjHLnEqSb{8vgQnNRLQB(m9BQ?s>BH(Fzi(FO{( zV2`C>r+^_75L|_pK~}i=nrv{e2FXmxO}9;^WS3DT<{8~-R=XgaIc;h3JFgs(5%P@6 zmIdljGnUpM9{i0rE{1;ek=ChVTA#N+~^XD=Uy@DG)V>Ot$c97O9dHDht# zDvr_7LEeap+=|Sx4Q~Un$8F$o#85U6OQ?f+F%h-~8d1aJWW-nIAdW^4I!0X4^`mDD zV`9XEJ{~h;IwXvd_!nb%EC;YIJbnej;l(0c&<={B(JZFKupk!F3PP<_JMm5rJJ}+mRc3kQk6cd+{2!;4dIWwon>&KwTiIRs7i`9IgWL^wQ*qk??2k2`B9X z!zVhT@zhDw>LrBw;xXK?{oSOmDBI3{>Frne06xY`uk~Qkkfqz`MqcyaTO{rP(r9Avb?ek zU7+3lw|hRjWJxIuf&$OgDI3A>;qq)Y+~(|0UR7ebs^TkYpFsZV6UdV#k{0!n7bjXn zm=vB_$m9T>1=`msdPcq6Y+bdD*9ZySL~>B9?>#htChA9evTfU zZGTQ-ueOD}o|`}IVjo+a8U^N}J;e68ZRD-?F+R(M>;QN*r{00Izhx6IXw?}$4nK07jMQ_o}JiPf&(;e&XObOX4*y!CqEzt=v z(>5?I6P{#uTRMiivu8J-b-{+9@WtVx#ka?fs#-78+i3=dD|l%}v6~W)-R;D@vn_v& z${&i!4Rl_L@M^4N?xdI zSuT70*&z?sJor2-Zs`x36}O-##BVjvUFLvt`z;fyDqhu{=jqS{>q(iLpV?5P Date: Tue, 5 Sep 2023 14:23:18 +0300 Subject: [PATCH 235/316] fix dockerfile to build arm64 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 5a355c21..352a7402 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,7 +15,7 @@ COPY api/ api/ COPY controllers/ controllers/ # Build -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o manager main.go +RUN CGO_ENABLED=0 GOOS=linux GOARCH=$TARGETARCH go build -a -o manager main.go # Use distroless as minimal base image to package the manager binary # Refer to https://github.com/GoogleContainerTools/distroless for more details From 21edd959bbd6421ed6d60ac200377f2a15bd9707 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Tue, 5 Sep 2023 14:25:22 +0300 Subject: [PATCH 236/316] release v0.0.30 --- CHANGELOG.md | 3 ++ helm/charts/vector-operator/Chart.yaml | 4 +- helm/index.yaml | 65 ++++++++++++++--------- helm/packages/vector-operator-0.0.30.tgz | Bin 0 -> 31089 bytes 4 files changed, 44 insertions(+), 28 deletions(-) create mode 100644 helm/packages/vector-operator-0.0.30.tgz diff --git a/CHANGELOG.md b/CHANGELOG.md index cfffdcbd..8a18f124 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## v0.0.30 +- [[124]](https://github.com/kaasops/vector-operator/pull/124) **Fix** fix dockerfile to build arm64 + ## v0.0.29 - [[121]](https://github.com/kaasops/vector-operator/pull/121) **Feature** Add nodeSelector for operator deployment to helm chart diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index 7cb67f63..e8e832fb 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.29 +version: 0.0.30 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.0.29" +appVersion: "v0.0.30" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/index.yaml b/helm/index.yaml index 3a1365e1..eb084f14 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.0.30 + created: "2023-09-05T14:25:07.995053293+03:00" + description: A Helm chart to install Vector Operator + digest: 03beda549d15f50325028ea29af5f2065ac0b8adf3078bf7dc1312981aa5e7db + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.30.tgz + version: 0.0.30 - apiVersion: v2 appVersion: v0.0.29 - created: "2023-08-30T22:03:26.195874+03:00" + created: "2023-09-05T14:25:07.994008321+03:00" description: A Helm chart to install Vector Operator digest: 0f025fc3a924b37b8c4131c4d8cfa437d2d4e557ab9476ed3e69a00232c7dca6 home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.0.29 - apiVersion: v2 appVersion: v0.0.28 - created: "2023-08-30T22:03:26.194131+03:00" + created: "2023-09-05T14:25:07.993070201+03:00" description: A Helm chart to install Vector Operator digest: af856d41314313e04f15e7143409a9c564c6ca610b0d2eaec3112add8573e668 home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.0.28 - apiVersion: v2 appVersion: v0.0.27 - created: "2023-08-30T22:03:26.190078+03:00" + created: "2023-09-05T14:25:07.991990653+03:00" description: A Helm chart to install Vector Operator digest: 631e2ff02bbd7f247cb486494fd2af60c57cc551066a6a3858226551bc1745a4 home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.0.27 - apiVersion: v2 appVersion: v0.0.26 - created: "2023-08-30T22:03:26.186741+03:00" + created: "2023-09-05T14:25:07.990594471+03:00" description: A Helm chart to install Vector Operator digest: 760a2833f4c1a33466982419b079ff18d996331ebacc40cf93b0f55229cdb7db home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.0.26 - apiVersion: v2 appVersion: v0.0.25 - created: "2023-08-30T22:03:26.183413+03:00" + created: "2023-09-05T14:25:07.989598019+03:00" description: A Helm chart to install Vector Operator digest: fd22b996b071b6d85740ccf76e85cb640fa717c2620748d206d3f4fdd44cbcc2 home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.0.25 - apiVersion: v2 appVersion: v0.0.24 - created: "2023-08-30T22:03:26.178805+03:00" + created: "2023-09-05T14:25:07.988542121+03:00" description: A Helm chart to install Vector Operator digest: ea257e60ecde063a0d1ed52ce5e3283245b8f0e2daba58ea3a5adb0ba82d7799 home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.0.24 - apiVersion: v2 appVersion: v0.0.23 - created: "2023-08-30T22:03:26.174937+03:00" + created: "2023-09-05T14:25:07.987090114+03:00" description: A Helm chart to install Vector Operator digest: 546d202b3b9263f789b88335263191098dfcabd5d8698105f37cad24d56a8ed0 home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.0.23 - apiVersion: v2 appVersion: v0.0.22 - created: "2023-08-30T22:03:26.172384+03:00" + created: "2023-09-05T14:25:07.986058735+03:00" description: A Helm chart to install Vector Operator digest: bf96ddc8ac61e9d6beb8bc763fbf3fa6025d950b29d70d80de6e8a0ea45e0411 home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.0.22 - apiVersion: v2 appVersion: v0.0.21 - created: "2023-08-30T22:03:26.167631+03:00" + created: "2023-09-05T14:25:07.985126452+03:00" description: A Helm chart to install Vector Operator digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2023-08-30T22:03:26.163838+03:00" + created: "2023-09-05T14:25:07.984167518+03:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2023-08-30T22:03:26.162047+03:00" + created: "2023-09-05T14:25:07.983288209+03:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -146,7 +159,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2023-08-30T22:03:26.1598+03:00" + created: "2023-09-05T14:25:07.982687635+03:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -159,7 +172,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2023-08-30T22:03:26.157854+03:00" + created: "2023-09-05T14:25:07.982097621+03:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -172,7 +185,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2023-08-30T22:03:26.156056+03:00" + created: "2023-09-05T14:25:07.981480072+03:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -185,7 +198,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2023-08-30T22:03:26.148554+03:00" + created: "2023-09-05T14:25:07.980215795+03:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -198,7 +211,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2023-08-30T22:03:26.145246+03:00" + created: "2023-09-05T14:25:07.979533374+03:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -211,7 +224,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2023-08-30T22:03:26.142239+03:00" + created: "2023-09-05T14:25:07.978790866+03:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -224,7 +237,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2023-08-30T22:03:26.139731+03:00" + created: "2023-09-05T14:25:07.978060285+03:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -237,7 +250,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2023-08-30T22:03:26.137808+03:00" + created: "2023-09-05T14:25:07.977390597+03:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -250,7 +263,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2023-08-30T22:03:26.135614+03:00" + created: "2023-09-05T14:25:07.973655945+03:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -263,7 +276,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2023-08-30T22:03:26.20103+03:00" + created: "2023-09-05T14:25:07.998167755+03:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -276,7 +289,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2023-08-30T22:03:26.199897+03:00" + created: "2023-09-05T14:25:07.997620247+03:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -289,7 +302,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2023-08-30T22:03:26.198726+03:00" + created: "2023-09-05T14:25:07.996284716+03:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -302,7 +315,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2023-08-30T22:03:26.197256+03:00" + created: "2023-09-05T14:25:07.995669908+03:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -315,7 +328,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2023-08-30T22:03:26.131117+03:00" + created: "2023-09-05T14:25:07.97148185+03:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -326,4 +339,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2023-08-30T22:03:26.126164+03:00" +generated: "2023-09-05T14:25:07.964022056+03:00" diff --git a/helm/packages/vector-operator-0.0.30.tgz b/helm/packages/vector-operator-0.0.30.tgz new file mode 100644 index 0000000000000000000000000000000000000000..3cbc5da353e959309268935c137218100e2affae GIT binary patch literal 31089 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 literal 0 HcmV?d00001 From a51304224d060a0f24e6205b79c8b741dd6a610a Mon Sep 17 00:00:00 2001 From: zvlb Date: Tue, 3 Oct 2023 11:31:25 +0300 Subject: [PATCH 237/316] Add script for simple test Signed-off-by: zvlb --- hack/simple-test.sh | 161 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100755 hack/simple-test.sh diff --git a/hack/simple-test.sh b/hack/simple-test.sh new file mode 100755 index 00000000..bdfacf94 --- /dev/null +++ b/hack/simple-test.sh @@ -0,0 +1,161 @@ +#!/usr/bin/env bash + +# This script test Vector Operator with simple settings + +NAMESPACE=test-vector-operator +TESTAPP=log-spamer + +function error_action() { + cleanup_action + exit 1 +} + +function cleanup_action() { + kubectl delete ns ${NAMESPACE} +} + +function check_command() { + local command=$1 + + if ! command -v $command &> /dev/null; then + echo "Error: ${command} not found" + exit 1 + fi +} + +check_command kubectl +check_command helm + +# Install Vector Operator +echo `date`": INFO: Add vector-operator helm repository" +error_add_helm_repo=$(helm repo add vector-operator https://kaasops.github.io/vector-operator/helm 2>&1) +if [ $? -ne 0 ]; then + echo `date`": $error_add_helm_repo" + exit 1 +fi + +echo `date`": INFO: Update helm repositories" +error_update_helm_repo=$(helm repo update 2>&1) +if [ $? -ne 0 ]; then + echo `date`": $error_update_helm_repo" + exit 1 +fi + +echo `date`": INFO: Create Namespace ${NAMESPACE} for Vector Operator" +error_create_ns=$(kubectl create ns ${NAMESPACE} 2>&1) +if [ $? -ne 0 ]; then + echo `date`": $error_create_ns" + exit 1 +fi + +echo `date`": INFO: Install Vector Operator" +error_install_vector_operator=$(helm install vector-operator vector-operator/vector-operator -n ${NAMESPACE} 2>&1) +if [ $? -ne 0 ]; then + echo `date`": $error_install_vector_operator" + error_action +fi + +echo `date`": INFO: Deploy test application with logs" +cat < Date: Thu, 2 Nov 2023 13:44:51 +0200 Subject: [PATCH 238/316] configurable configcheck timeout --- .../factory/config/configcheck/configcheck.go | 7 ++++--- controllers/pipeline_controller.go | 2 ++ controllers/vector_controller.go | 12 +++++++----- main.go | 4 ++++ 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/controllers/factory/config/configcheck/configcheck.go b/controllers/factory/config/configcheck/configcheck.go index c94d2440..ee6d4f4d 100644 --- a/controllers/factory/config/configcheck/configcheck.go +++ b/controllers/factory/config/configcheck/configcheck.go @@ -37,8 +37,6 @@ import ( vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" ) -const waitConfigcheckResultTimeout = 300 * time.Second - type ConfigCheck struct { Config []byte @@ -59,6 +57,7 @@ type ConfigCheck struct { CompressedConfig bool ConfigReloaderImage string ConfigReloaderResources corev1.ResourceRequirements + ConfigCheckTimeout time.Duration } func New( @@ -66,6 +65,7 @@ func New( c client.Client, cs *kubernetes.Clientset, va *vectorv1alpha1.Vector, + timeout time.Duration, ) *ConfigCheck { image := va.Spec.Agent.Image if va.Spec.Agent.ConfigCheck.Image != nil { @@ -100,6 +100,7 @@ func New( CompressedConfig: va.Spec.Agent.CompressConfigFile, ConfigReloaderImage: va.Spec.Agent.ConfigReloaderImage, ConfigReloaderResources: va.Spec.Agent.ConfigReloaderResources, + ConfigCheckTimeout: timeout, } } @@ -230,7 +231,7 @@ func (cc *ConfigCheck) getCheckResult(ctx context.Context, pod *corev1.Pod) (rea case <-ctx.Done(): watcher.Stop() return "", nil - case <-time.After(waitConfigcheckResultTimeout): + case <-time.After(cc.ConfigCheckTimeout): watcher.Stop() return "", ConfigcheckTimeoutError } diff --git a/controllers/pipeline_controller.go b/controllers/pipeline_controller.go index 5c020218..10d80ca2 100644 --- a/controllers/pipeline_controller.go +++ b/controllers/pipeline_controller.go @@ -47,6 +47,7 @@ type PipelineReconciler struct { Clientset *kubernetes.Clientset PipelineCheckWG *sync.WaitGroup PipelineDeleteEventTimeout time.Duration + ConfigCheckTimeout time.Duration } //+kubebuilder:rbac:groups=observability.kaasops.io,resources=vectorpipelines;clustervectorpipelines,verbs=get;list;watch;create;update;patch;delete @@ -175,6 +176,7 @@ func (r *PipelineReconciler) runPipelineCheck(ctx context.Context, p pipeline.Pi vaCtrl.Client, vaCtrl.ClientSet, vaCtrl.Vector, + r.ConfigCheckTimeout, ) configCheck.Initiator = configcheck.ConfigCheckInitiatorPipieline defer r.PipelineCheckWG.Done() diff --git a/controllers/vector_controller.go b/controllers/vector_controller.go index 455bc73b..459c1e49 100644 --- a/controllers/vector_controller.go +++ b/controllers/vector_controller.go @@ -59,6 +59,7 @@ type VectorReconciler struct { Clientset *kubernetes.Clientset PipelineCheckWG *sync.WaitGroup PipelineCheckTimeout time.Duration + ConfigCheckTimeout time.Duration DiscoveryClient *discovery.DiscoveryClient } @@ -87,7 +88,7 @@ func (r *VectorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr log.Error(err, "Failed to list vector instances") return ctrl.Result{}, err } - return reconcileVectors(ctx, r.Client, r.Clientset, false, vectors...) + return r.reconcileVectors(ctx, r.Client, r.Clientset, false, vectors...) } vectorCR, err := r.findVectorCustomResourceInstance(ctx, req) @@ -100,7 +101,7 @@ func (r *VectorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr return ctrl.Result{}, nil } - return createOrUpdateVector(ctx, r.Client, r.Clientset, vectorCR, false) + return r.createOrUpdateVector(ctx, r.Client, r.Clientset, vectorCR, false) } // SetupWithManager sets up the controller with the Manager. @@ -154,7 +155,7 @@ func (r *VectorReconciler) findVectorCustomResourceInstance(ctx context.Context, return vectorCR, nil } -func reconcileVectors(ctx context.Context, client client.Client, clientset *kubernetes.Clientset, configOnly bool, vectors ...*vectorv1alpha1.Vector) (ctrl.Result, error) { +func (r *VectorReconciler) reconcileVectors(ctx context.Context, client client.Client, clientset *kubernetes.Clientset, configOnly bool, vectors ...*vectorv1alpha1.Vector) (ctrl.Result, error) { if len(vectors) == 0 { return ctrl.Result{}, nil } @@ -163,14 +164,14 @@ func reconcileVectors(ctx context.Context, client client.Client, clientset *kube if vector.DeletionTimestamp != nil { continue } - if _, err := createOrUpdateVector(ctx, client, clientset, vector, configOnly); err != nil { + if _, err := r.createOrUpdateVector(ctx, client, clientset, vector, configOnly); err != nil { return ctrl.Result{}, err } } return ctrl.Result{}, nil } -func createOrUpdateVector(ctx context.Context, client client.Client, clientset *kubernetes.Clientset, v *vectorv1alpha1.Vector, configOnly bool) (ctrl.Result, error) { +func (r *VectorReconciler) createOrUpdateVector(ctx context.Context, client client.Client, clientset *kubernetes.Clientset, v *vectorv1alpha1.Vector, configOnly bool) (ctrl.Result, error) { _ = log.FromContext(ctx) log := log.FromContext(ctx).WithValues("Vector", v.Name) // Init Controller for Vector Agent @@ -198,6 +199,7 @@ func createOrUpdateVector(ctx context.Context, client client.Client, clientset * vaCtrl.Client, vaCtrl.ClientSet, vaCtrl.Vector, + r.ConfigCheckTimeout, ) configCheck.Initiator = configcheck.ConfigCheckInitiatorVector reason, err := configCheck.Run(ctx) diff --git a/main.go b/main.go index cd3d4301..e2ca0ae1 100644 --- a/main.go +++ b/main.go @@ -70,6 +70,7 @@ func main() { var pipelineCheckWG sync.WaitGroup var PipelineCheckTimeout time.Duration var PipelineDeleteEventTimeout time.Duration + var ConfigCheckTimeout time.Duration flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.") flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") @@ -80,6 +81,7 @@ func main() { flag.StringVar(&watchLabel, "watch-name", "", "Filter the list of watched objects by checking the app.kubernetes.io/managed-by label") flag.DurationVar(&PipelineCheckTimeout, "pipeline-check-timeout", 15*time.Second, "wait pipeline checks before force vector reconcile. Default: 15s") flag.DurationVar(&PipelineDeleteEventTimeout, "pipeline-delete-timeout", 5*time.Second, "collect delete events timeout") + flag.DurationVar(&ConfigCheckTimeout, "configcheck-timeout", 300*time.Second, "collect delete events timeout") opts := zap.Options{ Development: true, } @@ -129,6 +131,7 @@ func main() { Clientset: clientset, PipelineCheckWG: &pipelineCheckWG, PipelineCheckTimeout: PipelineCheckTimeout, + ConfigCheckTimeout: ConfigCheckTimeout, DiscoveryClient: dc, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Vector") @@ -140,6 +143,7 @@ func main() { Clientset: clientset, PipelineCheckWG: &pipelineCheckWG, PipelineDeleteEventTimeout: PipelineDeleteEventTimeout, + ConfigCheckTimeout: ConfigCheckTimeout, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "VectorPipeline") os.Exit(1) From 0d772011ead96644e37d1bf609e9eabb3fab1569 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Thu, 2 Nov 2023 14:35:18 +0200 Subject: [PATCH 239/316] update configcheck-timeout flag mesage --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index e2ca0ae1..9fda9f72 100644 --- a/main.go +++ b/main.go @@ -81,7 +81,7 @@ func main() { flag.StringVar(&watchLabel, "watch-name", "", "Filter the list of watched objects by checking the app.kubernetes.io/managed-by label") flag.DurationVar(&PipelineCheckTimeout, "pipeline-check-timeout", 15*time.Second, "wait pipeline checks before force vector reconcile. Default: 15s") flag.DurationVar(&PipelineDeleteEventTimeout, "pipeline-delete-timeout", 5*time.Second, "collect delete events timeout") - flag.DurationVar(&ConfigCheckTimeout, "configcheck-timeout", 300*time.Second, "collect delete events timeout") + flag.DurationVar(&ConfigCheckTimeout, "configcheck-timeout", 300*time.Second, "configcheck timeout") opts := zap.Options{ Development: true, } From b85eae5f59850903209c3edd6cc47cba388c6812 Mon Sep 17 00:00:00 2001 From: zvlb Date: Thu, 9 Nov 2023 16:54:13 +0200 Subject: [PATCH 240/316] Add imagePullSecrets from Vector Agent to ConfigCheck Signed-off-by: zvlb --- controllers/factory/config/configcheck/configcheck.go | 2 ++ controllers/factory/config/configcheck/configcheck_pod.go | 1 + 2 files changed, 3 insertions(+) diff --git a/controllers/factory/config/configcheck/configcheck.go b/controllers/factory/config/configcheck/configcheck.go index ee6d4f4d..7c9b2325 100644 --- a/controllers/factory/config/configcheck/configcheck.go +++ b/controllers/factory/config/configcheck/configcheck.go @@ -48,6 +48,7 @@ type ConfigCheck struct { Initiator string Image string ImagePullPolicy corev1.PullPolicy + ImagePullSecrets []corev1.LocalObjectReference Envs []corev1.EnvVar Hash string Tolerations []corev1.Toleration @@ -92,6 +93,7 @@ func New( Namespace: va.Namespace, Image: image, ImagePullPolicy: va.Spec.Agent.ImagePullPolicy, + ImagePullSecrets: va.Spec.Agent.ImagePullSecrets, Envs: env, Tolerations: tolerations, Resources: resources, diff --git a/controllers/factory/config/configcheck/configcheck_pod.go b/controllers/factory/config/configcheck/configcheck_pod.go index b892953c..51f8ff55 100644 --- a/controllers/factory/config/configcheck/configcheck_pod.go +++ b/controllers/factory/config/configcheck/configcheck_pod.go @@ -39,6 +39,7 @@ func (cc *ConfigCheck) createVectorConfigCheckPod() *corev1.Pod { ServiceAccountName: "vector-configcheck", Volumes: cc.generateVectorConfigCheckVolume(), SecurityContext: cc.SecurityContext, + ImagePullSecrets: cc.ImagePullSecrets, Tolerations: cc.Tolerations, InitContainers: initContainers, Containers: []corev1.Container{ From a72dec6373e7f8d1cbc17c16c425a2924d6d0b11 Mon Sep 17 00:00:00 2001 From: zvlb Date: Thu, 9 Nov 2023 16:56:42 +0200 Subject: [PATCH 241/316] Update ChangeLog for v0.0.31 release Signed-off-by: zvlb --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a18f124..7cd15bd4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## v0.0.31 +- [[127]](https://github.com/kaasops/vector-operator/pull/127) **Fix** Add imagePullSecrets from Vector Agent to ConfigCheck + ## v0.0.30 - [[124]](https://github.com/kaasops/vector-operator/pull/124) **Fix** fix dockerfile to build arm64 From d1f83036ec2564c834fdc33c83991c18fd505921 Mon Sep 17 00:00:00 2001 From: zvlb Date: Thu, 9 Nov 2023 16:57:40 +0200 Subject: [PATCH 242/316] Prepare helm for v0.0.31 release Signed-off-by: zvlb --- helm/charts/vector-operator/Chart.yaml | 4 +- helm/index.yaml | 67 ++++++++++++++--------- helm/packages/vector-operator-0.0.31.tgz | Bin 0 -> 31089 bytes 3 files changed, 42 insertions(+), 29 deletions(-) create mode 100644 helm/packages/vector-operator-0.0.31.tgz diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index e8e832fb..c29e30df 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.30 +version: 0.0.31 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.0.30" +appVersion: "v0.0.31" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/index.yaml b/helm/index.yaml index eb084f14..9396094e 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.0.31 + created: "2023-11-09T16:57:22.989212+02:00" + description: A Helm chart to install Vector Operator + digest: 45b924c07a825e0f7cd3fb534a6ffd16604790d13be1aff59150c045474754e3 + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.31.tgz + version: 0.0.31 - apiVersion: v2 appVersion: v0.0.30 - created: "2023-09-05T14:25:07.995053293+03:00" + created: "2023-11-09T16:57:22.98808+02:00" description: A Helm chart to install Vector Operator digest: 03beda549d15f50325028ea29af5f2065ac0b8adf3078bf7dc1312981aa5e7db home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.0.30 - apiVersion: v2 appVersion: v0.0.29 - created: "2023-09-05T14:25:07.994008321+03:00" + created: "2023-11-09T16:57:22.987318+02:00" description: A Helm chart to install Vector Operator digest: 0f025fc3a924b37b8c4131c4d8cfa437d2d4e557ab9476ed3e69a00232c7dca6 home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.0.29 - apiVersion: v2 appVersion: v0.0.28 - created: "2023-09-05T14:25:07.993070201+03:00" + created: "2023-11-09T16:57:22.986561+02:00" description: A Helm chart to install Vector Operator digest: af856d41314313e04f15e7143409a9c564c6ca610b0d2eaec3112add8573e668 home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.0.28 - apiVersion: v2 appVersion: v0.0.27 - created: "2023-09-05T14:25:07.991990653+03:00" + created: "2023-11-09T16:57:22.985798+02:00" description: A Helm chart to install Vector Operator digest: 631e2ff02bbd7f247cb486494fd2af60c57cc551066a6a3858226551bc1745a4 home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.0.27 - apiVersion: v2 appVersion: v0.0.26 - created: "2023-09-05T14:25:07.990594471+03:00" + created: "2023-11-09T16:57:22.984377+02:00" description: A Helm chart to install Vector Operator digest: 760a2833f4c1a33466982419b079ff18d996331ebacc40cf93b0f55229cdb7db home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.0.26 - apiVersion: v2 appVersion: v0.0.25 - created: "2023-09-05T14:25:07.989598019+03:00" + created: "2023-11-09T16:57:22.983593+02:00" description: A Helm chart to install Vector Operator digest: fd22b996b071b6d85740ccf76e85cb640fa717c2620748d206d3f4fdd44cbcc2 home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.0.25 - apiVersion: v2 appVersion: v0.0.24 - created: "2023-09-05T14:25:07.988542121+03:00" + created: "2023-11-09T16:57:22.982842+02:00" description: A Helm chart to install Vector Operator digest: ea257e60ecde063a0d1ed52ce5e3283245b8f0e2daba58ea3a5adb0ba82d7799 home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.0.24 - apiVersion: v2 appVersion: v0.0.23 - created: "2023-09-05T14:25:07.987090114+03:00" + created: "2023-11-09T16:57:22.982081+02:00" description: A Helm chart to install Vector Operator digest: 546d202b3b9263f789b88335263191098dfcabd5d8698105f37cad24d56a8ed0 home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.0.23 - apiVersion: v2 appVersion: v0.0.22 - created: "2023-09-05T14:25:07.986058735+03:00" + created: "2023-11-09T16:57:22.980961+02:00" description: A Helm chart to install Vector Operator digest: bf96ddc8ac61e9d6beb8bc763fbf3fa6025d950b29d70d80de6e8a0ea45e0411 home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.0.22 - apiVersion: v2 appVersion: v0.0.21 - created: "2023-09-05T14:25:07.985126452+03:00" + created: "2023-11-09T16:57:22.980161+02:00" description: A Helm chart to install Vector Operator digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2023-09-05T14:25:07.984167518+03:00" + created: "2023-11-09T16:57:22.97932+02:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -146,7 +159,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2023-09-05T14:25:07.983288209+03:00" + created: "2023-11-09T16:57:22.978658+02:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -159,7 +172,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2023-09-05T14:25:07.982687635+03:00" + created: "2023-11-09T16:57:22.977939+02:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -172,7 +185,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2023-09-05T14:25:07.982097621+03:00" + created: "2023-11-09T16:57:22.977413+02:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -185,7 +198,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2023-09-05T14:25:07.981480072+03:00" + created: "2023-11-09T16:57:22.976875+02:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -198,7 +211,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2023-09-05T14:25:07.980215795+03:00" + created: "2023-11-09T16:57:22.976334+02:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -211,7 +224,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2023-09-05T14:25:07.979533374+03:00" + created: "2023-11-09T16:57:22.975771+02:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -224,7 +237,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2023-09-05T14:25:07.978790866+03:00" + created: "2023-11-09T16:57:22.975153+02:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -237,7 +250,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2023-09-05T14:25:07.978060285+03:00" + created: "2023-11-09T16:57:22.973678+02:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -250,7 +263,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2023-09-05T14:25:07.977390597+03:00" + created: "2023-11-09T16:57:22.973116+02:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -263,7 +276,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2023-09-05T14:25:07.973655945+03:00" + created: "2023-11-09T16:57:22.972628+02:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -276,7 +289,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2023-09-05T14:25:07.998167755+03:00" + created: "2023-11-09T16:57:22.9908+02:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -289,7 +302,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2023-09-05T14:25:07.997620247+03:00" + created: "2023-11-09T16:57:22.990402+02:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -302,7 +315,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2023-09-05T14:25:07.996284716+03:00" + created: "2023-11-09T16:57:22.989998+02:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -315,7 +328,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2023-09-05T14:25:07.995669908+03:00" + created: "2023-11-09T16:57:22.989603+02:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -328,7 +341,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2023-09-05T14:25:07.97148185+03:00" + created: "2023-11-09T16:57:22.97211+02:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -339,4 +352,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2023-09-05T14:25:07.964022056+03:00" +generated: "2023-11-09T16:57:22.971209+02:00" diff --git a/helm/packages/vector-operator-0.0.31.tgz b/helm/packages/vector-operator-0.0.31.tgz new file mode 100644 index 0000000000000000000000000000000000000000..e962e1ae7d15b458bdea87b432d2efa03a8e0538 GIT binary patch literal 31089 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 literal 0 HcmV?d00001 From d7159fb802739e9668aaf997f47e4a6e79df26b3 Mon Sep 17 00:00:00 2001 From: "Dmitry K. Anisimov" Date: Tue, 14 Nov 2023 18:18:18 +0200 Subject: [PATCH 243/316] add probes attribute to vector-agent --- Makefile | 4 +- api/v1alpha1/vector_types.go | 12 + api/v1alpha1/zz_generated.deepcopy.go | 10 + .../observability.kaasops.io_vectors.yaml | 303 ++++++++++++++++++ controllers/factory/utils/k8s/k8s.go | 9 + .../vectoragent/vectoragent_daemonset.go | 2 + .../vector/vectoragent/vectoragent_default.go | 39 +++ docs/specification.md | 14 +- 8 files changed, 390 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 92b3a5b7..e1eba2b9 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,7 @@ # - use the VERSION as arg of the bundle target (e.g make bundle VERSION=0.0.2) # - use environment variables to overwrite this value (e.g export VERSION=0.0.2) VERSION ?= 0.0.1 +DOCKER_PLATFORM ?= linux/amd64 # CHANNELS define the bundle channels used in the bundle. # Add a new line here if you would like to change its default config. (E.g CHANNELS = "candidate,fast,stable") @@ -117,7 +118,7 @@ run: manifests generate fmt vet ## Run a controller from your host. .PHONY: docker-build docker-build: test ## Build docker image with the manager. - docker build -t ${IMG} . + docker build --platform ${DOCKER_PLATFORM} -t ${IMG} . .PHONY: docker-push docker-push: ## Push docker image with the manager. @@ -233,4 +234,3 @@ catalog-build: opm ## Build a catalog image. .PHONY: catalog-push catalog-push: ## Push a catalog image. $(MAKE) docker-push IMG=$(CATALOG_IMG) - \ No newline at end of file diff --git a/api/v1alpha1/vector_types.go b/api/v1alpha1/vector_types.go index dc03e3e5..baec1324 100644 --- a/api/v1alpha1/vector_types.go +++ b/api/v1alpha1/vector_types.go @@ -117,6 +117,14 @@ type VectorAgent struct { // +optional Volumes []v1.Volume `json:"volumes,omitempty"` + // Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. + // +optional + ReadinessProbe *v1.Probe `json:"readinessProbe,omitempty"` + + // Periodic probe of container liveness. Container will be restarted if the probe fails. + // +optional + LivenessProbe *v1.Probe `json:"livenessProbe,omitempty"` + // Pod volumes to mount into the container's filesystem. // +optional VolumeMounts []v1.VolumeMount `json:"volumeMounts,omitempty"` @@ -132,6 +140,10 @@ type VectorAgent struct { type ApiSpec struct { Enabled bool `json:"enabled,omitempty"` Playground bool `json:"playground,omitempty"` + // Enable ReadinessProbe and LivenessProbe via api /health endpoint. + // If probe enabled via VectorAgent, this setting will be ignored for that probe. + // +optional + Healthcheck bool `json:"healthcheck,omitempty"` } // ConfigCheck is the Schema for control params for ConfigCheck pods diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 9567cfcc..a16accea 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -226,6 +226,16 @@ func (in *VectorAgent) DeepCopyInto(out *VectorAgent) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.ReadinessProbe != nil { + in, out := &in.ReadinessProbe, &out.ReadinessProbe + *out = new(v1.Probe) + (*in).DeepCopyInto(*out) + } + if in.LivenessProbe != nil { + in, out := &in.LivenessProbe, &out.LivenessProbe + *out = new(v1.Probe) + (*in).DeepCopyInto(*out) + } if in.VolumeMounts != nil { in, out := &in.VolumeMounts, &out.VolumeMounts *out = make([]v1.VolumeMount, len(*in)) diff --git a/config/crd/bases/observability.kaasops.io_vectors.yaml b/config/crd/bases/observability.kaasops.io_vectors.yaml index a153b615..5d45ae4f 100644 --- a/config/crd/bases/observability.kaasops.io_vectors.yaml +++ b/config/crd/bases/observability.kaasops.io_vectors.yaml @@ -920,6 +920,11 @@ spec: properties: enabled: type: boolean + healthcheck: + description: Enable ReadinessProbe and LivenessProbe via api + /health endpoint. If probe enabled via VectorAgent, this + setting will be ignored for that probe. + type: boolean playground: type: boolean type: object @@ -2270,6 +2275,155 @@ spec: internalMetrics: description: Enable internal metrics exporter type: boolean + livenessProbe: + description: Periodic probe of container liveness. Container will + be restarted if the probe fails. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute inside + the container, the working directory for the command is + root ('/') in the container's filesystem. The command + is simply exec'd, it is not run inside a shell, so traditional + shell instructions ('|', etc) won't work. To use a shell, + you need to explicitly call out to that shell. Exit + status of 0 is treated as live/healthy and non-zero + is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe to + be considered failed after having succeeded. Defaults to + 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + This is a beta field and requires enabling GRPCContainerProbe + feature gate. + properties: + port: + description: Port number of the gRPC service. Number must + be in the range 1 to 65535. + format: int32 + type: integer + service: + description: "Service is the name of the service to place + in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + \n If this is not specified, the default behavior is + defined by gRPC." + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header to + be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on the + container. Number must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has started + before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe to + be considered successful after having failed. Defaults to + 1. Must be 1 for liveness and startup. Minimum value is + 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on the + container. Number must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs to + terminate gracefully upon probe failure. The grace period + is the duration in seconds after the processes running in + the pod are sent a termination signal and the time when + the processes are forcibly halted with a kill signal. Set + this value longer than the expected cleanup time for your + process. If this value is nil, the pod's terminationGracePeriodSeconds + will be used. Otherwise, this value overrides the value + provided by the pod spec. Value must be non-negative integer. + The value zero indicates stop immediately via the kill signal + (no opportunity to shut down). This is a beta field and + requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is + used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object podSecurityContext: description: SecurityContext holds pod-level security attributes and common container settings. This defaults to the default @@ -2449,6 +2603,155 @@ spec: priorityClassName: description: PriorityClassName assigned to the Pods type: string + readinessProbe: + description: Periodic probe of container service readiness. Container + will be removed from service endpoints if the probe fails. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute inside + the container, the working directory for the command is + root ('/') in the container's filesystem. The command + is simply exec'd, it is not run inside a shell, so traditional + shell instructions ('|', etc) won't work. To use a shell, + you need to explicitly call out to that shell. Exit + status of 0 is treated as live/healthy and non-zero + is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe to + be considered failed after having succeeded. Defaults to + 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + This is a beta field and requires enabling GRPCContainerProbe + feature gate. + properties: + port: + description: Port number of the gRPC service. Number must + be in the range 1 to 65535. + format: int32 + type: integer + service: + description: "Service is the name of the service to place + in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + \n If this is not specified, the default behavior is + defined by gRPC." + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header to + be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on the + container. Number must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has started + before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe to + be considered successful after having failed. Defaults to + 1. Must be 1 for liveness and startup. Minimum value is + 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on the + container. Number must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs to + terminate gracefully upon probe failure. The grace period + is the duration in seconds after the processes running in + the pod are sent a termination signal and the time when + the processes are forcibly halted with a kill signal. Set + this value longer than the expected cleanup time for your + process. If this value is nil, the pod's terminationGracePeriodSeconds + will be used. Otherwise, this value overrides the value + provided by the pod spec. Value must be non-negative integer. + The value zero indicates stop immediately via the kill signal + (no opportunity to shut down). This is a beta field and + requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is + used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object resources: description: Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ diff --git a/controllers/factory/utils/k8s/k8s.go b/controllers/factory/utils/k8s/k8s.go index ab31d66b..81a27097 100644 --- a/controllers/factory/utils/k8s/k8s.go +++ b/controllers/factory/utils/k8s/k8s.go @@ -95,6 +95,7 @@ func createOrUpdateDeployment(ctx context.Context, obj runtime.Object, c client. } // Compare + // FIXME: DeepDerivative does not compare fields, if they omitted in desiredFields if !equality.Semantic.DeepDerivative(desiredFields, existingFields) { // Update if not equal existing.Labels = desired.Labels @@ -133,6 +134,7 @@ func createOrUpdateStatefulSet(ctx context.Context, obj runtime.Object, c client } // Compare + // FIXME: DeepDerivative does not compare fields, if they omitted in desiredFields if !equality.Semantic.DeepDerivative(desiredFields, existingFields) { // Update if not equal existing.Labels = desired.Labels @@ -171,6 +173,7 @@ func createOrUpdateDaemonSet(ctx context.Context, obj runtime.Object, c client.C } // Compare + // FIXME: DeepDerivative does not compare fields, if they omitted in desiredFields if !equality.Semantic.DeepDerivative(desiredFields, existingFields) { // Update if not equal existing.Labels = desired.Labels @@ -209,6 +212,7 @@ func createOrUpdateSecret(ctx context.Context, obj runtime.Object, c client.Clie } // Compare + // FIXME: DeepDerivative does not compare fields, if they omitted in desiredFields if !equality.Semantic.DeepDerivative(desiredFields, existingFields) { // Update if not equal existing.Labels = desired.Labels @@ -247,6 +251,7 @@ func createOrUpdateService(ctx context.Context, obj runtime.Object, c client.Cli } // Compare + // FIXME: DeepDerivative does not compare fields, if they omitted in desiredFields if !equality.Semantic.DeepDerivative(desiredFields, existingFields) { // Update if not equal existing.Labels = desired.Labels @@ -283,6 +288,7 @@ func createOrUpdateServiceAccount(ctx context.Context, obj runtime.Object, c cli } // Compare + // FIXME: DeepDerivative does not compare fields, if they omitted in desiredFields if !equality.Semantic.DeepDerivative(desiredFields, existingFields) { // Update if not equal existing.Labels = desired.Labels @@ -320,6 +326,7 @@ func createOrUpdateClusterRole(ctx context.Context, obj runtime.Object, c client } // Compare + // FIXME: DeepDerivative does not compare fields, if they omitted in desiredFields if !equality.Semantic.DeepDerivative(desiredFields, existingFields) { // Update if not equal existing.Labels = desired.Labels @@ -360,6 +367,7 @@ func createOrUpdateClusterRoleBinding(ctx context.Context, obj runtime.Object, c } // Compare + // FIXME: DeepDerivative does not compare fields, if they omitted in desiredFields if !equality.Semantic.DeepDerivative(desiredFields, existingFields) { // Update if not equal existing.Labels = desired.Labels @@ -401,6 +409,7 @@ func createOrUpdatePodMonitor(ctx context.Context, obj runtime.Object, c client. } // Compare + // FIXME: DeepDerivative does not compare fields, if they omitted in desiredFields if !equality.Semantic.DeepDerivative(desiredFields, existingFields) { // Update if not equal existing.Labels = desired.Labels diff --git a/controllers/factory/vector/vectoragent/vectoragent_daemonset.go b/controllers/factory/vector/vectoragent/vectoragent_daemonset.go index 93dcde22..1df0ad9d 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_daemonset.go +++ b/controllers/factory/vector/vectoragent/vectoragent_daemonset.go @@ -215,6 +215,8 @@ func (ctrl *Controller) VectorAgentContainer() *corev1.Container { }, }, VolumeMounts: ctrl.generateVectorAgentVolumeMounts(), + ReadinessProbe: ctrl.Vector.Spec.Agent.ReadinessProbe, + LivenessProbe: ctrl.Vector.Spec.Agent.LivenessProbe, Resources: ctrl.Vector.Spec.Agent.Resources, SecurityContext: ctrl.Vector.Spec.Agent.ContainerSecurityContext, ImagePullPolicy: ctrl.Vector.Spec.Agent.ImagePullPolicy, diff --git a/controllers/factory/vector/vectoragent/vectoragent_default.go b/controllers/factory/vector/vectoragent/vectoragent_default.go index e63e2d7b..896311e9 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_default.go +++ b/controllers/factory/vector/vectoragent/vectoragent_default.go @@ -20,6 +20,7 @@ import ( "github.com/kaasops/vector-operator/api/v1alpha1" corev1 "k8s.io/api/core/v1" resourcev1 "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/util/intstr" ) func (ctrl *Controller) SetDefault() { @@ -76,6 +77,43 @@ func (ctrl *Controller) SetDefault() { } } + if ctrl.Vector.Spec.Agent.ReadinessProbe == nil && ctrl.Vector.Spec.Agent.Api.Enabled && ctrl.Vector.Spec.Agent.Api.Healthcheck { + ctrl.Vector.Spec.Agent.ReadinessProbe = &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/health", + Port: intstr.IntOrString{ + Type: intstr.Type(0), + IntVal: 8686, + }, + }, + }, + PeriodSeconds: 20, + InitialDelaySeconds: 15, + TimeoutSeconds: 3, + SuccessThreshold: 0, + FailureThreshold: 0, + } + } + if ctrl.Vector.Spec.Agent.LivenessProbe == nil && ctrl.Vector.Spec.Agent.Api.Enabled && ctrl.Vector.Spec.Agent.Api.Healthcheck { + ctrl.Vector.Spec.Agent.LivenessProbe = &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/health", + Port: intstr.IntOrString{ + Type: intstr.Type(0), + IntVal: 8686, + }, + }, + }, + PeriodSeconds: 20, + InitialDelaySeconds: 15, + TimeoutSeconds: 3, + SuccessThreshold: 0, + FailureThreshold: 0, + } + } + if ctrl.Vector.Spec.Agent.VolumeMounts == nil { ctrl.Vector.Spec.Agent.VolumeMounts = []corev1.VolumeMount{ { @@ -107,4 +145,5 @@ func (ctrl *Controller) SetDefault() { corev1.ResourceCPU: resourcev1.MustParse("1000m"), } } + } diff --git a/docs/specification.md b/docs/specification.md index 826e65d3..b83da489 100644 --- a/docs/specification.md +++ b/docs/specification.md @@ -66,6 +66,14 @@ podSecurityPolicyName PodSecurityPolicyName - defines name for podSecurityPolicy in case of empty value, prefixedName will be used. + + readinessProbe + Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. By default - not set + + + livenessProbe + Periodic probe of container liveness. Container will be restarted if the probe fails. By default - not set + volumes List of volumes that can be mounted by containers belonging to the pod. @@ -91,7 +99,7 @@ ## Api Spec - + @@ -105,6 +113,10 @@ + + + +
apiapi
addressplayground Whether the GraphQL Playground is enabled for the API. The Playground is accessible via the /playground endpoint of the address set using the bind parameter. By default - false
healthcheckEnable ReadinessProbe and LivenessProbe via API /health endpoint. If probes enabled via VectorAgent, this setting will be ignored for that probe. By default - false
From bc538d161aed02248c5b651033c1aa1669e9e7dd Mon Sep 17 00:00:00 2001 From: Vladimir <31961982+zvlb@users.noreply.github.com> Date: Mon, 20 Nov 2023 11:25:28 +0200 Subject: [PATCH 244/316] Revert "Added the ability to split the configuration among different vector agent installations" From 3ba9e8e1fbcfbd86627477d810a943b5bccd7595 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Mon, 20 Nov 2023 11:28:16 +0200 Subject: [PATCH 245/316] release v0.0.32 --- CHANGELOG.md | 3 + helm/charts/vector-operator/Chart.yaml | 4 +- .../observability.kaasops.io_vectors.yaml | 303 ++++++++++++++++++ helm/index.yaml | 69 ++-- helm/packages/vector-operator-0.0.32.tgz | Bin 0 -> 32988 bytes 5 files changed, 349 insertions(+), 30 deletions(-) create mode 100644 helm/packages/vector-operator-0.0.32.tgz diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cd15bd4..164c568a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## v0.0.32 +- [[128]](https://github.com/kaasops/vector-operator/pull/128) **Feature** Add probes attribute to vector-agent + ## v0.0.31 - [[127]](https://github.com/kaasops/vector-operator/pull/127) **Fix** Add imagePullSecrets from Vector Agent to ConfigCheck diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index c29e30df..b46cea4a 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.31 +version: 0.0.32 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.0.31" +appVersion: "v0.0.32" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml index a153b615..5d45ae4f 100644 --- a/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml +++ b/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml @@ -920,6 +920,11 @@ spec: properties: enabled: type: boolean + healthcheck: + description: Enable ReadinessProbe and LivenessProbe via api + /health endpoint. If probe enabled via VectorAgent, this + setting will be ignored for that probe. + type: boolean playground: type: boolean type: object @@ -2270,6 +2275,155 @@ spec: internalMetrics: description: Enable internal metrics exporter type: boolean + livenessProbe: + description: Periodic probe of container liveness. Container will + be restarted if the probe fails. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute inside + the container, the working directory for the command is + root ('/') in the container's filesystem. The command + is simply exec'd, it is not run inside a shell, so traditional + shell instructions ('|', etc) won't work. To use a shell, + you need to explicitly call out to that shell. Exit + status of 0 is treated as live/healthy and non-zero + is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe to + be considered failed after having succeeded. Defaults to + 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + This is a beta field and requires enabling GRPCContainerProbe + feature gate. + properties: + port: + description: Port number of the gRPC service. Number must + be in the range 1 to 65535. + format: int32 + type: integer + service: + description: "Service is the name of the service to place + in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + \n If this is not specified, the default behavior is + defined by gRPC." + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header to + be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on the + container. Number must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has started + before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe to + be considered successful after having failed. Defaults to + 1. Must be 1 for liveness and startup. Minimum value is + 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on the + container. Number must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs to + terminate gracefully upon probe failure. The grace period + is the duration in seconds after the processes running in + the pod are sent a termination signal and the time when + the processes are forcibly halted with a kill signal. Set + this value longer than the expected cleanup time for your + process. If this value is nil, the pod's terminationGracePeriodSeconds + will be used. Otherwise, this value overrides the value + provided by the pod spec. Value must be non-negative integer. + The value zero indicates stop immediately via the kill signal + (no opportunity to shut down). This is a beta field and + requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is + used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object podSecurityContext: description: SecurityContext holds pod-level security attributes and common container settings. This defaults to the default @@ -2449,6 +2603,155 @@ spec: priorityClassName: description: PriorityClassName assigned to the Pods type: string + readinessProbe: + description: Periodic probe of container service readiness. Container + will be removed from service endpoints if the probe fails. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute inside + the container, the working directory for the command is + root ('/') in the container's filesystem. The command + is simply exec'd, it is not run inside a shell, so traditional + shell instructions ('|', etc) won't work. To use a shell, + you need to explicitly call out to that shell. Exit + status of 0 is treated as live/healthy and non-zero + is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe to + be considered failed after having succeeded. Defaults to + 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + This is a beta field and requires enabling GRPCContainerProbe + feature gate. + properties: + port: + description: Port number of the gRPC service. Number must + be in the range 1 to 65535. + format: int32 + type: integer + service: + description: "Service is the name of the service to place + in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + \n If this is not specified, the default behavior is + defined by gRPC." + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header to + be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on the + container. Number must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has started + before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe to + be considered successful after having failed. Defaults to + 1. Must be 1 for liveness and startup. Minimum value is + 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on the + container. Number must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs to + terminate gracefully upon probe failure. The grace period + is the duration in seconds after the processes running in + the pod are sent a termination signal and the time when + the processes are forcibly halted with a kill signal. Set + this value longer than the expected cleanup time for your + process. If this value is nil, the pod's terminationGracePeriodSeconds + will be used. Otherwise, this value overrides the value + provided by the pod spec. Value must be non-negative integer. + The value zero indicates stop immediately via the kill signal + (no opportunity to shut down). This is a beta field and + requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is + used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object resources: description: Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ diff --git a/helm/index.yaml b/helm/index.yaml index 9396094e..c9f3ee35 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.0.32 + created: "2023-11-20T11:28:07.27529149+02:00" + description: A Helm chart to install Vector Operator + digest: 26323037ec47f1703ea930a99ab4ec8fb93b44975ce969514ea68d4130017015 + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.32.tgz + version: 0.0.32 - apiVersion: v2 appVersion: v0.0.31 - created: "2023-11-09T16:57:22.989212+02:00" + created: "2023-11-20T11:28:07.274406503+02:00" description: A Helm chart to install Vector Operator digest: 45b924c07a825e0f7cd3fb534a6ffd16604790d13be1aff59150c045474754e3 home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.0.31 - apiVersion: v2 appVersion: v0.0.30 - created: "2023-11-09T16:57:22.98808+02:00" + created: "2023-11-20T11:28:07.273000231+02:00" description: A Helm chart to install Vector Operator digest: 03beda549d15f50325028ea29af5f2065ac0b8adf3078bf7dc1312981aa5e7db home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.0.30 - apiVersion: v2 appVersion: v0.0.29 - created: "2023-11-09T16:57:22.987318+02:00" + created: "2023-11-20T11:28:07.272108201+02:00" description: A Helm chart to install Vector Operator digest: 0f025fc3a924b37b8c4131c4d8cfa437d2d4e557ab9476ed3e69a00232c7dca6 home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.0.29 - apiVersion: v2 appVersion: v0.0.28 - created: "2023-11-09T16:57:22.986561+02:00" + created: "2023-11-20T11:28:07.271169582+02:00" description: A Helm chart to install Vector Operator digest: af856d41314313e04f15e7143409a9c564c6ca610b0d2eaec3112add8573e668 home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.0.28 - apiVersion: v2 appVersion: v0.0.27 - created: "2023-11-09T16:57:22.985798+02:00" + created: "2023-11-20T11:28:07.269812986+02:00" description: A Helm chart to install Vector Operator digest: 631e2ff02bbd7f247cb486494fd2af60c57cc551066a6a3858226551bc1745a4 home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.0.27 - apiVersion: v2 appVersion: v0.0.26 - created: "2023-11-09T16:57:22.984377+02:00" + created: "2023-11-20T11:28:07.268711612+02:00" description: A Helm chart to install Vector Operator digest: 760a2833f4c1a33466982419b079ff18d996331ebacc40cf93b0f55229cdb7db home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.0.26 - apiVersion: v2 appVersion: v0.0.25 - created: "2023-11-09T16:57:22.983593+02:00" + created: "2023-11-20T11:28:07.267738585+02:00" description: A Helm chart to install Vector Operator digest: fd22b996b071b6d85740ccf76e85cb640fa717c2620748d206d3f4fdd44cbcc2 home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.0.25 - apiVersion: v2 appVersion: v0.0.24 - created: "2023-11-09T16:57:22.982842+02:00" + created: "2023-11-20T11:28:07.266829797+02:00" description: A Helm chart to install Vector Operator digest: ea257e60ecde063a0d1ed52ce5e3283245b8f0e2daba58ea3a5adb0ba82d7799 home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.0.24 - apiVersion: v2 appVersion: v0.0.23 - created: "2023-11-09T16:57:22.982081+02:00" + created: "2023-11-20T11:28:07.265485653+02:00" description: A Helm chart to install Vector Operator digest: 546d202b3b9263f789b88335263191098dfcabd5d8698105f37cad24d56a8ed0 home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.0.23 - apiVersion: v2 appVersion: v0.0.22 - created: "2023-11-09T16:57:22.980961+02:00" + created: "2023-11-20T11:28:07.264300127+02:00" description: A Helm chart to install Vector Operator digest: bf96ddc8ac61e9d6beb8bc763fbf3fa6025d950b29d70d80de6e8a0ea45e0411 home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.0.22 - apiVersion: v2 appVersion: v0.0.21 - created: "2023-11-09T16:57:22.980161+02:00" + created: "2023-11-20T11:28:07.263238533+02:00" description: A Helm chart to install Vector Operator digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d home: https://github.com/kaasops/vector-operator @@ -146,7 +159,7 @@ entries: version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2023-11-09T16:57:22.97932+02:00" + created: "2023-11-20T11:28:07.262189691+02:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -159,7 +172,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2023-11-09T16:57:22.978658+02:00" + created: "2023-11-20T11:28:07.26127816+02:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -172,7 +185,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2023-11-09T16:57:22.977939+02:00" + created: "2023-11-20T11:28:07.260589367+02:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -185,7 +198,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2023-11-09T16:57:22.977413+02:00" + created: "2023-11-20T11:28:07.259937731+02:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -198,7 +211,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2023-11-09T16:57:22.976875+02:00" + created: "2023-11-20T11:28:07.259366886+02:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -211,7 +224,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2023-11-09T16:57:22.976334+02:00" + created: "2023-11-20T11:28:07.258741078+02:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -224,7 +237,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2023-11-09T16:57:22.975771+02:00" + created: "2023-11-20T11:28:07.257702279+02:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -237,7 +250,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2023-11-09T16:57:22.975153+02:00" + created: "2023-11-20T11:28:07.256851504+02:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -250,7 +263,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2023-11-09T16:57:22.973678+02:00" + created: "2023-11-20T11:28:07.256276081+02:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -263,7 +276,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2023-11-09T16:57:22.973116+02:00" + created: "2023-11-20T11:28:07.255655831+02:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -276,7 +289,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2023-11-09T16:57:22.972628+02:00" + created: "2023-11-20T11:28:07.255123529+02:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -289,7 +302,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2023-11-09T16:57:22.9908+02:00" + created: "2023-11-20T11:28:07.277344543+02:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -302,7 +315,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2023-11-09T16:57:22.990402+02:00" + created: "2023-11-20T11:28:07.276828286+02:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -315,7 +328,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2023-11-09T16:57:22.989998+02:00" + created: "2023-11-20T11:28:07.276348629+02:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -328,7 +341,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2023-11-09T16:57:22.989603+02:00" + created: "2023-11-20T11:28:07.275787866+02:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -341,7 +354,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2023-11-09T16:57:22.97211+02:00" + created: "2023-11-20T11:28:07.254493609+02:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -352,4 +365,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2023-11-09T16:57:22.971209+02:00" +generated: "2023-11-20T11:28:07.24712405+02:00" diff --git a/helm/packages/vector-operator-0.0.32.tgz b/helm/packages/vector-operator-0.0.32.tgz new file mode 100644 index 0000000000000000000000000000000000000000..c027800b3d123b07483deec8bfa3ec5e52c070af GIT binary patch literal 32988 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 Date: Tue, 28 Nov 2023 17:59:03 +0200 Subject: [PATCH 246/316] config build refactoring and add use_apiserver_cache --- Makefile | 2 +- api/v1alpha1/clustervectorpipeline.go | 14 +- api/v1alpha1/vector_types.go | 10 +- api/v1alpha1/vectorpipeline.go | 14 +- .../observability.kaasops.io_vectors.yaml | 9 +- controllers/factory/config/config.go | 80 ---- controllers/factory/config/config_build.go | 444 ------------------ controllers/factory/config/types.go | 54 --- controllers/factory/config/utils.go | 31 -- controllers/pipeline_controller.go | 13 +- controllers/vector_controller.go | 15 +- go.mod | 32 +- go.sum | 72 ++- main.go | 2 +- pkg/config/config.go | 161 +++++++ .../config/configcheck/configcheck.go | 2 +- .../config/configcheck/configcheck_config.go | 2 +- .../config/configcheck/configcheck_error.go | 0 .../config/configcheck/configcheck_pod.go | 0 .../config/configcheck/configcheck_rbac.go | 0 .../config/configcheck/const.go | 0 pkg/config/default.go | 49 ++ pkg/config/types.go | 66 +++ pkg/config/utils.go | 12 + {controllers/factory => pkg}/pipeline/hash.go | 2 +- .../factory => pkg}/pipeline/pipeline.go | 4 +- .../utils/compression/compression.go | 0 .../factory => pkg}/utils/hash/hash.go | 0 .../factory => pkg}/utils/hash/hash_test.go | 2 +- {controllers/factory => pkg}/utils/k8s/k8s.go | 0 .../factory => pkg}/utils/k8s/k8s_test.go | 2 +- .../factory => pkg}/utils/k8s/label.go | 0 .../vector/vectoragent/vectoragent.go | 2 +- .../vector/vectoragent/vectoragent_config.go | 2 +- .../vectoragent/vectoragent_controller.go | 2 +- .../vectoragent/vectoragent_daemonset.go | 2 +- .../vector/vectoragent/vectoragent_default.go | 0 .../vectoragent/vectoragent_podmonitor.go | 0 .../vector/vectoragent/vectoragent_rbac.go | 0 .../vector/vectoragent/vectoragent_service.go | 0 40 files changed, 368 insertions(+), 734 deletions(-) delete mode 100644 controllers/factory/config/config.go delete mode 100644 controllers/factory/config/config_build.go delete mode 100644 controllers/factory/config/types.go delete mode 100644 controllers/factory/config/utils.go create mode 100644 pkg/config/config.go rename {controllers/factory => pkg}/config/configcheck/configcheck.go (99%) rename {controllers/factory => pkg}/config/configcheck/configcheck_config.go (94%) rename {controllers/factory => pkg}/config/configcheck/configcheck_error.go (100%) rename {controllers/factory => pkg}/config/configcheck/configcheck_pod.go (100%) rename {controllers/factory => pkg}/config/configcheck/configcheck_rbac.go (100%) rename {controllers/factory => pkg}/config/configcheck/const.go (100%) create mode 100644 pkg/config/default.go create mode 100644 pkg/config/types.go create mode 100644 pkg/config/utils.go rename {controllers/factory => pkg}/pipeline/hash.go (94%) rename {controllers/factory => pkg}/pipeline/pipeline.go (98%) rename {controllers/factory => pkg}/utils/compression/compression.go (100%) rename {controllers/factory => pkg}/utils/hash/hash.go (100%) rename {controllers/factory => pkg}/utils/hash/hash_test.go (94%) rename {controllers/factory => pkg}/utils/k8s/k8s.go (100%) rename {controllers/factory => pkg}/utils/k8s/k8s_test.go (99%) rename {controllers/factory => pkg}/utils/k8s/label.go (100%) rename {controllers/factory => pkg}/vector/vectoragent/vectoragent.go (96%) rename {controllers/factory => pkg}/vector/vectoragent/vectoragent_config.go (93%) rename {controllers/factory => pkg}/vector/vectoragent/vectoragent_controller.go (98%) rename {controllers/factory => pkg}/vector/vectoragent/vectoragent_daemonset.go (99%) rename {controllers/factory => pkg}/vector/vectoragent/vectoragent_default.go (100%) rename {controllers/factory => pkg}/vector/vectoragent/vectoragent_podmonitor.go (100%) rename {controllers/factory => pkg}/vector/vectoragent/vectoragent_rbac.go (100%) rename {controllers/factory => pkg}/vector/vectoragent/vectoragent_service.go (100%) diff --git a/Makefile b/Makefile index e1eba2b9..0c39cffc 100644 --- a/Makefile +++ b/Makefile @@ -48,7 +48,7 @@ ifeq ($(USE_IMAGE_DIGESTS), true) endif # Image URL to use all building/pushing image targets -IMG ?= controller:latest +IMG ?= docker.io/kaasops/vector-operator:test6 # ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. ENVTEST_K8S_VERSION = 1.24.2 diff --git a/api/v1alpha1/clustervectorpipeline.go b/api/v1alpha1/clustervectorpipeline.go index aba26cc5..3d09a4f7 100644 --- a/api/v1alpha1/clustervectorpipeline.go +++ b/api/v1alpha1/clustervectorpipeline.go @@ -3,7 +3,7 @@ package v1alpha1 import ( "context" - "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" + "github.com/kaasops/vector-operator/pkg/utils/k8s" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -15,18 +15,6 @@ func (vp *ClusterVectorPipeline) GetSpec() VectorPipelineSpec { return vp.Spec } -func (vp *ClusterVectorPipeline) GetName() string { - return vp.Name -} - -func (vp *ClusterVectorPipeline) GetNamespace() string { - return vp.Namespace -} - -func (vp *ClusterVectorPipeline) Type() string { - return vp.Kind -} - func (vp *ClusterVectorPipeline) IsValid() bool { if vp.Status.ConfigCheckResult != nil { return *vp.Status.ConfigCheckResult diff --git a/api/v1alpha1/vector_types.go b/api/v1alpha1/vector_types.go index baec1324..7c1fee98 100644 --- a/api/v1alpha1/vector_types.go +++ b/api/v1alpha1/vector_types.go @@ -30,14 +30,8 @@ type VectorSpec struct { // DisableAggregation bool `json:"disableAggregation,omitempty"` // Vector Agent Agent *VectorAgent `json:"agent,omitempty"` - - // Merge kubernetes sources and move selectors processing to transforms. - // +optional - MergeKubernetesSources bool `json:"mergeKubernetesSources,omitempty"` - // Merge kubernetes sink with equal options. - // +optional - MergeSinks bool `json:"mergeSinks,omitempty"` - + // Determines if requests to the kube-apiserver can be served by a cache. + UseApiServerCache bool `json:"use_apiserver_cache,omitempty"` // Vector Aggregator // Aggregator *VectorAggregator `json:"aggregator,omitempty"` } diff --git a/api/v1alpha1/vectorpipeline.go b/api/v1alpha1/vectorpipeline.go index ebcde2f4..302d181b 100644 --- a/api/v1alpha1/vectorpipeline.go +++ b/api/v1alpha1/vectorpipeline.go @@ -3,7 +3,7 @@ package v1alpha1 import ( "context" - "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" + "github.com/kaasops/vector-operator/pkg/utils/k8s" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -15,18 +15,6 @@ func (vp *VectorPipeline) GetSpec() VectorPipelineSpec { return vp.Spec } -func (vp *VectorPipeline) GetName() string { - return vp.Name -} - -func (vp *VectorPipeline) GetNamespace() string { - return vp.Namespace -} - -func (vp *VectorPipeline) Type() string { - return vp.Kind -} - func (vp *VectorPipeline) IsValid() bool { if vp.Status.ConfigCheckResult != nil { return *vp.Status.ConfigCheckResult diff --git a/config/crd/bases/observability.kaasops.io_vectors.yaml b/config/crd/bases/observability.kaasops.io_vectors.yaml index 5d45ae4f..b244fdf5 100644 --- a/config/crd/bases/observability.kaasops.io_vectors.yaml +++ b/config/crd/bases/observability.kaasops.io_vectors.yaml @@ -4443,12 +4443,9 @@ spec: type: object type: array type: object - mergeKubernetesSources: - description: Merge kubernetes sources and move selectors processing - to transforms. - type: boolean - mergeSinks: - description: Merge kubernetes sink with equal options. + use_apiserver_cache: + description: Determines if requests to the kube-apiserver can be served + by a cache. type: boolean type: object status: diff --git a/controllers/factory/config/config.go b/controllers/factory/config/config.go deleted file mode 100644 index 87d19bc3..00000000 --- a/controllers/factory/config/config.go +++ /dev/null @@ -1,80 +0,0 @@ -/* -Copyright 2022. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package config - -import ( - "strconv" - - vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" - "github.com/kaasops/vector-operator/controllers/factory/vector/vectoragent" - "github.com/mitchellh/mapstructure" -) - -type ApiSpec struct { - Address string `json:"address,omitempty"` - Enabled bool `json:"enabled,omitempty"` - Playground bool `json:"playground,omitempty"` -} - -func New(vector *vectorv1alpha1.Vector) *VectorConfig { - sources := []*Source{} - sinks := []*Sink{} - - api := &ApiSpec{ - Address: "0.0.0.0:" + strconv.Itoa(vectoragent.ApiPort), - Enabled: vector.Spec.Agent.Api.Enabled, - Playground: vector.Spec.Agent.Api.Playground, - } - - return &VectorConfig{ - DataDir: "/vector-data-dir", - Api: api, - Sources: sources, - Sinks: sinks, - } -} - -func Mapper(c ConfigComponent) (map[string]interface{}, error) { - spec := c.GetOptions() - config := &mapstructure.DecoderConfig{ - Result: &spec, - ZeroFields: false, - TagName: "mapper", - IgnoreUntaggedFields: true, - } - decoder, err := mapstructure.NewDecoder(config) - if err != nil { - return nil, err - } - err = decoder.Decode(c) - if err != nil { - return nil, err - } - return spec, nil -} - -func (t Source) GetOptions() map[string]interface{} { - return t.Options -} - -func (t Transform) GetOptions() map[string]interface{} { - return t.Options -} - -func (t Sink) GetOptions() map[string]interface{} { - return t.Options -} diff --git a/controllers/factory/config/config_build.go b/controllers/factory/config/config_build.go deleted file mode 100644 index b7f62954..00000000 --- a/controllers/factory/config/config_build.go +++ /dev/null @@ -1,444 +0,0 @@ -/* -Copyright 2022. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package config - -import ( - "bytes" - "encoding/json" - "errors" - "fmt" - "sort" - "strings" - - vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" - "github.com/kaasops/vector-operator/controllers/factory/pipeline" - "github.com/kaasops/vector-operator/controllers/factory/utils/hash" - "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" - "github.com/kaasops/vector-operator/controllers/factory/vector/vectoragent" - "github.com/mitchellh/mapstructure" -) - -const ( - KubernetesSourceType = "kubernetes_logs" - BlackholeSinkType = "blackhole" - InternalMetricsSourceType = "internal_metrics" - InternalMetricsSourceName = "internalMetricsSource" - InternalMetricsSinkType = "prometheus_exporter" - InternalMetricsSinkName = "internalMetricsSink" - MergedKubernetesSourceName = "mergedKubernetesSource" - MergedSourceTransformName = "merged" - RouteTransformType = "route" - DefaultSourceName = "defaultSource" - PodSelectorType = "pod_labels" - NamespaceSelectorType = "ns_labels" - OptimizationConditionType = "vrl" -) - -var ( - sourceDefault = &Source{ - Name: "defaultSource", - Type: KubernetesSourceType, - } - internalMetricSource = &Source{ - Name: InternalMetricsSourceName, - Type: InternalMetricsSourceType, - } - sinkDefault = &Sink{ - Name: "defaultSink", - Type: BlackholeSinkType, - Inputs: []string{sourceDefault.Name}, - Options: map[string]interface{}{ - "rate": 100, - "print_interval_secs": 60, - }, - } - internalMetricsExporter = &Sink{ - Name: InternalMetricsSinkName, - Type: InternalMetricsSinkType, - Inputs: []string{internalMetricSource.Name}, - } -) - -var ( - PipelineTypeError error = errors.New("type kubernetes_logs only allowed") - PipelineScopeError error = errors.New("logs from external namespace not allowed") -) - -type Builder struct { - Name string - vaCtrl *vectoragent.Controller - Pipelines []pipeline.Pipeline -} - -func NewBuilder(vaCtrl *vectoragent.Controller, pipelines ...pipeline.Pipeline) *Builder { - return &Builder{ - vaCtrl: vaCtrl, - Pipelines: pipelines, - } -} - -func (b *Builder) GetByteConfig() ([]byte, error) { - config, err := b.generateVectorConfig() - if err != nil { - return nil, err - } - data, err := vectorConfigToByte(config) - if err != nil { - return nil, err - } - return data, nil -} - -func (b *Builder) generateVectorConfig() (*VectorConfig, error) { - vectorConfig := New(b.vaCtrl.Vector) - - sources, transforms, sinks, err := b.getComponents() - if err != nil { - return nil, err - } - - if b.vaCtrl.Vector.Spec.Agent.InternalMetrics && !isExporterSinkExists(sinks) { - sources = append(sources, internalMetricSource) - sinks = append(sinks, internalMetricsExporter) - } - - if len(sources) == 0 { - sources = []*Source{sourceDefault} - } - if len(sinks) == 0 { - sinks = []*Sink{sinkDefault} - } - - vectorConfig.Sinks = sinks - vectorConfig.Sources = sources - vectorConfig.Transforms = transforms - - if b.vaCtrl.Vector.Spec.MergeKubernetesSources { - if err := b.mergeKubernetesSources(vectorConfig); err != nil { - return nil, err - } - } - - if b.vaCtrl.Vector.Spec.MergeSinks { - if err := b.mergeSyncs(vectorConfig); err != nil { - return nil, err - } - } - - return vectorConfig, nil -} - -func (b *Builder) getComponents() (sources []*Source, transforms []*Transform, sinks []*Sink, err error) { - for _, pipeline := range b.Pipelines { - pipelineSources, err := getSources(pipeline, nil) - if err != nil { - return nil, nil, nil, err - } - for _, source := range pipelineSources { - if err != nil { - return nil, nil, nil, err - } - if source.Type == KubernetesSourceType { - if pipeline.Type() != vectorv1alpha1.ClusterPipelineKind && source.ExtraNamespaceLabelSelector == "" { - source.ExtraNamespaceLabelSelector = k8s.NamespaceNameToLabel(pipeline.GetNamespace()) - } - } - if pipeline.Type() != vectorv1alpha1.ClusterPipelineKind { - if source.Type != KubernetesSourceType { - return nil, nil, nil, PipelineTypeError - } - if source.Type == InternalMetricsSourceType { - return nil, nil, nil, PipelineTypeError - } - if source.ExtraNamespaceLabelSelector != "" { - if source.ExtraNamespaceLabelSelector != k8s.NamespaceNameToLabel(pipeline.GetNamespace()) { - return nil, nil, nil, PipelineScopeError - } - } - } - sources = append(sources, source) - } - pipelineTransforms, err := getTransforms(pipeline) - if err != nil { - return nil, nil, nil, err - } - for _, transform := range pipelineTransforms { - if err != nil { - return nil, nil, nil, err - } - transforms = append(transforms, transform) - } - pipelineSinks, err := getSinks(pipeline) - if err != nil { - return nil, nil, nil, err - } - for _, sink := range pipelineSinks { - if err != nil { - return nil, nil, nil, err - } - sinks = append(sinks, sink) - } - } - return sources, transforms, sinks, nil -} - -func vectorConfigToByte(config *VectorConfig) ([]byte, error) { - cfgMap, err := cfgToMap(config) - if err != nil { - return nil, err - } - data, err := json.Marshal(cfgMap) - if err != nil { - return nil, err - } - return data, nil -} - -func getSources(pipeline pipeline.Pipeline, filter []string) ([]*Source, error) { - if pipeline.GetSpec().Sources == nil { - return nil, nil - } - var sources []*Source - sourcesMap, err := decodeRaw(pipeline.GetSpec().Sources.Raw) - if err != nil { - return nil, err - } - for k, v := range sourcesMap { - if len(filter) != 0 { - if !contains(filter, k) { - continue - } - } - var source *Source - if err := mapstructure.Decode(v, &source); err != nil { - return nil, err - } - source.Name = addPrefix(pipeline.GetNamespace(), pipeline.GetName(), k) - sources = append(sources, source) - } - return sources, nil -} - -func getTransforms(pipeline pipeline.Pipeline) ([]*Transform, error) { - if pipeline.GetSpec().Transforms == nil { - return nil, nil - } - transformsMap, err := decodeRaw(pipeline.GetSpec().Transforms.Raw) - if err != nil { - return nil, err - } - var transforms []*Transform - if err := json.Unmarshal(pipeline.GetSpec().Transforms.Raw, &transformsMap); err != nil { - return nil, err - } - for k, v := range transformsMap { - var transform *Transform - if err := mapstructure.Decode(v, &transform); err != nil { - return nil, err - } - transform.Name = addPrefix(pipeline.GetNamespace(), pipeline.GetName(), k) - for i, inputName := range transform.Inputs { - transform.Inputs[i] = addPrefix(pipeline.GetNamespace(), pipeline.GetName(), inputName) - } - transforms = append(transforms, transform) - } - return transforms, nil -} - -func getSinks(pipeline pipeline.Pipeline) ([]*Sink, error) { - if pipeline.GetSpec().Sinks == nil { - return nil, nil - } - sinksMap, err := decodeRaw(pipeline.GetSpec().Sinks.Raw) - if err != nil { - return nil, err - } - var sinks []*Sink - for k, v := range sinksMap { - var sink *Sink - if err := mapstructure.Decode(v, &sink); err != nil { - return nil, err - } - sink.Name = addPrefix(pipeline.GetNamespace(), pipeline.GetName(), k) - for i, inputName := range sink.Inputs { - sink.Inputs[i] = addPrefix(pipeline.GetNamespace(), pipeline.GetName(), inputName) - } - optbyte, err := json.Marshal(sink.Options) - if err != nil { - return nil, err - } - sink.OptionsHash = fmt.Sprint(hash.Get(optbyte)) - sinks = append(sinks, sink) - } - return sinks, nil -} - -func cfgToMap(config *VectorConfig) (cfgMap map[string]interface{}, err error) { - sources := make(map[string]interface{}) - transforms := make(map[string]interface{}) - sinks := make(map[string]interface{}) - for _, source := range config.Sources { - spec, err := Mapper(source) - if err != nil { - return nil, err - } - sources[source.Name] = spec - } - for _, transform := range config.Transforms { - spec, err := Mapper(transform) - if err != nil { - return nil, err - } - transforms[transform.Name] = spec - } - for _, sink := range config.Sinks { - spec, err := Mapper(sink) - if err != nil { - return nil, err - } - sinks[sink.Name] = spec - } - - err = mapstructure.Decode(config, &cfgMap) - if err != nil { - return nil, err - } - // TODO: remove hardcoded map keys - cfgMap["sources"] = sources - cfgMap["transforms"] = transforms - cfgMap["sinks"] = sinks - - return cfgMap, nil -} - -func isExporterSinkExists(sinks []*Sink) bool { - for _, sink := range sinks { - if sink.Type == InternalMetricsSinkType { - return true - } - } - return false -} - -// Merges a large number of kubernetes sources to reduce k8s api pressure during vector start. -func (b *Builder) mergeKubernetesSources(config *VectorConfig) error { - var mergedSource []*Source - routes := make(map[string]string) - for _, source := range config.Sources { - if source.Type == KubernetesSourceType { - if source.ExtraFieldSelector == "" && source.ExtraNamespaceLabelSelector != "" && source.ExtraLabelSelector != "" && source.Options == nil { - routes[source.Name] = generateVrlFilter(source.ExtraLabelSelector, PodSelectorType) + "&&" + generateVrlFilter(source.ExtraNamespaceLabelSelector, NamespaceSelectorType) - continue - } - } - mergedSource = append(mergedSource, source) - } - - if len(routes) > 0 { - mergedSource = append(mergedSource, &Source{ - Name: MergedKubernetesSourceName, - Type: KubernetesSourceType, - }) - transform := &Transform{ - Name: MergedSourceTransformName, - Type: RouteTransformType, - Inputs: []string{MergedKubernetesSourceName}, - Route: routes, - } - - for _, t := range config.Transforms { - for n, i := range t.Inputs { - _, ok := routes[i] - if ok { - t.Inputs[n] = MergedSourceTransformName + "." + i - - } - } - } - - for _, t := range config.Sinks { - for n, i := range t.Inputs { - _, ok := routes[i] - if ok { - t.Inputs[n] = MergedSourceTransformName + "." + i - - } - } - } - config.Sources = mergedSource - config.Transforms = append(config.Transforms, transform) - } - - return nil -} - -func (b *Builder) mergeSyncs(config *VectorConfig) error { - uniqOpts := make(map[string]*Sink) - var mergedSinks []*Sink - - for _, sink := range config.Sinks { - v, ok := uniqOpts[sink.OptionsHash] - if ok { - if sink.Type == v.Type { - // If sink spec already exists rename and merge inputs - v.Name = v.OptionsHash - v.Inputs = append(v.Inputs, sink.Inputs...) - sort.Strings(v.Inputs) - continue - } - } - uniqOpts[sink.OptionsHash] = sink - mergedSinks = append(mergedSinks, sink) - } - - if len(mergedSinks) > 0 { - config.Sinks = mergedSinks - } - return nil -} - -func generateVrlFilter(selector string, selectorType string) string { - buffer := new(bytes.Buffer) - - labels := strings.Split(selector, ",") - - for _, label := range labels { - label = formatVrlSelector(label) - switch selectorType { - case PodSelectorType: - fmt.Fprintf(buffer, ".kubernetes.pod_labels.%s&&", label) - case NamespaceSelectorType: - fmt.Fprintf(buffer, ".kubernetes.namespace_labels.%s&&", label) - } - } - - vrlstring := buffer.String() - return strings.TrimSuffix(vrlstring, "&&") -} - -func formatVrlSelector(label string) string { - l := strings.Split(label, "!=") - if len(l) == 2 { - return fmt.Sprintf("\"%s\" != \"%s\"", l[0], l[1]) - } - - l = strings.Split(label, "=") - if len(l) != 2 { - return label - } - return fmt.Sprintf("\"%s\" == \"%s\"", l[0], l[1]) -} diff --git a/controllers/factory/config/types.go b/controllers/factory/config/types.go deleted file mode 100644 index 2e5b3b54..00000000 --- a/controllers/factory/config/types.go +++ /dev/null @@ -1,54 +0,0 @@ -/* -Copyright 2022. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package config - -type VectorConfig struct { - DataDir string `mapstructure:"data_dir"` - Api *ApiSpec `mapstructure:"api"` - Sources []*Source `mapstructure:"sources"` - Transforms []*Transform `mapstructure:"transforms"` - Sinks []*Sink `mapstructure:"sinks"` -} - -type Source struct { - Name string - Type string `mapper:"type"` - ExtraNamespaceLabelSelector string `mapstructure:"extra_namespace_label_selector" mapper:"extra_namespace_label_selector,omitempty"` - ExtraLabelSelector string `mapstructure:"extra_label_selector" mapper:"extra_label_selector,omitempty"` - ExtraFieldSelector string `mapstructure:"extra_field_selector" mapper:"extra_field_selector,omitempty"` - Options map[string]interface{} `mapstructure:",remain"` -} - -type Transform struct { - Name string - Type string `mapper:"type"` - Inputs []string `mapper:"inputs"` - Route map[string]string `mapper:"route,omitempty"` - Options map[string]interface{} `mapstructure:",remain"` -} - -type Sink struct { - Name string - Type string `mapper:"type"` - Inputs []string `mapper:"inputs"` - Options map[string]interface{} `mapstructure:",remain"` - OptionsHash string -} - -type ConfigComponent interface { - GetOptions() map[string]interface{} -} diff --git a/controllers/factory/config/utils.go b/controllers/factory/config/utils.go deleted file mode 100644 index 4d8ab0f4..00000000 --- a/controllers/factory/config/utils.go +++ /dev/null @@ -1,31 +0,0 @@ -package config - -import "encoding/json" - -func decodeRaw(raw []byte) (map[string]interface{}, error) { - result := make(map[string]interface{}) - if err := json.Unmarshal(raw, &result); err != nil { - return nil, err - } - return result, nil -} - -func contains(elems []string, v string) bool { - for _, s := range elems { - if v == s { - return true - } - } - return false -} - -func addPrefix(Namespace, Name, componentName string) string { - return generateName(Namespace, Name) + "-" + componentName -} - -func generateName(Namespace, Name string) string { - if Namespace != "" { - return Namespace + "-" + Name - } - return Name -} diff --git a/controllers/pipeline_controller.go b/controllers/pipeline_controller.go index 10d80ca2..d55caafb 100644 --- a/controllers/pipeline_controller.go +++ b/controllers/pipeline_controller.go @@ -33,10 +33,10 @@ import ( "sigs.k8s.io/controller-runtime/pkg/source" vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" - "github.com/kaasops/vector-operator/controllers/factory/config" - "github.com/kaasops/vector-operator/controllers/factory/config/configcheck" - "github.com/kaasops/vector-operator/controllers/factory/pipeline" - "github.com/kaasops/vector-operator/controllers/factory/vector/vectoragent" + "github.com/kaasops/vector-operator/pkg/config" + "github.com/kaasops/vector-operator/pkg/config/configcheck" + "github.com/kaasops/vector-operator/pkg/pipeline" + "github.com/kaasops/vector-operator/pkg/vector/vectoragent" ) type PipelineReconciler struct { @@ -110,12 +110,9 @@ func (r *PipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c // Init Controller for Vector Agent vaCtrl := vectoragent.NewController(vector, r.Client, r.Clientset) - vaCtrl.SetDefault() // Get Vector Config file - configBuilder := config.NewBuilder(vaCtrl, pipelineCR) - - byteConfig, err := configBuilder.GetByteConfig() + byteConfig, _ := config.BuildByteConfig(vaCtrl, pipelineCR) if err != nil { if err := pipeline.SetFailedStatus(ctx, r.Client, pipelineCR, err.Error()); err != nil { return ctrl.Result{}, err diff --git a/controllers/vector_controller.go b/controllers/vector_controller.go index 459c1e49..a4c7cbab 100644 --- a/controllers/vector_controller.go +++ b/controllers/vector_controller.go @@ -22,12 +22,12 @@ import ( "sync" "time" - "github.com/kaasops/vector-operator/controllers/factory/config" - "github.com/kaasops/vector-operator/controllers/factory/config/configcheck" - "github.com/kaasops/vector-operator/controllers/factory/pipeline" - "github.com/kaasops/vector-operator/controllers/factory/utils/hash" - "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" - "github.com/kaasops/vector-operator/controllers/factory/vector/vectoragent" + "github.com/kaasops/vector-operator/pkg/config" + "github.com/kaasops/vector-operator/pkg/config/configcheck" + "github.com/kaasops/vector-operator/pkg/pipeline" + "github.com/kaasops/vector-operator/pkg/utils/hash" + "github.com/kaasops/vector-operator/pkg/utils/k8s" + "github.com/kaasops/vector-operator/pkg/vector/vectoragent" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -184,10 +184,9 @@ func (r *VectorReconciler) createOrUpdateVector(ctx context.Context, client clie if err != nil { return ctrl.Result{}, err } - configBuilder := config.NewBuilder(vaCtrl, pipelines...) // Get Config in Json ([]byte) - byteConfig, err := configBuilder.GetByteConfig() + byteConfig, err := config.BuildByteConfig(vaCtrl, pipelines...) if err != nil { return ctrl.Result{}, err } diff --git a/go.mod b/go.mod index 7da08190..f7027fd7 100644 --- a/go.mod +++ b/go.mod @@ -3,16 +3,19 @@ module github.com/kaasops/vector-operator go 1.18 require ( + github.com/go-logr/logr v1.2.3 github.com/mitchellh/mapstructure v1.5.0 github.com/onsi/ginkgo v1.16.5 github.com/onsi/gomega v1.19.0 github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.60.1 - github.com/stretchr/testify v1.7.0 + github.com/stretchr/testify v1.8.1 + gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.25.0 k8s.io/apimachinery v0.25.0 k8s.io/client-go v0.25.0 k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73 sigs.k8s.io/controller-runtime v0.12.3 + sigs.k8s.io/yaml v1.4.0 ) require ( @@ -23,31 +26,28 @@ require ( github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect - github.com/PuerkitoBio/purell v1.1.1 // indirect - github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/emicklei/go-restful/v3 v3.8.0 // indirect github.com/evanphx/json-patch v4.12.0+incompatible // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect - github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/zapr v1.2.3 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.19.5 // indirect - github.com/go-openapi/swag v0.19.14 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.1 // indirect + github.com/go-openapi/swag v0.22.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.2.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect - github.com/google/go-cmp v0.5.8 // indirect + github.com/google/go-cmp v0.5.9 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/uuid v1.1.2 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/mailru/easyjson v0.7.6 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -63,25 +63,23 @@ require ( go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.19.1 // indirect - golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect - golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b // indirect + golang.org/x/crypto v0.7.0 // indirect + golang.org/x/net v0.8.0 // indirect golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect - golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect - golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect - golang.org/x/text v0.3.7 // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/term v0.6.0 // indirect + golang.org/x/text v0.8.0 // indirect golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.28.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.25.0 // indirect k8s.io/component-base v0.25.0 // indirect k8s.io/klog/v2 v2.80.0 // indirect k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect - sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect - sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index d113a4ce..bb6b8abe 100644 --- a/go.sum +++ b/go.sum @@ -63,10 +63,6 @@ github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBp github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -131,14 +127,12 @@ github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM= -github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= -github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8= +github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -197,8 +191,8 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -254,14 +248,14 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= -github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= @@ -278,8 +272,6 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= @@ -334,13 +326,18 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -372,8 +369,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38= -golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -424,7 +421,6 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -451,8 +447,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY= -golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -541,11 +537,11 @@ golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -554,8 +550,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -759,8 +755,8 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= @@ -810,9 +806,9 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/controller-runtime v0.12.3 h1:FCM8xeY/FI8hoAfh/V4XbbYMY20gElh9yh+A98usMio= sigs.k8s.io/controller-runtime v0.12.3/go.mod h1:qKsk4WE6zW2Hfj0G4v10EnNB2jMG1C+NTb8h+DwCoU0= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= -sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/main.go b/main.go index 9fda9f72..27f07b7e 100644 --- a/main.go +++ b/main.go @@ -43,7 +43,7 @@ import ( observabilityv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" "github.com/kaasops/vector-operator/controllers" - "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" + "github.com/kaasops/vector-operator/pkg/utils/k8s" monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" //+kubebuilder:scaffold:imports ) diff --git a/pkg/config/config.go b/pkg/config/config.go new file mode 100644 index 00000000..4bbd7c48 --- /dev/null +++ b/pkg/config/config.go @@ -0,0 +1,161 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package config + +import ( + "encoding/json" + "errors" + "fmt" + "strconv" + + vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" + "github.com/kaasops/vector-operator/pkg/pipeline" + "github.com/kaasops/vector-operator/pkg/utils/k8s" + "github.com/kaasops/vector-operator/pkg/vector/vectoragent" + "github.com/mitchellh/mapstructure" + "gopkg.in/yaml.v2" + goyaml "sigs.k8s.io/yaml" +) + +var ( + ErrNotAllowedSourceType error = errors.New("type kubernetes_logs only allowed") + ErrClusterScopeNotAllowed error = errors.New("logs from external namespace not allowed") +) + +func New(vector *vectorv1alpha1.Vector) *VectorConfig { + sources := make(map[string]*Source) + transforms := make(map[string]*Transform) + sinks := make(map[string]*Sink) + + api := &ApiSpec{ + Address: "0.0.0.0:" + strconv.Itoa(vectoragent.ApiPort), + Enabled: vector.Spec.Agent.Api.Enabled, + Playground: vector.Spec.Agent.Api.Playground, + } + + return &VectorConfig{ + DataDir: "/vector-data-dir", + Api: api, + PipelineConfig: PipelineConfig{ + Sources: sources, + Transforms: transforms, + Sinks: sinks, + }, + } +} + +func BuildByteConfig(vaCtrl *vectoragent.Controller, pipelines ...pipeline.Pipeline) ([]byte, error) { + config, err := BuildConfig(vaCtrl, pipelines...) + if err != nil { + return nil, err + } + yaml_byte, err := yaml.Marshal(config) + if err != nil { + return nil, err + } + json_byte, err := goyaml.YAMLToJSON(yaml_byte) + if err != nil { + return nil, err + } + return json_byte, nil +} + +func BuildConfig(vaCtrl *vectoragent.Controller, pipelines ...pipeline.Pipeline) (*VectorConfig, error) { + config := New(vaCtrl.Vector) + + for _, pipeline := range pipelines { + p := &PipelineConfig{} + if err := UnmarshalJson(pipeline.GetSpec(), p); err != nil { + return nil, fmt.Errorf("failed to unmarshal pipeline %s: %w", pipeline.GetName(), err) + } + for k, v := range p.Sources { + // Validate source + if _, ok := pipeline.(*vectorv1alpha1.VectorPipeline); ok { + if v.Type != KubernetesSourceType { + return nil, ErrNotAllowedSourceType + } + if v.ExtraNamespaceLabelSelector == "" { + v.ExtraNamespaceLabelSelector = k8s.NamespaceNameToLabel(pipeline.GetNamespace()) + } + if v.ExtraNamespaceLabelSelector != k8s.NamespaceNameToLabel(pipeline.GetNamespace()) { + return nil, ErrClusterScopeNotAllowed + } + } + if v.Type == KubernetesSourceType && vaCtrl.Vector.Spec.UseApiServerCache { + v.UseApiServerCache = true + } + v.Name = addPrefix(pipeline.GetNamespace(), pipeline.GetName(), k) + config.Sources[v.Name] = v + } + for k, v := range p.Transforms { + v.Name = addPrefix(pipeline.GetNamespace(), pipeline.GetName(), k) + for i, inputName := range v.Inputs { + v.Inputs[i] = addPrefix(pipeline.GetNamespace(), pipeline.GetName(), inputName) + } + config.Transforms[v.Name] = v + } + for k, v := range p.Sinks { + v.Name = addPrefix(pipeline.GetNamespace(), pipeline.GetName(), k) + for i, inputName := range v.Inputs { + v.Inputs[i] = addPrefix(pipeline.GetNamespace(), pipeline.GetName(), inputName) + } + config.Sinks[v.Name] = v + } + } + + // Add exporter pipeline + if vaCtrl.Vector.Spec.Agent.InternalMetrics && !isExporterSinkExists(config.Sinks) { + config.Sources[DefaultInternalMetricsSourceName] = defaultInternalMetricsSource + config.Sinks[DefaultInternalMetricsSinkName] = defaultInternalMetricsSink + } + + if len(config.Sources) == 0 && len(config.Sinks) == 0 { + config.PipelineConfig = defaultPipelineConfig + } + + return config, nil +} + +func UnmarshalJson(spec vectorv1alpha1.VectorPipelineSpec, p *PipelineConfig) error { + b, err := json.Marshal(spec) + if err != nil { + return err + } + pipeline_ := &pipelineConfig_{} + if err := json.Unmarshal(b, pipeline_); err != nil { + return err + } + if err := mapstructure.Decode(pipeline_.Sources, &p.Sources); err != nil { + return err + } + if err := mapstructure.Decode(pipeline_.Transforms, &p.Transforms); err != nil { + return err + } + if err := mapstructure.Decode(pipeline_.Sinks, &p.Sinks); err != nil { + return err + } + return nil +} + +func isExporterSinkExists(sinks map[string]*Sink) bool { + for _, sink := range sinks { + if sink.Type == InternalMetricsSinkType { + return true + } + } + return false +} diff --git a/controllers/factory/config/configcheck/configcheck.go b/pkg/config/configcheck/configcheck.go similarity index 99% rename from controllers/factory/config/configcheck/configcheck.go rename to pkg/config/configcheck/configcheck.go index 7c9b2325..ee7a48ee 100644 --- a/controllers/factory/config/configcheck/configcheck.go +++ b/pkg/config/configcheck/configcheck.go @@ -22,7 +22,7 @@ import ( "math/rand" "time" - "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" + "github.com/kaasops/vector-operator/pkg/utils/k8s" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" diff --git a/controllers/factory/config/configcheck/configcheck_config.go b/pkg/config/configcheck/configcheck_config.go similarity index 94% rename from controllers/factory/config/configcheck/configcheck_config.go rename to pkg/config/configcheck/configcheck_config.go index 5f6d858a..a36f4990 100644 --- a/controllers/factory/config/configcheck/configcheck_config.go +++ b/pkg/config/configcheck/configcheck_config.go @@ -19,7 +19,7 @@ package configcheck import ( "context" - "github.com/kaasops/vector-operator/controllers/factory/utils/compression" + "github.com/kaasops/vector-operator/pkg/utils/compression" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/log" diff --git a/controllers/factory/config/configcheck/configcheck_error.go b/pkg/config/configcheck/configcheck_error.go similarity index 100% rename from controllers/factory/config/configcheck/configcheck_error.go rename to pkg/config/configcheck/configcheck_error.go diff --git a/controllers/factory/config/configcheck/configcheck_pod.go b/pkg/config/configcheck/configcheck_pod.go similarity index 100% rename from controllers/factory/config/configcheck/configcheck_pod.go rename to pkg/config/configcheck/configcheck_pod.go diff --git a/controllers/factory/config/configcheck/configcheck_rbac.go b/pkg/config/configcheck/configcheck_rbac.go similarity index 100% rename from controllers/factory/config/configcheck/configcheck_rbac.go rename to pkg/config/configcheck/configcheck_rbac.go diff --git a/controllers/factory/config/configcheck/const.go b/pkg/config/configcheck/const.go similarity index 100% rename from controllers/factory/config/configcheck/const.go rename to pkg/config/configcheck/const.go diff --git a/pkg/config/default.go b/pkg/config/default.go new file mode 100644 index 00000000..993a099e --- /dev/null +++ b/pkg/config/default.go @@ -0,0 +1,49 @@ +package config + +const ( + // types + KubernetesSourceType = "kubernetes_logs" + BlackholeSinkType = "blackhole" + InternalMetricsSourceType = "internal_metrics" + InternalMetricsSinkType = "prometheus_exporter" + + // default names + DefaultSourceName = "defaultSource" + DefaultSinkName = "defaultSink" + DefaultInternalMetricsSourceName = "internalMetricsSource" + DefaultInternalMetricsSinkName = "internalMetricsSink" +) + +var ( + defaultSource = &Source{ + Name: DefaultSourceName, + Type: KubernetesSourceType, + } + defaultSink = &Sink{ + Name: DefaultSinkName, + Type: BlackholeSinkType, + Inputs: []string{DefaultSourceName}, + Options: map[string]interface{}{ + "rate": 100, + "print_interval_secs": 60, + }, + } + defaultPipelineConfig = PipelineConfig{ + Sources: map[string]*Source{ + DefaultSourceName: defaultSource, + }, + Sinks: map[string]*Sink{ + DefaultSourceName: defaultSink, + }, + } + + defaultInternalMetricsSource = &Source{ + Name: DefaultInternalMetricsSourceName, + Type: InternalMetricsSourceType, + } + defaultInternalMetricsSink = &Sink{ + Name: DefaultInternalMetricsSinkName, + Type: InternalMetricsSinkType, + Inputs: []string{DefaultInternalMetricsSourceName}, + } +) diff --git a/pkg/config/types.go b/pkg/config/types.go new file mode 100644 index 00000000..4116e725 --- /dev/null +++ b/pkg/config/types.go @@ -0,0 +1,66 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package config + +type VectorConfig struct { + DataDir string `yaml:"data_dir"` + Api *ApiSpec `yaml:"api"` + PipelineConfig `yaml:",inline"` +} + +type PipelineConfig struct { + Sources map[string]*Source `yaml:"sources"` + Transforms map[string]*Transform `yaml:"transforms"` + Sinks map[string]*Sink `yaml:"sinks"` +} + +type ApiSpec struct { + Address string `yaml:"address,omitempty"` + Enabled bool `yaml:"enabled,omitempty"` + Playground bool `yaml:"playground,omitempty"` +} + +type Source struct { + Name string `yaml:"-"` + Type string `mapstructure:"type" yaml:"type"` + ExtraNamespaceLabelSelector string `mapstructure:"extra_namespace_label_selector" yaml:"extra_namespace_label_selector,omitempty"` + ExtraLabelSelector string `mapstructure:"extra_label_selector" yaml:"extra_label_selector,omitempty"` + ExtraFieldSelector string `mapstructure:"extra_field_selector" yaml:"extra_field_selector,omitempty"` + UseApiServerCache bool `mapstructure:"use_apiserver_cache" yaml:"use_apiserver_cache,omitempty"` + Options map[string]interface{} `mapstructure:",remain" yaml:",inline,omitempty"` +} + +type Transform struct { + Name string `yaml:"-"` + Type string `mapstructure:"type" yaml:"type"` + Inputs []string `mapstructure:"inputs" yaml:"inputs"` + Options map[string]interface{} `mapstructure:",remain" yaml:",inline,omitempty"` +} + +type Sink struct { + Name string `yaml:"-"` + Type string `mapstructure:"type" yaml:"type"` + Inputs []string `mapstructure:"inputs" yaml:"inputs"` + Options map[string]interface{} `mapstructure:",remain" yaml:",inline,omitempty"` +} + +// Used to unmarshal pipeline config from kubernetes object and pass to mapstructure. +type pipelineConfig_ struct { + Sources map[string]interface{} + Transforms map[string]interface{} + Sinks map[string]interface{} +} diff --git a/pkg/config/utils.go b/pkg/config/utils.go new file mode 100644 index 00000000..d39db2dc --- /dev/null +++ b/pkg/config/utils.go @@ -0,0 +1,12 @@ +package config + +func addPrefix(Namespace, Name, componentName string) string { + return generateName(Namespace, Name) + "-" + componentName +} + +func generateName(Namespace, Name string) string { + if Namespace != "" { + return Namespace + "-" + Name + } + return Name +} diff --git a/controllers/factory/pipeline/hash.go b/pkg/pipeline/hash.go similarity index 94% rename from controllers/factory/pipeline/hash.go rename to pkg/pipeline/hash.go index 2be62ee8..da31df8e 100644 --- a/controllers/factory/pipeline/hash.go +++ b/pkg/pipeline/hash.go @@ -19,7 +19,7 @@ package pipeline import ( "encoding/json" - "github.com/kaasops/vector-operator/controllers/factory/utils/hash" + "github.com/kaasops/vector-operator/pkg/utils/hash" ) func GetSpecHash(pipeline Pipeline) (*uint32, error) { diff --git a/controllers/factory/pipeline/pipeline.go b/pkg/pipeline/pipeline.go similarity index 98% rename from controllers/factory/pipeline/pipeline.go rename to pkg/pipeline/pipeline.go index 7e54222a..39c02c0c 100644 --- a/controllers/factory/pipeline/pipeline.go +++ b/pkg/pipeline/pipeline.go @@ -24,10 +24,8 @@ import ( ) type Pipeline interface { + client.Object GetSpec() vectorv1alpha1.VectorPipelineSpec - GetName() string - GetNamespace() string - Type() string SetConfigCheck(bool) SetReason(*string) GetLastAppliedPipeline() *uint32 diff --git a/controllers/factory/utils/compression/compression.go b/pkg/utils/compression/compression.go similarity index 100% rename from controllers/factory/utils/compression/compression.go rename to pkg/utils/compression/compression.go diff --git a/controllers/factory/utils/hash/hash.go b/pkg/utils/hash/hash.go similarity index 100% rename from controllers/factory/utils/hash/hash.go rename to pkg/utils/hash/hash.go diff --git a/controllers/factory/utils/hash/hash_test.go b/pkg/utils/hash/hash_test.go similarity index 94% rename from controllers/factory/utils/hash/hash_test.go rename to pkg/utils/hash/hash_test.go index d3c8196d..0300419e 100644 --- a/controllers/factory/utils/hash/hash_test.go +++ b/pkg/utils/hash/hash_test.go @@ -19,7 +19,7 @@ package hash_test import ( "testing" - "github.com/kaasops/vector-operator/controllers/factory/utils/hash" + "github.com/kaasops/vector-operator/pkg/utils/hash" "github.com/stretchr/testify/require" ) diff --git a/controllers/factory/utils/k8s/k8s.go b/pkg/utils/k8s/k8s.go similarity index 100% rename from controllers/factory/utils/k8s/k8s.go rename to pkg/utils/k8s/k8s.go diff --git a/controllers/factory/utils/k8s/k8s_test.go b/pkg/utils/k8s/k8s_test.go similarity index 99% rename from controllers/factory/utils/k8s/k8s_test.go rename to pkg/utils/k8s/k8s_test.go index c2c71975..eb67d392 100644 --- a/controllers/factory/utils/k8s/k8s_test.go +++ b/pkg/utils/k8s/k8s_test.go @@ -23,7 +23,7 @@ import ( // . "github.com/onsi/ginkgo/v2" // . "github.com/onsi/gomega" - "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" + "github.com/kaasops/vector-operator/pkg/utils/k8s" "github.com/stretchr/testify/require" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" diff --git a/controllers/factory/utils/k8s/label.go b/pkg/utils/k8s/label.go similarity index 100% rename from controllers/factory/utils/k8s/label.go rename to pkg/utils/k8s/label.go diff --git a/controllers/factory/vector/vectoragent/vectoragent.go b/pkg/vector/vectoragent/vectoragent.go similarity index 96% rename from controllers/factory/vector/vectoragent/vectoragent.go rename to pkg/vector/vectoragent/vectoragent.go index 81f88b18..36c57433 100644 --- a/controllers/factory/vector/vectoragent/vectoragent.go +++ b/pkg/vector/vectoragent/vectoragent.go @@ -20,7 +20,7 @@ import ( "context" vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" - "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" + "github.com/kaasops/vector-operator/pkg/utils/k8s" "k8s.io/client-go/kubernetes" "sigs.k8s.io/controller-runtime/pkg/client" ) diff --git a/controllers/factory/vector/vectoragent/vectoragent_config.go b/pkg/vector/vectoragent/vectoragent_config.go similarity index 93% rename from controllers/factory/vector/vectoragent/vectoragent_config.go rename to pkg/vector/vectoragent/vectoragent_config.go index 5aaa0ff4..9f8811bb 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_config.go +++ b/pkg/vector/vectoragent/vectoragent_config.go @@ -19,7 +19,7 @@ package vectoragent import ( "context" - "github.com/kaasops/vector-operator/controllers/factory/utils/compression" + "github.com/kaasops/vector-operator/pkg/utils/compression" corev1 "k8s.io/api/core/v1" "sigs.k8s.io/controller-runtime/pkg/log" ) diff --git a/controllers/factory/vector/vectoragent/vectoragent_controller.go b/pkg/vector/vectoragent/vectoragent_controller.go similarity index 98% rename from controllers/factory/vector/vectoragent/vectoragent_controller.go rename to pkg/vector/vectoragent/vectoragent_controller.go index 814d3e11..b80b4abd 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_controller.go +++ b/pkg/vector/vectoragent/vectoragent_controller.go @@ -19,7 +19,7 @@ package vectoragent import ( "context" - "github.com/kaasops/vector-operator/controllers/factory/utils/k8s" + "github.com/kaasops/vector-operator/pkg/utils/k8s" monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/utils/pointer" diff --git a/controllers/factory/vector/vectoragent/vectoragent_daemonset.go b/pkg/vector/vectoragent/vectoragent_daemonset.go similarity index 99% rename from controllers/factory/vector/vectoragent/vectoragent_daemonset.go rename to pkg/vector/vectoragent/vectoragent_daemonset.go index 1df0ad9d..3fa54370 100644 --- a/controllers/factory/vector/vectoragent/vectoragent_daemonset.go +++ b/pkg/vector/vectoragent/vectoragent_daemonset.go @@ -204,7 +204,7 @@ func (ctrl *Controller) VectorAgentContainer() *corev1.Container { return &corev1.Container{ Name: ctrl.getNameVectorAgent(), Image: ctrl.Vector.Spec.Agent.Image, - Args: []string{"--config-dir", "/etc/vector", "--watch-config"}, + Args: []string{"--config-dir", "/etc/vector", "--watch-config", "--require-healthy", "false"}, // Command: []string{"/bin/sleep", "1000000000000000"}, Env: ctrl.generateVectorAgentEnvs(), Ports: []corev1.ContainerPort{ diff --git a/controllers/factory/vector/vectoragent/vectoragent_default.go b/pkg/vector/vectoragent/vectoragent_default.go similarity index 100% rename from controllers/factory/vector/vectoragent/vectoragent_default.go rename to pkg/vector/vectoragent/vectoragent_default.go diff --git a/controllers/factory/vector/vectoragent/vectoragent_podmonitor.go b/pkg/vector/vectoragent/vectoragent_podmonitor.go similarity index 100% rename from controllers/factory/vector/vectoragent/vectoragent_podmonitor.go rename to pkg/vector/vectoragent/vectoragent_podmonitor.go diff --git a/controllers/factory/vector/vectoragent/vectoragent_rbac.go b/pkg/vector/vectoragent/vectoragent_rbac.go similarity index 100% rename from controllers/factory/vector/vectoragent/vectoragent_rbac.go rename to pkg/vector/vectoragent/vectoragent_rbac.go diff --git a/controllers/factory/vector/vectoragent/vectoragent_service.go b/pkg/vector/vectoragent/vectoragent_service.go similarity index 100% rename from controllers/factory/vector/vectoragent/vectoragent_service.go rename to pkg/vector/vectoragent/vectoragent_service.go From 6263cf823f2ab18f224d9c23833b968ecb6d3787 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Tue, 5 Dec 2023 11:10:19 +0200 Subject: [PATCH 247/316] update golang version --- Dockerfile | 3 ++- Makefile | 2 +- go.mod | 2 +- go.sum | 2 ++ 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 352a7402..31dcc569 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Build the manager binary -FROM golang:1.18 as builder +FROM golang:1.21 as builder WORKDIR /workspace # Copy the Go Modules manifests @@ -13,6 +13,7 @@ RUN go mod download COPY main.go main.go COPY api/ api/ COPY controllers/ controllers/ +COPY pkg/ pkg/ # Build RUN CGO_ENABLED=0 GOOS=linux GOARCH=$TARGETARCH go build -a -o manager main.go diff --git a/Makefile b/Makefile index 0c39cffc..e1eba2b9 100644 --- a/Makefile +++ b/Makefile @@ -48,7 +48,7 @@ ifeq ($(USE_IMAGE_DIGESTS), true) endif # Image URL to use all building/pushing image targets -IMG ?= docker.io/kaasops/vector-operator:test6 +IMG ?= controller:latest # ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. ENVTEST_K8S_VERSION = 1.24.2 diff --git a/go.mod b/go.mod index f7027fd7..79038422 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/kaasops/vector-operator -go 1.18 +go 1.21 require ( github.com/go-logr/logr v1.2.3 diff --git a/go.sum b/go.sum index bb6b8abe..e9375202 100644 --- a/go.sum +++ b/go.sum @@ -280,6 +280,7 @@ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108 github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.1.4 h1:GNapqRSid3zijZ9H77KrgVG4/8KqiyRsxcSxe+7ApXY= +github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= @@ -356,6 +357,7 @@ go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= +go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= From 28b8f05f52e305333165ace9a84eed3ee8c034c6 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Tue, 5 Dec 2023 11:16:02 +0200 Subject: [PATCH 248/316] update helm release --- CHANGELOG.md | 3 + helm/charts/vector-operator/Chart.yaml | 4 +- .../observability.kaasops.io_vectors.yaml | 9 +-- helm/index.yaml | 71 +++++++++++------- helm/packages/vector-operator-0.0.33.tgz | Bin 0 -> 32968 bytes 5 files changed, 50 insertions(+), 37 deletions(-) create mode 100644 helm/packages/vector-operator-0.0.33.tgz diff --git a/CHANGELOG.md b/CHANGELOG.md index 164c568a..c0d0b124 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## v0.0.33 +- [[134]](https://github.com/kaasops/vector-operator/pull/134) **Feature** Add use_apiserver_cache option and config build refactoring + ## v0.0.32 - [[128]](https://github.com/kaasops/vector-operator/pull/128) **Feature** Add probes attribute to vector-agent diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index b46cea4a..cdae7586 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.32 +version: 0.0.33 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.0.32" +appVersion: "v0.0.33" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml index 5d45ae4f..b244fdf5 100644 --- a/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml +++ b/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml @@ -4443,12 +4443,9 @@ spec: type: object type: array type: object - mergeKubernetesSources: - description: Merge kubernetes sources and move selectors processing - to transforms. - type: boolean - mergeSinks: - description: Merge kubernetes sink with equal options. + use_apiserver_cache: + description: Determines if requests to the kube-apiserver can be served + by a cache. type: boolean type: object status: diff --git a/helm/index.yaml b/helm/index.yaml index c9f3ee35..edec627b 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.0.33 + created: "2023-12-05T11:14:30.122178062+02:00" + description: A Helm chart to install Vector Operator + digest: 3011e7f46cd3788be38c607d67af27ae45940809a1eec15120041a64a973a576 + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.33.tgz + version: 0.0.33 - apiVersion: v2 appVersion: v0.0.32 - created: "2023-11-20T11:28:07.27529149+02:00" + created: "2023-12-05T11:14:30.12122064+02:00" description: A Helm chart to install Vector Operator digest: 26323037ec47f1703ea930a99ab4ec8fb93b44975ce969514ea68d4130017015 home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.0.32 - apiVersion: v2 appVersion: v0.0.31 - created: "2023-11-20T11:28:07.274406503+02:00" + created: "2023-12-05T11:14:30.119974818+02:00" description: A Helm chart to install Vector Operator digest: 45b924c07a825e0f7cd3fb534a6ffd16604790d13be1aff59150c045474754e3 home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.0.31 - apiVersion: v2 appVersion: v0.0.30 - created: "2023-11-20T11:28:07.273000231+02:00" + created: "2023-12-05T11:14:30.119125806+02:00" description: A Helm chart to install Vector Operator digest: 03beda549d15f50325028ea29af5f2065ac0b8adf3078bf7dc1312981aa5e7db home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.0.30 - apiVersion: v2 appVersion: v0.0.29 - created: "2023-11-20T11:28:07.272108201+02:00" + created: "2023-12-05T11:14:30.118244242+02:00" description: A Helm chart to install Vector Operator digest: 0f025fc3a924b37b8c4131c4d8cfa437d2d4e557ab9476ed3e69a00232c7dca6 home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.0.29 - apiVersion: v2 appVersion: v0.0.28 - created: "2023-11-20T11:28:07.271169582+02:00" + created: "2023-12-05T11:14:30.117374178+02:00" description: A Helm chart to install Vector Operator digest: af856d41314313e04f15e7143409a9c564c6ca610b0d2eaec3112add8573e668 home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.0.28 - apiVersion: v2 appVersion: v0.0.27 - created: "2023-11-20T11:28:07.269812986+02:00" + created: "2023-12-05T11:14:30.115996907+02:00" description: A Helm chart to install Vector Operator digest: 631e2ff02bbd7f247cb486494fd2af60c57cc551066a6a3858226551bc1745a4 home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.0.27 - apiVersion: v2 appVersion: v0.0.26 - created: "2023-11-20T11:28:07.268711612+02:00" + created: "2023-12-05T11:14:30.115125439+02:00" description: A Helm chart to install Vector Operator digest: 760a2833f4c1a33466982419b079ff18d996331ebacc40cf93b0f55229cdb7db home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.0.26 - apiVersion: v2 appVersion: v0.0.25 - created: "2023-11-20T11:28:07.267738585+02:00" + created: "2023-12-05T11:14:30.114265154+02:00" description: A Helm chart to install Vector Operator digest: fd22b996b071b6d85740ccf76e85cb640fa717c2620748d206d3f4fdd44cbcc2 home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.0.25 - apiVersion: v2 appVersion: v0.0.24 - created: "2023-11-20T11:28:07.266829797+02:00" + created: "2023-12-05T11:14:30.113348624+02:00" description: A Helm chart to install Vector Operator digest: ea257e60ecde063a0d1ed52ce5e3283245b8f0e2daba58ea3a5adb0ba82d7799 home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.0.24 - apiVersion: v2 appVersion: v0.0.23 - created: "2023-11-20T11:28:07.265485653+02:00" + created: "2023-12-05T11:14:30.111937287+02:00" description: A Helm chart to install Vector Operator digest: 546d202b3b9263f789b88335263191098dfcabd5d8698105f37cad24d56a8ed0 home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.0.23 - apiVersion: v2 appVersion: v0.0.22 - created: "2023-11-20T11:28:07.264300127+02:00" + created: "2023-12-05T11:14:30.111030457+02:00" description: A Helm chart to install Vector Operator digest: bf96ddc8ac61e9d6beb8bc763fbf3fa6025d950b29d70d80de6e8a0ea45e0411 home: https://github.com/kaasops/vector-operator @@ -146,7 +159,7 @@ entries: version: 0.0.22 - apiVersion: v2 appVersion: v0.0.21 - created: "2023-11-20T11:28:07.263238533+02:00" + created: "2023-12-05T11:14:30.110100863+02:00" description: A Helm chart to install Vector Operator digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d home: https://github.com/kaasops/vector-operator @@ -159,7 +172,7 @@ entries: version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2023-11-20T11:28:07.262189691+02:00" + created: "2023-12-05T11:14:30.108584051+02:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -172,7 +185,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2023-11-20T11:28:07.26127816+02:00" + created: "2023-12-05T11:14:30.107627396+02:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -185,7 +198,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2023-11-20T11:28:07.260589367+02:00" + created: "2023-12-05T11:14:30.106946517+02:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -198,7 +211,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2023-11-20T11:28:07.259937731+02:00" + created: "2023-12-05T11:14:30.106408933+02:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -211,7 +224,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2023-11-20T11:28:07.259366886+02:00" + created: "2023-12-05T11:14:30.105865068+02:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -224,7 +237,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2023-11-20T11:28:07.258741078+02:00" + created: "2023-12-05T11:14:30.105322076+02:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -237,7 +250,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2023-11-20T11:28:07.257702279+02:00" + created: "2023-12-05T11:14:30.104367482+02:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -250,7 +263,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2023-11-20T11:28:07.256851504+02:00" + created: "2023-12-05T11:14:30.103785961+02:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -263,7 +276,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2023-11-20T11:28:07.256276081+02:00" + created: "2023-12-05T11:14:30.103245761+02:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -276,7 +289,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2023-11-20T11:28:07.255655831+02:00" + created: "2023-12-05T11:14:30.102696603+02:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -289,7 +302,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2023-11-20T11:28:07.255123529+02:00" + created: "2023-12-05T11:14:30.102200388+02:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -302,7 +315,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2023-11-20T11:28:07.277344543+02:00" + created: "2023-12-05T11:14:30.124041302+02:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -315,7 +328,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2023-11-20T11:28:07.276828286+02:00" + created: "2023-12-05T11:14:30.123558317+02:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -328,7 +341,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2023-11-20T11:28:07.276348629+02:00" + created: "2023-12-05T11:14:30.123103588+02:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -341,7 +354,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2023-11-20T11:28:07.275787866+02:00" + created: "2023-12-05T11:14:30.122643716+02:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -354,7 +367,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2023-11-20T11:28:07.254493609+02:00" + created: "2023-12-05T11:14:30.101668331+02:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -365,4 +378,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2023-11-20T11:28:07.24712405+02:00" +generated: "2023-12-05T11:14:30.100874313+02:00" diff --git a/helm/packages/vector-operator-0.0.33.tgz b/helm/packages/vector-operator-0.0.33.tgz new file mode 100644 index 0000000000000000000000000000000000000000..e946ccfe340bc77515875cdc5b0f5121b15f8bc6 GIT binary patch literal 32968 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

dO*N;=T&x z^g%v>yt@O14vDzA0Wt`>1*Z$Hczi+S`w9b$tkGyb?HvX>W09X@5wEGCceWsgATrbR z_-iX&wd;Bjm*{`FVdbr*-1P~nU7HK4A$0tmO%ANNTnVV@DgoEwJRR?u0sBw9rN)^e?Q!F3!<~Cpf%Op7IJu9>{o(1>jaV^C$IqhpPC}?jAxY;IF^0)4uWS}U+~tvKY*bO z{J*VVW$^=x{s-ZHAQCN-tG(vZ7gG^r$Z1CT>h@8C0TN}E<5170l(%yeY z5IA;@Z))oXPG={2;L;R0e!`OZscQ0LGu9Nqg)tZY76DmT06>AREBvoGjWHYYQ&AlV zY(tp`|JI}j{-ZdC|JMbazEb(W$(_P*I?AJg)NxmU{cqycH2<4?Jf|a5*pB;aAA&e{ z?w?+M8gzgg>u!KB*$EtPp`@?efo9EJ*Rxi$Vs}qT7*Bz={#f-`buvr*I~Q9%e9A&2 z?3~lbfSF2Ge7@cuAySr9CR8&D3fL(jqXv#}cQQF3x`ev0tlg*_rv!UkF{r`BBD>+o zC>aiUC{rsRRtk>z2XnasZh5i;X}8}U#2h72P!8D-(7?;z5p)tyV*!S&XS}e!1$r@1 zk;DbaNegfUY(}6Y9?J&vu{vxD_9XC!DnqS60Dx>iEke=%H!L$>riRCpdqiDi{bQN zHG^)^8^?U%i8dfn`LLky;OEXV;I`h?E4y`O;O+#!#aI;y*+@3$g<@k$G=xa0ZPpj4 zxKuK?A}Nz0LDc)g%*2hHYMS@9oRaPua%YJd7|8? z{V(so@@X7~@$AyGIn$|l|LkKZt+0*mmp8w4`ipjTMt1V9g;C-8s>wDPq+wP*y#4g7 zKov6!R$0wGyxZ3%>^&U(mf81)Tk5TuIX>9B>ZzGIDi{lY=_?W%3t~i!gNuLO8*Nvx z;B5;~TOW2&*UFfTy0r>wNijC(W zgtHQWP_$|`Ob#FW%$^;e{mjZDb)R*TdFGv)cTRO`W_eI^*?5V1A6%B@&N|~err9A@ z12noL3Xk)h6x|7#+7QBUJZUS9@m&b*b`M5_n{Tv6mm}??U3C5u2dtm5aQ ztCRIJDDYF0HVBTSY9728c@FSGIW@g%9q0y6jQ38Eshtf|t(Qvzf@>-N)~h~~Z8Fy@ zTJDxeeamk8BSM2AZu~z9WE?*{_RSqdFNyQ|Cg`p?LfZM&Wd zY%PJsY=Iu8%z7*g!1{)CApRH; zY`rSn{40xSVXP{+l3zK7P;IJ@N4GIH7tO@gnmdM2YZ`{L0cy3~aH+d!v8iYG(qDyq zDMe`C1|8+wWUdF<>4BNx^SXC|Jh06q%=)<9gC$gd*@IEQhw$ly5pS_6y0$)|#{OJ= zo=?O%WGm>0b}0yWA`QKh`r^?2d&fD>M-b8vGo~Rm z*~$1#HlWBxgA+!H6MywJgf>1WP!Vw4{*2G+l-MKfiAfIT{0$BadroH4rqbew`s*7m z?41c;&^DnI+TxzfgJ+3Rn<}oY{BFOhv0fo#C~5K`su@-BxX(nUfboQ^UoFV@HK`lwEP{z@}OTfDRe5|Us%WX zV!vsY=sWts5TUTk?6RedANGntr>t?0tX_{T7w_r2j$)%9Soo+@_G3Pu2zuV9@*UXj z7wlrg8<_!KK*MKN8-e>xU$?mKY_lGOpS5VXrGtSnx2F9l7hNZ;{I3u}+&m|he7CX% zsx8kMzSh=Rf8^hOZ4(^cCKUP~Yj1h*9uigC3N)>7_h|5Y@*I>*WLj4v)VfXeb^{Op ziG8iG^8{@GmNT{axauFXzC7FBp7^o(KHOT_pCw-0TDW+kF~d7)tyM>j(8FhgZ%0E5DvTQxv-d=7%*1=6I0S~UPeI5qjZ>`#K-{;Fbz zARvM5qX892d?{(4I0fhn+BfM7nz#PXzN*2Xg`!kPviz-%6aa3ZZuetdJ%sBEo?cnk z5&bZ)-fVD##`oZEhqFO6dNrAMvHMC2kp7YgH}uGDOucK_24)0r@n3=Sud}#n*RqZa zuMhre_YiRIr@@VaUG2VA?!T)>0Yq-W0JsQo=b^OPp=eLk>qvUL5FGbJZB(soWZOg8 z2AUc#o4$Ae*OK9Qw!{9m3UQ7XB;!7){B+&yu7SfIBFY6fkV39x2XC3r7g!C^1-BTu zK@}7|N3^1<1nAsWY|Rak#^^TU>HEdp6Yc~C(KFzR==tK}MzB6+=q9%Drup_yTr1{u z4~1;>5TX6E4J^k;cP}Ex#}kJA^TludTJXze)*sQV*{6G91MK+&S$`nxsui&3RH#6o zt|Ko_-3rL_|Ebgcy?Y6n_2t*tJO96(y4<9HVbQFAac0xMjB3prjbY9EKfnhTP(Au& zUiSp1CI4R*tM=0^uo?gEr6)hC{WIvaPv4rW67>JAYh5qgy=4E(?sK94K@ezzFdRs! ze~&1%`9f5YnEr|U!Yg4Z<6}4EjTk}xtCw)U2Xgq=|Kv0<4?+kC6Ql!kT>(|DX7w)m>eFshGF-F5eOYn~484yZ8TX4qy*&ySXFH*vHo3KR2bU zd;g7-pC;}~wh~Js2qdHjg!1O;oilCr<-*D(8nq&1A-z?H1cm@lhr&=Pdj|Jgg6=)Q zfLFBxj`#uZH2SL>y-Y%PzK-L>zL1QixIv9FOFBKgF)>j}0bAJzN}fMfMSSGR3a6 zhAP?$HA(dKz>>IRoD&$ax|SLAwn`Pb2rylf@cCt1IT2Znn zdGCYIzlL1M@SXra_h9pQHAGtgTJHPK-z;_u|A-Iq@;3iV?Ro1ZmEOzFHv1gZhv%wc zt9;rw7oYP@|3u7@qkOoiy3W{AnC0%Qq7CsCVs~w(?6KV&8SlFK3*y@5w59p0)_OD> z$kO?s!Wm0398g12lTk7QCRMdONW??8Q+ zaXI4L7HURuynEQIEG1nMGdEhDna==hn+7NZhhHbG_(XE znUluL^38nAX_Ke+SD=cE9yQX^*ogR&tf~Xj%kkU8e1tR+7j|YW(f#W3G}Gt;Z98J= z?4-%KV*?Qbu{`ru4MhRJodV;Zg z5o(aleYF*gW%}(*uDgvGAv{P3i#cQuB(qWAm(|39z_wn*uPz#pKKqaiz;;?}wo`Rs z4;>sp`N}NnFH<0MwftF(+#`^?bNAe|pvc5BJJ}w`ueT+S$gIK{SuXZ3v0Ayn71^!3 z6_xsR>IF?@t=~#@5A3%7b0*q26G}IBe zvZbVCw|v$2J=8dwUX~uf{$r<=KdaZx`w60ZFJUoGrzru`z!W*f!(ij48r^1cIBgGY zG2C<9(1(76&Luy^f7iJYX%9}`Tl(pUbo<; z(}Q{$!^=XfTJHXZbF2gXu3WctfF)GLOnWl02P#$HMq2+}J|@a273QP1*6nJEQ35tW zLO~SwcmatT@Z${;Ib+IUmo`FtlqS#V97HY*KKO}Z*|w+Cn-4N?EI*NZ+m?tKxiDH0 zhGxT}jMWEUtQT5HXd7j%6+7WmL3Aemz^mxeKiSXB!sK-wix7C6T7uPQ4mKqV8uKc5 zpp+#t;|#S`^or_HrpRx8N#50F5In@{9+3`0?cYiuHOclsYP*mu|Kofn&nu!v#Wp!@ z(zRJ}yy#kVi;wJFd~Rfo$e-&Uiw(^-hdaHv_W%!3?S2$()yCzvYrv!l6=Ezf8m$Ud zET<4U8g|zcOkdEBpRW{Fy+lmfu4%{+$u^@f zBYEa255U%RRq~+hn3+A7T+7+;rjUr;@d{q^ZdEo_!)#rwv5C>On@W&^_@u%6$u$Y{%kP_}!CZ!J#Kv{p$~u=bhBV)-iK-WWCw6A*m4X zUlzbm06TgOuxYms&AP|8|9%pYlw9z-Q>-zOFBv7xnrx4o78SY*w&d4@#TYrix=nWo z=4zoZfeLi!%UB_LYZ$lu9h9&@l(jMYeu6fz$Qm)v0Hs^SG zwe^J`=sif}9e4MaXzAvX_R6mJ{5oqxv6rGh@BU1u?^Y_q9@?DNr8)oeUnA3D25uKBVJ!6)DKj_@$?%$R}(T9Gxf=^G2UGh zMin!P(=WNn5rXpV&N_5T>AZ}nBPW&WlhS>F@b0_ROCJTbvCh=)N?8{RLOg^lnl8Ge<`2-B~+z! zYeY1ii#?t)EvW6K!f$*uhlHs+iOQk~aNwB+l;JGl0l#&L^OoaDGR`Xakp@}r2K^_v z)Ee0V(IdlZ&r$8xi)zXET89!(>XWWnmu)U8-iWL)5{IWGCXxHBlV7T%8E)_pW2~{n za8R@Ci@gYzn!#3<{R4cXu)K&uSBBXW`9=NjC9^W0ND0(w*mvLT$JWVSx_ubcvYIqxxbze4#;I(XrNx+zOSXMfd{-*#&q?}t`mrrK@mba!=h zoo#6AYE7hXE=Bcv+nr!FBrH8+oIVV`oUrqEar%0@7m+6L8K(^+hylMHu6HFRf*%D- zq!d?duyV%$Q7syp;y`(ZgNLCL2c3lIP)J#FR`Pnyd==|!?dR)T2kN_W=+}? zP}IO?%TK)8O-)Sg$D_QZ-l;ZieEZ0A+{Fn$GM(_oi(PGkz=N6yk9WA7_}}*Lwo$eg z&+jeibk>cf#+AXi4SmsB&>w=^z<5PB+Kt_Za3EW1hND4;Tc@;RsL<1LrpoYwiEg8^ z`-m3Im$^W=PDA3_BGF}C#GGC(3w|{)oCqVx_3gn9fBpLkN3o#Fj85WWnl}vK0N(6R zoZ0my_!2(d+Q76U#?qodE5~R5jgr{GB*syA!#cUnsIK^>Zwre9C?~uUZ54E^sWx2Q z16!(Y|08&<#nvX#u7(cC%XxjUlwU)0=U)NF$>YxhHV@?6T?U;6XAhkS_GU_Q#Z#FS zwpu)vG3MbDF8IfkBdvIjqkrvq(>XE&?D2s0RrrbIm9Sq63E2yHzV8LN+26kSwo$3d zgYqvE;lQJ;lZ?5^Y`Z-n``8wxK2Yw1C*Bh6f?yi*oCxXaqyi@0yqvEDo1ZUwcyP8t z79!#joe{d zGB)gxh&sH+qt!32?#S5g^Hzt+9Uea3CjE4-JTksM%um|Q;(J_dJC8>xo25b5)*G_Z zeOPB%7u3Y+q^N!&hROqT`QP?}GpX0iHa>C9?scr;vS0v$0^IIca zh~|%V{*Y6>V>@{XEcNI3ryHyIi)_?IYxe&1NxniX)sHEvM|g{A|f_s9^U%+ zl`P+P(T;o71_iXL{)TE;O4*1~NNyzV!~{)?>NYp`_Tu+S2q;>~+cjhlD_Hnn64$~D zOU-2-Fsyi8DN?AC+%OMqp~gW4YHX8?2dAh$`!nYXVm@eX^^|3Nhy9ZLyuX0V^37Hc zJ9F=iP7dYS*3j+&)&k`7vGvDC$?A!A)$E9m$a z5h`!&e^n7IS#{2(g4`9>3>k*`ZV;4S2{g}M+Ko?vis({$i&=wPpW{1;e-K6l#?22a zkJW2xGL1BDaH~AGC_hT+vi+l7WsWCChC#u=gOF_xUZ^{dY;M0Jy$hP$51{psu*~pI z5bV(r(EBtb8GVc2NLAj8$9T# z+z~z@G!t}Z$OgHlL^axdWWga7CL*m%lvuH1&pR3-Of()ewQfFrT?o$UrKI$zaJEK; zeG_10M>7B9=sr$1hApRyn!fPwL_+WURQ!WT>M2p*Y3vAxG(J*;!buPdd7tY9m9WGc zo?($iDN_MdQf&`6Ww|Y4;S5xIK@l6}MCqf?Sv$OdYnWvXOE|^rH1fc9`V9y>$g zJ*{u)hM^;}x-w5hD7z;f^n2NMYmv=_@Uc3GQdEj3%yk=8Zv`_%sZdyc?fUkyC{{8i z`!O^rKuV%1ib%jY=qDFwO*{zZd8(r)acD ztOZcO>TKfq6xk9`^!+kMk`Ehd)~qLQK_7agiHOj3x9f9W6kZ8Anz7ewN8|rhJ;LB8 ztoNWzqFRKSpcQWc@ld?=ZS*MSdy-0O$zdL|7L`X4VZsAAuPoJjBK*@U>kokj$B<2! z5awdTX1JoI@C85yg^yFRK8{R{ zseCW7P(m@^noaJ2G`0QRP6m@^cB5HQ{;tjcnUnbZQ1~}!X`~?Jh+GOo)2LLI%7!~` zN~P&L?mjHUQ>yDtO)qf+xhNHmNxMulEKAN@3x)Iaa9koxx=bBCxtdqj*?^*KZqY__ z`W=>zPVAiT1IRTTk7UXz0rP=Uyt!Vtxt@&?>vb;@Ugv{R5{uBSEy|Il9X^Q#hR{9Q z)Kj1fZ3^L8Gw$F!SBN1XG2n%^++DQB;<2}P2eW4uBgGL4k%OSRc3GQh>@9j<{5*E`K zatsmcbRKA5h)U~8%&qQbLA1EYc$|6?up%YC1)IBcjnbB0I@xH$GK#8IfO>8jIK8Vv zGYf;T+KJt?Z=1I-@EXcjWs_(f)Q~bl{HX1oe}~}mrdyo&0L`lYsKkW@h9&p4J|SPo zq7Zg68U?*XKnwSmUTa-7p79Cq+#`=@Z}u*G`B>5XH9F z?mnsZc#(-w7l0@fF7i1hsefiBh~n=$7r*m6mC2odcNi>uV*13ss-T1*P&G1ptDqv# zWN$)dtNndETugXVr)H%D2we_v8A#X|xg#|n?^Z{zJ6LhhVTYyC?N?ff z!`<4+9Wq$++ojQc+la=DwiU5TK)cEIjyZ_bu(yd{9*fsk@2lug9GRL14I%KEeF)9U zIGbpajiI1po3RqA#G_sf`Q^4r*WoCutIbfylh8zpuii6vbI!X+bReN6Lame{Lvtx7 z=DRT`NdS4R`T#AMQlM>7&5&|QrkKZ|S{RiwbQDS&>N1G6cE4Wfb1b+D4y?h2M8LNU zhsyjv0iZu$z=tuLCf9F9w-^`yA~~e@Ci9F5ts7t)l^V+-g{bGqt zz1Fn~d_lKJ>NSry3ad#QAH55BwcshUSmQ<&MYK`ol zYrz^PkYr}+QzmGpXf&{c`>$!7B=zD+fC7{8*c4#Y*T8cuZjjylH>OU{J53banpZEX!r^m*Uf#kqqA$k=Ygo=`uSG!z zLI@(!AtU!Xz5+!iGQbF!rn~k%(BVFsOWTbj^wc2RDn<%Wh3bu3EO>i!U}S;(ew5#j zMx#*=QSCtkZrV}CgK>#c9@FWvHRJu)N!2Zgiok4X#_B?H@AY*$K#kWs>UMV-nA3Ct zo3JmY6s_C{9giAcqo}--e5yBize?0thWZ{b+K|~-#;P?TYD1eGzdkWo2kQXJ-Mc!h zp2YxECm)23#`$%X-7?9lcIwass~fx=!eMg=;%JPnq^npbAl)%;5cHI~SLVz%I|r3o zusAdEuzjIHI@mbHnKqdKnoCS`3omTSwW>6{Y=iDF3++5^>8# zZNGznAJHiGcG|beKdI6!p^DY7uZq>L zvci#LmxdBneXHx{rbAcPX^r;C;5Sz<#;bxm zmil>{(bezMw>GY7#>-SW+{a>tR2rHEwWyH{@Yufg4jaJg|x_<-2bgHz_z6o|gA zuX!xA)2Co1JY-4fuq_7k??J^Ij!;Txa|w1Sz(l|Wi{`y@L7(et8-t^B!8_)+bgf`? zM{-6Il;Iqw1j2bdY{b5qBR*Lpos}uDeTBh8ca}Qwjh6Gu7HMJv>yViT>yuBGh%-aP zogLzp8R9<8zlI8g>Y#&|P`d{F4IzU{5jvb(zuNlWp%h^#McCCsDZ)^SFq9$;r3gbQ z!cdCvS6+&+78R@G&DL+b7F0^db7)kmRB-Q2b)muL57!KZG8AC}cO`^X>DfM3vo)n3 zNDTBFLTS%%*G^iNQx5BuU?F^m+I|LMA?S=%rkm>x!5?#iW4bGdHjB(s60KpityN?6 zbmk=G3li(1@;Zz-_ax1pv2>769B(0B~!y0N@5K0JsGJw*cT40Nhqv0B}PAz^$VN0Ji|(R&lpId3y#!;Yr06dvdP|8J zmXhS`4E>+Ozy0p#(b1m{5C3{P`k#OQ-QnNH@W&TNe>(cv{^NH?M~8=h{r2@Y@2)TZ zPLF>6D=X4F{jZ-7|4J_Z?n&h6=ue;bbZTR%uE|SY!TQw9=Mt(=e}L1EQ(kCXkcg*Q zA^YyRa_iYx%fU7c{~hgui*lAbCnD_0Pj^omFYn5(Gk`$O zdqd(MZ(5JkqC9PmaHPJ2g{#KD&OXWGvF~iz=`gPu!E3s&QO9W&(VWCZ(ic^|VzDoO zUmI}M;fmyXx0j<+k|#uO1|N~;UG-}Sac+h{7;UTiO}EtSUn~2aQISC3s@@GMhm=?i z9!ymFn;!8Nvn5gsDfOWQI7GupWKOe$px@t-<*^R;)j0-#M1|-LdWNe}Msp<_m1W_7 zZ~yw^Hr2It54{M%Pday!r%$;WzwJ$FTPG)$3loBR!a>tttGLEUk4$kN1u&E9Aav zOM79mNIveJ%R92PRto|zQkR7KeSbh{xyh`R;PL|%Qv8=b9uZF`*65>OYZtxW*s9S3 zVR={#|Ag*sJNdS=-Yf9djxHxzP6Q-MVbzxdVE{om11MQX2uEp#sUR;jVZHzKoWWQx ze=KlP>Aa35wLdom)RO&J&;?G24)nRFNgUx^iw>HR^imWi_!sb|QG{8^#;TnnOtLf1 zC5?&%_Zzy^*`M*eH;mUWpQGIFPFuv<`@x!6YO5*d)8m-0W?4(Jy;P?>Ckv9J z!OTJ2lIo*v^s}dx}heem0?4OXJhjKDg=hmFKEl;MW+`TRm zNdr#5R{D~Utar_0^N7AmK+mGA1J|XN(y^t$_rB_4BZv=P4uY2Ahwm*My`1;BMz9+g z#zz)|S6@4Vw^l>94DvxUMg+^6Sys<&{~b~8J?MqfSGDt9F$O~x9h@C(&GGdhbDrTD z93k(qwjZqLXV3Y){vK}L-hpO@g3bT3!AQn!ZKuyTvEY=U``&OngmBIa78`-J| z`~~ROdV|7)BS?Rz7OfgSy3eU3UPcdu4gnP!@w}dxd(`w!z)#%6bmcmWVzSlQHX#16 zX{)2{hjwB)a@R4h4|RVZ9qL~yQJ8bto2=lqyk^*!A2NRd{qjSGSxjPxWbK^;r+i+& zJl4;_pUE6A2qgZ|1m{WrahDw3>QSLdArWBFcjIP9IsF+L*9D}z~9iZOs!j9G1)X=n}y)Wd`%9BQ4^0-|;!>NK@oz@)J!!qy_{ zhYLwD0FCHyBt4u4CY}v3TH9kkDG1+-%LEHaqdzA+y1SJ;_rI|Yx}I)~oT2mYZ_x`X z?t1rk={>ck^|}=hmpGvZLJJQmj^>p0-!(?2V7Bf|eT9eG)`jFbo{|~AeXeD-yecGtU@RsyCON@b8l!hKJ42VX zdJ@f7x#^l7R=9vhErr$8Y8vF!n)LC@4>-*dpOl+@@rb7LX$tl#iNlVWR zWmA&pdi^KFJm93ef*rE^4dd*?ySFdio`J9(b94{LybE>f8B`u~{%b3)E@~&$h^JZ3 zQ(E45<}mfgfDXv%0i%{n*FY=#xO~iR<=O{q7n}HMYMp@1yiR92C#tmMi7g`r9KE(t z(Mz7&uTEmJI1w>^;(`&-KmvM~En8#80_{K9AER3 z8sgC}6*K_%$;wt&FNV#VeM+NA&>steu3nT8(69uY#5|VjMmLp9*W>vN{AEifuSAm9Zln;T3oW$edWR(KzQjsNdptmf_7Rjy z8M`e*?`kdXs0&xC%?+T?LYa+Fi&?mxW5uvhS|pNY&hi6hZ>h~F)a_??nNre$`=J*^ zM{2}pNXgejD0UNk4RKtt;oeAxb-CA}Iy8scWXtTM2rn>A@PsB*E|n~EH1^FI(=`BL z%67*6HWKGbX>P@Cj1JXzCt#CyBEKNu=RgP`k6Qv^`QiN5N$T9G5>ZWl|Icinw`B+s z$>vl4U?KJMm$yv|_yx)4=;iI|5qJ2M07Vh!xSzmD~lM)D|b~ zQ)(yN*!4oMYoB5i@gyOTQtmtge5xm>B->#}AjZx#zWYy5a*aBBq8H}m=JNc->&tN( ze>Ug$BgsdFAR{VWx3m`&(;ze-Kg1j#%$!y8pq#6zty18>6FTk*Y?#{T;~YxPxjaTW zhB$*VQFU4*iCzPl6LMU$45FT3`TB?-O46weSB;^s$UV-1H?AhXt7oT%u!q^eLvJ~k zOoLYDzk05@0=MW>yY1 zW%Gp+?#ca$L75)iQ#l`Lp+cNMVX>3X;C~+dvyQLxI5q)cg&@-+(IHx5jBrNZlUzX2 z1*O`%-7KmVH2#xoy3r=Lod=LyD`zgn*~r|KlBYE4eb!xkw!NfS7mPH)*Qzi zqT>4fEXeGr26w`7O4%pJZTw_rD_(6a3sV;NuSm-j4Zm*+!{#&NFSM}^y|aO>4y=o^ znuv&IJcJixN|)ltWTbmA%HnEmKFt7z(pc)g1k0+l4`(d3-d?B4)Jd~5EWs!2U?=N| z9#L}rr0pbLveTAR>@S~gCofg}-jvpW*eOpDQgc0Pk5MyOmaEh;)J>o<^fn{xR^sT+ z=?M!m%)h-1vt{ss-F_6R*O^e6GMLhBV}yhINifsLjSs( zD+nvjq|imo^qbwadXRm&@u420|8@KJO@;i`{1!B9HNEcfQ`xHicKZC;voU&MY86o; z>S+nt`snnk;hgA_fRIaSML#O6pkOf0bcyMSA0Odtn$SpUlswJWZ9QC_@oRGOjTj4q zcQf{c-VrnvQ11P>Ooh@V8f{Fwep$I!eN1y7DL!vFygxkAl6;SK1^PR(9O-?WVd~ab z&HjK;CY{gK6bd2|ql=Q2In!&AKAKt1|Eaf7nImZxH(`_wbU){U_#yWjJ?D<-YEGu4 zt^L>}xHlj;yv&OU6{?GOWLXu{@94R2HAQWr7kWPyY=ychdu;9q=rDGYG>8Zl7MV`0 zGWpepmR-eiq5Q^gvyF%>^wR1uY1MZkgh}I0aek-tXT$y7%E7gogfcZ56B zMz*Hv#KG8k>h#1aRt|{DnwO~OT%GZB)--eXaHh7X7E3hp1qaxHD$mQkYF2!6&(;u0 z--+U}W=MAxo`)Ha+2!u;p`leXkY}9WN+%CeBWJwbRL(HH?UXhQ%t1mbz;1_jS+yMt z8M+M>H9W~GGD8FoPqJ1lF=EHnw5#ZoXPmumb>XrJE!&ncN*ThF@@^#!-G<2ckA;iQq{; zvR!*f6^Z)2105t9OU~;uvCR^z-9;cJ=nKQ2(jd0#Tk=P#!gtQNAAl$K(Fg`uI()+z ze9Tp!wyA2Unaaxq<-Hq9HGIe%+K0--srxxG@fXh((pmw}ATaL8s_VJo zHZ!Y5g8Ex^1Vx8sh0QX^?utp0PjN(!YSy0Kv6F00Qj)8=H#BU%?i{l?F-g}X2gXD` zNRqp1^j_Rvp*W`tk_&YBXTTcQ$l$$>0dI2=P#%_q0FQLlwye797uu+&MAR){P&6Cv zSzxtGUgXB7YHGiD*dX#;-TX{f3P(AaDb+03W$ggKASu)bI}5kpJoTZm^(ED+C`fN5(oH>0)JeAPbmV|0=qS59U=0|L z$#pSFsF>ew_fMS?IvOFe36VWZ%jn8V`Qn!#!JZ|}6*E<2)DT&o5h)c~n>vs6+Yl|93+}IRK=CI0) zOTooS6=WLy(1AcPkBSoD9M-dn#nDC^zg2T?b)!m8cBHmU~aZE)TO=WS0L z?!3Ly&RccMCkB(P!P7kL=xW9v!2k84;<{SwZ>OjZ(NutcXl7CElA8Ww%f5lMxMN=# z(DYFHI_BV@N>RHdiIR&^(>v~Kedh5Q0wE4F#`TRZj!}XqBoW8PAi;F`dXZs|^bFmg zyrC5eJlzMwCJ)|HitfHov{C)^(^g}FD&4x^-b*!?J&1{2jS8fMR!KCt70FY9`1H6D zE4#mV;$a`LZJ=Wl*-hmFNo}@O$W${QdPBhlo9L1i17@QUn4sB)p6v%5zP}R!f#^6L zw8{e%G~G5=({$U^QN2!4?KCaiYt%*p!PDQ66)fALG$@+;?oA|(YCRCa6 zI~~+@G2Ppx?B$>aImtJuz*Cw6X~~y4?N+AJ?Kj|{u3hKX##h~NRuAwcH@q9xV?7~O zAsUy6^xns{^k}O?+6as~7>^GQtOwgittv?tayt4?L|DXQytPF_5N1s}} z{dyo4^wV#P47_qI`rXSdM7c_UE3x#<1eCDAzIQoDa#h_tUBD9#o2{IP4)urQC@cJ9 zOW=u=q m!uSl25s|;euVC-DH$;;@|-ZJwfErCVmX^WFd`7m1p|30aVXw0 zKsYj~l*g6dTiqgrEzknzJ#};2Z36)@rVA?iD?K%vM}hx(8&d4Q>$yRLY1iN@?bA?r zqL5i34cB)SF={rcE1d(xba|dylKc}l;IM6B+qnK`v)Reo^n6tpVTB%U^^XTU z7DE*W+qq9sZx|AZ^>JdXya<8=_5~e~lx?4Ara4KHW<1CF62-=N>8MPxf=*O}C)yeh z82T8~d>47yDT!&34um;^XX-O_576%hR^AU`)4sYa1A?PFtsjm*7zVrsXv^>DNm(?~@Z}l+xn{MGm=ujUT zK^Yp*?_rzRtp`L+p>-$SLa3fXna>)Hs-yR|l1{Bo_CQyXdh)iM*~;h!x%EcXcF$=6 zikIo7Q62acf>NrID`lM%m;?{QeF-zdXwp34w&X-P@dY!XnuGKEyH8^wNIsesG#-8q zKUslo3o*^_*0sT)-q-Ga6+hazca1Hp6-(MXyvY(_8|qk|aT}}fuC$Mt`#4%gZlEqn z8%1JtcWh~HD$z*&$6C*<(|AIruIs?k12coksEbKLBqYF*vWkxm8cX?t#AEcgeq-i$ zw%RG3o*{hHNQf6&w`YXJI>jy__7%M}^MZ+EG&!;nxNxMI(uk6>&JwsgbD}03Ak16^ zM+C!}m~#pCAe`Zd$|aIHj_%as;O2#H9;4{UWFEMsiYi36;$L!}^I|rKWH-GTpq#sz z?IR~!Yi=QHVh|`^GMv)LPA-I@3d)Vc58l~J7o+R4N`iJlUL=^N=ui-XF70v9-`r{? z#$_HzEkiSoOADrHz&X?>6}R^Z2{H9~)J%1D_DF3<*m9j;_fqCLge}@KM1;k>3QPqy|RSzYc&FV0Y)k#-(5reYn3mO+V(YKJtnYy^W(xmBZE=TtS4C5eZ z)MDRlXQ|tLS_@~T0&X*DjI7$pqK12qmjLN%!j0<@Q~gtG;Qlpv?OJH=3|P1GvJAD7 zk|*4u#i^|TCx3OkYJ*fYKdO)2F-rm~_O6toK0}DFGP9`m-aC?J36{e`l=i=`xj<@8 zeZ@O4i$^$7TlV_>gZY7d)1SY_CJ2FCP6*@$ImgL23?p|R$s(D$7D)4H!< z8Qlg!!+|3#2?NQ18LVi=r6vzY6vO-+`j~(XHq9}ToCB}ZeE6Ip@_`BfyV7$F%Y~)i zDp?^*bxZA_5))006UP1p7Az_;alytaN_(7Ku;QAA2`$Z|5WVx9RFFl-+#U}vkE%j5De z%(1pnWHFHT@~dKIj(0shqnroMqEe*Oo52I;>ZMsYA(_ z1uB?6DAiJV(NxO`>pdezU%1l<}v@%6cR2J+grfveXyecv|4S8oUTB3s~c<@4ssGC@j4zwjQaO=u@nvBWd< zMMKy#N`tYecoQ}GgooA?Gd2rqArG5meIqhZt8*aLc3Tg;DPEbfl2)Tq+D#5V8Iws4 zhbZSsG9Xq!Erqe>wBk&mMwa%?9wX{Mp>QRYDkCHJrEH6Z24}VbInI3gdb5GWUH!8; zr0AS2_0+y7x19G2j9CxORL7N4$?zo6;e@5E1!Kd>Jzn~Ed~OQJY9?>TZ7XwB9las` zVHMCD&+wj20(>AK90CmepJ<)>^Xn@hV0F?N{KGeozuwC5g$YqdsRFBmWw-AcMA9gD zt(ehjZ+#0Twfy7obkh-{dUxjp0<~e(W2k^l2nFDs00gQI=6&l9K%JKjO4>*bvrGOcBLgH>|KXpmMajER)jb6HCn7wlW=jlMCc zP`n=_#@Wc=gp`gkBX`@dcTe#U*(}ecaRnHW3i@7uQOOX!$sRJ!G&9vSk92m!a6e1E zX;NeK{hYA+xzDQG{^DIBd5R^Cz#>VfH7{hUJu2$J4(TQ^H!xL!aAlSQOdvUdHInxn z(Nt*~K2~GfVmdAHS*+i-Ig`y9^1rUXK;MJ&zeY{lP?Ln;3+S#lSVTDs);(x2R*svU zv$QeuXRT^3Yb@|s9fNzK^boNVMkvNzGeKueqh?TH?2X=JO)}SEobxO*{)zOcJrfY$ zk}terNfCrk*)%e>>@&hhjwN_r4Vp3BY@2quaRH1q+}aWPPUs;FW{72y+Zx5Rp>Xn=H2P+kZd zw_Qjr!wYHT;yInohY~GAFLM&{Jg&e(g~YNDo4|n}6oHd+(=3+`cT%|@9`e@L(dt7u z-e%}-4GP23BGDG9k!bVuNc1TOvp3t0G7!-UZ+oS48T5w}!~O<#z>KP6Gsy?@Hrge2(4g5*oY`vFLs ztxSu6(yM~TCeYs)<9)aCCUO#3;x9ia0UvOkHr5f_getJWLEDc{%`}!%TNk-?#C`pf zVSFPe=={x#Va#D*91~hGAN34q5LKk7w4?)Uq>MK+7R7-SF}fqmV-qQAN@ZKpI@EgD z3?o!8dgllI+iaU`V%@w8jn&(_qGNi^e9F?Swla^kTq?*Nh66rqGSH^x9^LGEZaFRG zwy0-wZZI!*WO*PAh7)IEPBUA&8t(E=?b_QvL4|LgzQ23r-nM z#2YSw#-lEto{lY-&iF7W_!e|J%plYo+YC^GSt1*zR&ERrG>6r8#SqW!8kI#ch3V#C zo3(BeQg_gWEc`k?~Lx5=%0p5lTt! zf#R{+GIg&H-}oEcn!0UqVunWq+cwlvteaoivgQ+ZD-Ls=Y@&~EHX9(uy@LFa$5rGv zT=!KORCiv>N-56N&GM5Ha^R@`GEilwxfr8!Bq*CDq)x|LR%@>uL|XMssenHgbb%A1 z^Mx@htNZckibOq3w$r7Gl|)lU1?}IF<^FMlBM$X7?q9L}3ahihu&pFIOx$P5672(> z{Z-+9GlBgvw8mZdXN-ybBEdoo0U&JQ=d`G_{B(ojUT$l&DkL3~>{#;ycoW@;zW@*( z{PRu*GZOtCp9&bZb<}BrqOcZ4qz<4p&g|}w@J$Q-+P<&aHDx(5yL)2oCFfI{g zQBr6p(J*HaSfG4QWfFkp}|J zxsWKu(VVgp2#Z-@&t9TcV)iadR&G#k@xatuG~2pfh_F$H(Yddx5<6`gRn;cLdSJS= zu;8;}HLbovkmxG4MsIaHklZIfQU{A^f{0F0!D{hsx|rLX-=mnLdjg(yAJZB{+6gPi z)n)?Xy(6AxIZvsuzyxN`>#B$p&-5XgKAX2;Iy}92DCjWo-uDm&J z8_VzC`zcVKt!inbZp*XTBok?wFvMGp!)EKb|ngAttZT7oQ;Uvgr z6Ax_aNZkA(X1)AVqoi$a8B=K%59&l|vPlU}A|lT~(RDIbjvg*1@g!PzwYT0y6SRA0)Ui z;F>gr0u?S@mudsWAzF`Xog9)rbrOQBVnaTW#ANptS#?Ew>q$HaHgye|TLBoOD}idt zRgHCZ4Tp;UZHq2`{o?xbLCPs9NS-RhoLBXp){`^Zniy1;#|2?Ju~;#Uo61X7Te1w0 zltey>5=&OQl1$UlyhJz;vqi89HKq`gekMW;AB0jq;XXEbJ!_5EaBbaYkKHL?xxNt0 zspWNMsSgYumbX}|m(5HaR^rJJ(~FEjV6Ad@XVL0Iv^Yj z!u-BvVKOg`FllANkRfQBMal35@5Wcik@_ROL`}&X#O2nkGsLPq?_XH37>WhvL_o}{ zO{>+~=E_s-xVD5_Bq3NHESerK}NrBa_h)xg*QdL#zbjTc0nboOW-ULu1 z5`1z@?|%5<{B+3F$#z!oRsY|dzOc-zwv;M{Pd`?^+N$awy9qP&#u+u}1YRYiHjSTB zehmj^*=X84mfyWRL*{LScGqq5-DdRdC25hKJfrWfI4@TTshwLX<=0~XzU*d3ZtGCC zd^fBNgPQg=tI(|}cdd0e^nn)7(OJ%`lGLGa5&Duay$fh`p2nsM=mKO8xJkNUat_jM zo219yDPb1TZ3FMOaRWrlJGOxrxD-kqjoZMknPscqi+qe7e12&&0FF!@M=)rSB_j8% zSJT?tr7DHm{OM^*Zl3ZKKMMYQOns3sP0hX5S~EJlU)G84%*|1x(c;rYxACrT-<&r;g{M)|T;7T^ z^n_8ORWrg!YlvLO}kNzpq) zEs0P!XxaLykS`dgf|YB81;UzPB~^w)K1prqpP|#U`TMh%Cm+sEpQG>0SE#Qj%+Lo9 z;GvhQmkg#WmeqYcGK?SRHIkPq#~iSso##~47_vI4BV)NqkCT**hvJIrpyOPBC?FD( zRxcF4(a?o%2)za!ux4oi{^FD)+sakb(9X;Hy}CAG4uPCGgCAmL1L%1+o6J;t-()ti zLS;q?zzX9PE2ul=19LPt=M15DHXAQYoL+7Lde>EFJxJ~5S0LAfvLRL%ey0u6#}&OH zIcQ=dc((0Y)be|W+N#FyUys)(eUc-4Wbj=~e-gG@X{dmutKaUHEVvAg`;0Rt80Q9U z6Bzf_5Du5UN593tZIhNJ?_Qgn-9S`?RrBI8R}GE|w*{}wb^6qG?CJZnlhZe6vsF3< z+aq~eFq((+-CKyXwwc{$_JtM<4H%uk6cA5d&#hnu%h=U=*NORqu6I4fQ#s?~sd-`< ztKo^IouGP@7Mu->1n0t{FDk=^I*bOT#)DlcNV%dq+n|eb7Vy%+9%>xIbx&F3Ald%>hnYyz4moy_Nkj+uc1IFs!u!>q6 zs{U2BjtPma*~|M8Z~y zrjzE4{FjGdtIpKhcOMi=rHW4g@R#A{4B43=%=&aXRc5_|e@I;*%?{Cz#Pp9TXwH%* z0^Kf|!~BzEfjdAQe+>|1C_I%^q#UiUX*FN=_cY)L#TvOuM=y+`Qw+Q@tY@Wbr_azi z4BgG%Js9!zDx>-JkpqOiWOCQ({BW9ILoh$I4u>=E)*4~wjCLc5Xzzit&dc-sy=6-# zpvgCsJPF&(AJio8R~}E6 zxHzfA@|1E_Nia^x+Gy?Na%s3sxotNLA%n`2Sj0%GXHj-phk9napLr&B57gTF;o=eRP1H4kr9EBqVlcoDcEe-T1W7<^}^Rquc{}b@MDJ*8n;|EpxodPD#>y#m2X=7g448ouE6vF==XLws&b;RRf}EJ#DQ2B2B71^ z%end`N8fyP9Hc2F(f|14SI5B(^l-UpgDiR=kG1tkLp(kqkM(FG*sys2MqIxfqmmLA zI?P@2bG4Df%cnQG+!gK?i<33?@#&v=e7fC4nps9|V!xp(r?JU=VK~Y27`}^OI#?9n z1;HVK`0l9275cI7_)g_z-uridfp#_0gbsi6(EA}>Ce_kQR6KpV_~rGb>+gA(c6>fTd`!As_Ug2HU(EPG@M()^l?G=S? z7y;xYDS)YWo@#|L?@ybMa%? z1g`S}CjkXwJY<;qm42g2YmLH`6{Yy-ZP%<_jA1BtYv=S@4E2K9ViXZo8y1IMy-A<#-F9}-7e_T+(brh|Nbq#YC&%Pm9vU8kEwk27V`I2X&3|B*9iNKk~ zDmCc!$H&LYmW+@h?49mR zy)ywNOzy^k4g@!DdLj*@upm#AYvdu5Sfcq1V3vUn=f=0^j=EkVQ#`C@Za8D0qTt@1 zs6`K(-LWnJUcoqwEzL0BU?75iGs@7m^=q#oe8DPVqj>b+L8Nsp9#xu6&l;#zJfc`6 zcuh0ZMqN=&x$6iKoGj5bSx;e{(d#@`3A}2uPUYZz4Ci4;>Mz}z*w`xXg0-o_z!J^ark=ZSD#j+9brdJEm4_S&1b7S4x;wC3J!#Tv3KQ0-7x%4$7_= zlGb#6GCv<+oAFwATW6p)8IR86x6hnw`;1h>toiS>9>CvX%^vcayeH*xqnoef6%7pk z+USb6;DQ+^L4EPp@Ci{=Z4{GYTKAxos_y{OQQA-Q_IPh^U9?bu{o0ZhHv;`I8?>K3 z*U+{&&zaD0_eUsGFu`#!zR28+N<#J`#!Aoc94+Gxoo@Nm_s|e@@5aO%I(F80vLq`! zfT-SgujGo&jTn4eQXz=02TaP<5QKKQnnw*^^^==tPvVi#cZ4**w8PqYy60Yr6m3NY z1V3~=T%8gZ%jhhV@&dTgYkJSqGz`C8%EE4X2MPGmknCs_^zcnfR?{*S zO%&o4kH4Y2Em@vrDw<@UCCgZ*;=B3zTCzNG%--gMY>f>oSflK^F>MXuMkbhMR@WVt zEXo?S0EIZ(k6-F{GMUXL6KfhXYHKAfr{6@AC3(V9Z4Q>>ExAz2M{Og~a}!Nh&E?9D z-S?uYzQ=CS!B@VG`Fb^q0#J+nuAWu|%)WgZr4Wt$U&p3pbVVa|G-NIp$@U2s935yP zv!tkw48hrotXR2@=USGiB-Z$fp?DbI)smp3Doc`!Z2dsmiJN~&u20v~+!%FTc4eJ( za^@hh`O@Kj@T1z~CEt?#W)Q_2Tn4jqx}KdBl9b+1&Vo6IT@TLJ@1lXFt=!Uw_K0)wTZD zuLnPov!8)P4iCTmw*j4P=ukDdPA`A03vfN@^B9;IB#E9#yP z`*5{-;BX&-Rz1uG_ZVGjksr~)^8@?{2Cud|z*GMb>PdL?-h;;<-D%1y?L#|1+tAL1 zfqw3c>(tSj5kvuTow2q(KwPH^x`;YR`?$`1T<5?c?BhE3ah?0P&V5|x)@L8r+5PO} zI)TqVt`kasAJ++beq(W+TCHtGcgAd0r`C9(oxkVEPA)fMLQax|Rk`@Ut_d*Qs%4qk zprT6G)|MLJocZ>gO_KLxw!u|ONgiXBZOnyj?iat$QHaZ)caVa*x`9o!AW*$(1r85JV+Sa-iZ-*qjXF^gIoL6t9Am8Ga8|FH2B zW-dPX-%6piHS5QiYP><44&#DCtDrhs@%)Z_Drl(=#)2d)PluWbH|W$NoN}^8En!+| zQmRq`&S*--+Qm%j<0$p4ASQJ4v749I%ZG4Llnf_J!o3m1TuVxz)Fq%?QH89!qPbRP z6m==V0sz_Tr)0v$Cbx#`?>h6@SW}@k7>RPN$}TehV|q?#j7`sL#u3X>Tax>mV^okb zF&rW;4nUPQ77S%l@|U;Y<+jg65dGtqXvwOQ&wQ|eXX>3c$?2;##+)foMlcuX*)dwt zypjSIxVfjCef4GJ0gqG5;uNy2s#u}%IE_9Q6-%TG`^ep^aerHSnYlzG)o`;La>4G9 zKgMY>5&B4!cop$un6d&Q__1OaYXL8`XMW$Tu=iKIK0uX-> zU}=}e*i;K^LojR}Ckf$-i`cTN09$9yG%e6F-g#NNu4;02#h?&zcUX5EG)WMprX*LC zi)g@p-Ke>tInA#W@-SLenVAoKfd(IRbo+5dIG-!tMrykh8Ad(AJ zu}fvkkOG(3E=!PR!d5HH4~tLQ+3z$U5Utez1su4}A~EJ%*?pfA3OPokP2cAHxy zyDNG_^4J}0^o18Vxh8pve&BW=r|TTA44g<&vKuNlGD$7{AYm8zrX37aS#fQ}Of4ji zHG370EQv20g!mbU-13~1^d=!zJe73E8q`g&(Cfyq%uuLzn$%t5;tCWB2hJpm3Jc`6 zj$m^`$ra4UL%D;MqO?b^_ea4E54!iu3wWK%NqLbr`Qv+>GLBwkEV)LfM5!*Tpq|zS zL)i*=0=9&P;CRc6bX+jrJyh%O6sonn2wN_Gt3a(Qd=-={fpBdIK*;Ss}ATX5y{hp z6w9aP3sLyu_c2{7mQ%sXxB{dT+e+Wc3Cl856O>(|m!w!avV(9x?qEJ14qLNeEDoY? zB3m?%qk5xHV}xE>Uc|xK-^X^l13)cXDjgu!c9&A`fEs;NOz-X>O(zXZQ$M*d3Xs%( zzQHEZ`K8vdQj#KeL&}n-gj@C;#YJFfTpyoE^|&{^B+o%30d)Fxn$M^&>rb zTwjC7j3sznVL@(joZMmB_!yHOvZM~@(K{(P^pp8H`c9J@4}Gd+LW|v}Y=vnsw5E+l z(rDa<28k}c z#o!=La!!>)*^AIp;vgt++~T!xt#j7vS`Ttv`QF@8>p5AJ0t}&1qkSXz}%M%Xm00gw87~; z7>$%%H`_!?wjR|;KcmfNH5=w_1ma84K%H;Tj#S${RtFTPt&HtG5BT<2LGX zgsN-+wot3)#YnX^z!f!Ios96#?5(5GQ5_@9=(?+C^w-1)8+}~OjPWXA46J`K1jn+c z)`hEI0Z@3M6)xfu#lY1p#;jox8AuPWR{^O~5nZ!6P^mcz7Fa(cix>Kd6*7QigViKj ze+7~aRaA~IytK8B^3IhY#+5Pj6DMf^iAL6Xh_CPvn?M9l9)i_yFhT@$k8Q8q&{2s2 zNVKzF!Ai z+jW}KY#l<7plo8PYe*HrB!9gJiv)u2ZI~@b_ki^ZNL_Zjht#Ce-974H}&5aMbjXa zuYG07RjIjEPCo()_qy_&PHMOVznh{vaL%+O)8Z@%E5I{mE`BpmH-DCic;b>@xE4ZM zD;0B+PbVu%w1fO06E2 zCKhKwgxNxJx*?Y_&UJ#M{C^wFt@>_x>;+lk8_KFuQHPLxVw$S^ea$N8k*HL1l$%{1 zJ@6TNTQ^olfs|Pz>Xdw=~}qYK?bMM5cK`lk(2+pn+Jn2VF5$S6rQaQpX|cbt@dtA3vaUkA#) zT$1Gay>dis67SRYOM-b9$nn-=(mBB!FZG;x$V7&P`ERNVhX7^2Z Date: Tue, 5 Dec 2023 11:28:53 +0200 Subject: [PATCH 249/316] rename useApiServerCache and update helm --- api/v1alpha1/vector_types.go | 2 +- .../observability.kaasops.io_vectors.yaml | 2 +- .../observability.kaasops.io_vectors.yaml | 2 +- .../vector-operator/templates/vector.yaml | 3 +- helm/charts/vector-operator/values.yaml | 3 +- helm/index.yaml | 62 +++++++++--------- helm/packages/vector-operator-0.0.33.tgz | Bin 32968 -> 32956 bytes 7 files changed, 36 insertions(+), 38 deletions(-) diff --git a/api/v1alpha1/vector_types.go b/api/v1alpha1/vector_types.go index 7c1fee98..7b48af4c 100644 --- a/api/v1alpha1/vector_types.go +++ b/api/v1alpha1/vector_types.go @@ -31,7 +31,7 @@ type VectorSpec struct { // Vector Agent Agent *VectorAgent `json:"agent,omitempty"` // Determines if requests to the kube-apiserver can be served by a cache. - UseApiServerCache bool `json:"use_apiserver_cache,omitempty"` + UseApiServerCache bool `json:"useApiServerCache,omitempty"` // Vector Aggregator // Aggregator *VectorAggregator `json:"aggregator,omitempty"` } diff --git a/config/crd/bases/observability.kaasops.io_vectors.yaml b/config/crd/bases/observability.kaasops.io_vectors.yaml index b244fdf5..e9f6b3a0 100644 --- a/config/crd/bases/observability.kaasops.io_vectors.yaml +++ b/config/crd/bases/observability.kaasops.io_vectors.yaml @@ -4443,7 +4443,7 @@ spec: type: object type: array type: object - use_apiserver_cache: + useApiServerCache: description: Determines if requests to the kube-apiserver can be served by a cache. type: boolean diff --git a/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml index b244fdf5..e9f6b3a0 100644 --- a/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml +++ b/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml @@ -4443,7 +4443,7 @@ spec: type: object type: array type: object - use_apiserver_cache: + useApiServerCache: description: Determines if requests to the kube-apiserver can be served by a cache. type: boolean diff --git a/helm/charts/vector-operator/templates/vector.yaml b/helm/charts/vector-operator/templates/vector.yaml index 9023060b..e606cbd7 100644 --- a/helm/charts/vector-operator/templates/vector.yaml +++ b/helm/charts/vector-operator/templates/vector.yaml @@ -5,8 +5,7 @@ metadata: name: {{ .Values.vector.name }} namespace: {{ .Release.Namespace }} spec: - mergeKubernetesSources: {{ .Values.vector.mergeKubernetesSources}} - mergeSinks: {{ .Values.vector.mergeSinks}} + useApiServerCache: {{ .Values.vector.useApiServerCache }} {{- with .Values.vector.agent }} agent: {{ toYaml . | indent 4 }} diff --git a/helm/charts/vector-operator/values.yaml b/helm/charts/vector-operator/values.yaml index 72599c3f..1d43cb02 100644 --- a/helm/charts/vector-operator/values.yaml +++ b/helm/charts/vector-operator/values.yaml @@ -74,8 +74,7 @@ args: vector: enable: false name: "vector" - mergeKubernetesSources: false - mergeSinks: false + useApiServerCache: false # agent: # image: timberio/vector:0.24.0-distroless-libc # env: diff --git a/helm/index.yaml b/helm/index.yaml index edec627b..cce0660e 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -3,9 +3,9 @@ entries: vector-operator: - apiVersion: v2 appVersion: v0.0.33 - created: "2023-12-05T11:14:30.122178062+02:00" + created: "2023-12-05T11:28:06.125648974+02:00" description: A Helm chart to install Vector Operator - digest: 3011e7f46cd3788be38c607d67af27ae45940809a1eec15120041a64a973a576 + digest: fcde3c94a0fa6caa5f3d333226c95b7c85ede8489d46277e1222a868ed4ec8c3 home: https://github.com/kaasops/vector-operator name: vector-operator sources: @@ -16,7 +16,7 @@ entries: version: 0.0.33 - apiVersion: v2 appVersion: v0.0.32 - created: "2023-12-05T11:14:30.12122064+02:00" + created: "2023-12-05T11:28:06.124757003+02:00" description: A Helm chart to install Vector Operator digest: 26323037ec47f1703ea930a99ab4ec8fb93b44975ce969514ea68d4130017015 home: https://github.com/kaasops/vector-operator @@ -29,7 +29,7 @@ entries: version: 0.0.32 - apiVersion: v2 appVersion: v0.0.31 - created: "2023-12-05T11:14:30.119974818+02:00" + created: "2023-12-05T11:28:06.123447797+02:00" description: A Helm chart to install Vector Operator digest: 45b924c07a825e0f7cd3fb534a6ffd16604790d13be1aff59150c045474754e3 home: https://github.com/kaasops/vector-operator @@ -42,7 +42,7 @@ entries: version: 0.0.31 - apiVersion: v2 appVersion: v0.0.30 - created: "2023-12-05T11:14:30.119125806+02:00" + created: "2023-12-05T11:28:06.122609568+02:00" description: A Helm chart to install Vector Operator digest: 03beda549d15f50325028ea29af5f2065ac0b8adf3078bf7dc1312981aa5e7db home: https://github.com/kaasops/vector-operator @@ -55,7 +55,7 @@ entries: version: 0.0.30 - apiVersion: v2 appVersion: v0.0.29 - created: "2023-12-05T11:14:30.118244242+02:00" + created: "2023-12-05T11:28:06.121744431+02:00" description: A Helm chart to install Vector Operator digest: 0f025fc3a924b37b8c4131c4d8cfa437d2d4e557ab9476ed3e69a00232c7dca6 home: https://github.com/kaasops/vector-operator @@ -68,7 +68,7 @@ entries: version: 0.0.29 - apiVersion: v2 appVersion: v0.0.28 - created: "2023-12-05T11:14:30.117374178+02:00" + created: "2023-12-05T11:28:06.12089191+02:00" description: A Helm chart to install Vector Operator digest: af856d41314313e04f15e7143409a9c564c6ca610b0d2eaec3112add8573e668 home: https://github.com/kaasops/vector-operator @@ -81,7 +81,7 @@ entries: version: 0.0.28 - apiVersion: v2 appVersion: v0.0.27 - created: "2023-12-05T11:14:30.115996907+02:00" + created: "2023-12-05T11:28:06.119473271+02:00" description: A Helm chart to install Vector Operator digest: 631e2ff02bbd7f247cb486494fd2af60c57cc551066a6a3858226551bc1745a4 home: https://github.com/kaasops/vector-operator @@ -94,7 +94,7 @@ entries: version: 0.0.27 - apiVersion: v2 appVersion: v0.0.26 - created: "2023-12-05T11:14:30.115125439+02:00" + created: "2023-12-05T11:28:06.118512664+02:00" description: A Helm chart to install Vector Operator digest: 760a2833f4c1a33466982419b079ff18d996331ebacc40cf93b0f55229cdb7db home: https://github.com/kaasops/vector-operator @@ -107,7 +107,7 @@ entries: version: 0.0.26 - apiVersion: v2 appVersion: v0.0.25 - created: "2023-12-05T11:14:30.114265154+02:00" + created: "2023-12-05T11:28:06.117653997+02:00" description: A Helm chart to install Vector Operator digest: fd22b996b071b6d85740ccf76e85cb640fa717c2620748d206d3f4fdd44cbcc2 home: https://github.com/kaasops/vector-operator @@ -120,7 +120,7 @@ entries: version: 0.0.25 - apiVersion: v2 appVersion: v0.0.24 - created: "2023-12-05T11:14:30.113348624+02:00" + created: "2023-12-05T11:28:06.116783358+02:00" description: A Helm chart to install Vector Operator digest: ea257e60ecde063a0d1ed52ce5e3283245b8f0e2daba58ea3a5adb0ba82d7799 home: https://github.com/kaasops/vector-operator @@ -133,7 +133,7 @@ entries: version: 0.0.24 - apiVersion: v2 appVersion: v0.0.23 - created: "2023-12-05T11:14:30.111937287+02:00" + created: "2023-12-05T11:28:06.115233362+02:00" description: A Helm chart to install Vector Operator digest: 546d202b3b9263f789b88335263191098dfcabd5d8698105f37cad24d56a8ed0 home: https://github.com/kaasops/vector-operator @@ -146,7 +146,7 @@ entries: version: 0.0.23 - apiVersion: v2 appVersion: v0.0.22 - created: "2023-12-05T11:14:30.111030457+02:00" + created: "2023-12-05T11:28:06.1138291+02:00" description: A Helm chart to install Vector Operator digest: bf96ddc8ac61e9d6beb8bc763fbf3fa6025d950b29d70d80de6e8a0ea45e0411 home: https://github.com/kaasops/vector-operator @@ -159,7 +159,7 @@ entries: version: 0.0.22 - apiVersion: v2 appVersion: v0.0.21 - created: "2023-12-05T11:14:30.110100863+02:00" + created: "2023-12-05T11:28:06.112300943+02:00" description: A Helm chart to install Vector Operator digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d home: https://github.com/kaasops/vector-operator @@ -172,7 +172,7 @@ entries: version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2023-12-05T11:14:30.108584051+02:00" + created: "2023-12-05T11:28:06.110444277+02:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -185,7 +185,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2023-12-05T11:14:30.107627396+02:00" + created: "2023-12-05T11:28:06.109527429+02:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -198,7 +198,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2023-12-05T11:14:30.106946517+02:00" + created: "2023-12-05T11:28:06.108649646+02:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -211,7 +211,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2023-12-05T11:14:30.106408933+02:00" + created: "2023-12-05T11:28:06.107668183+02:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -224,7 +224,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2023-12-05T11:14:30.105865068+02:00" + created: "2023-12-05T11:28:06.106785725+02:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -237,7 +237,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2023-12-05T11:14:30.105322076+02:00" + created: "2023-12-05T11:28:06.10594046+02:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -250,7 +250,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2023-12-05T11:14:30.104367482+02:00" + created: "2023-12-05T11:28:06.104960994+02:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -263,7 +263,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2023-12-05T11:14:30.103785961+02:00" + created: "2023-12-05T11:28:06.104063052+02:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -276,7 +276,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2023-12-05T11:14:30.103245761+02:00" + created: "2023-12-05T11:28:06.103442551+02:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -289,7 +289,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2023-12-05T11:14:30.102696603+02:00" + created: "2023-12-05T11:28:06.102826936+02:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -302,7 +302,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2023-12-05T11:14:30.102200388+02:00" + created: "2023-12-05T11:28:06.10231291+02:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -315,7 +315,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2023-12-05T11:14:30.124041302+02:00" + created: "2023-12-05T11:28:06.127665753+02:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -328,7 +328,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2023-12-05T11:14:30.123558317+02:00" + created: "2023-12-05T11:28:06.12720499+02:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -341,7 +341,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2023-12-05T11:14:30.123103588+02:00" + created: "2023-12-05T11:28:06.126655608+02:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -354,7 +354,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2023-12-05T11:14:30.122643716+02:00" + created: "2023-12-05T11:28:06.126089492+02:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -367,7 +367,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2023-12-05T11:14:30.101668331+02:00" + created: "2023-12-05T11:28:06.101782713+02:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -378,4 +378,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2023-12-05T11:14:30.100874313+02:00" +generated: "2023-12-05T11:28:06.101170623+02:00" diff --git a/helm/packages/vector-operator-0.0.33.tgz b/helm/packages/vector-operator-0.0.33.tgz index e946ccfe340bc77515875cdc5b0f5121b15f8bc6..9fc572d9ca8e712f178c95f67d41395843d642a3 100644 GIT binary patch delta 31753 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 Jckux71OQEuoJIfu From 41cf6ea80b03d56c936cef0622c89948b42dce84 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Tue, 5 Dec 2023 23:03:13 +0200 Subject: [PATCH 250/316] hostfix DefaultSinkName --- config/samples/observability_v1alpha1_vector.yaml | 4 +--- pkg/config/default.go | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/config/samples/observability_v1alpha1_vector.yaml b/config/samples/observability_v1alpha1_vector.yaml index 66324587..2b61d0ab 100644 --- a/config/samples/observability_v1alpha1_vector.yaml +++ b/config/samples/observability_v1alpha1_vector.yaml @@ -4,10 +4,8 @@ metadata: name: vector-sample namespace: vector spec: - mergeKubernetesSources: true agent: image: "timberio/vector:0.28.1-debian" internalMetrics: false api: - enabled: false - compressConfigFile: false + enabled: true diff --git a/pkg/config/default.go b/pkg/config/default.go index 993a099e..6a8fadd3 100644 --- a/pkg/config/default.go +++ b/pkg/config/default.go @@ -33,7 +33,7 @@ var ( DefaultSourceName: defaultSource, }, Sinks: map[string]*Sink{ - DefaultSourceName: defaultSink, + DefaultSinkName: defaultSink, }, } From 0ee4fc83c93c6a5b29047616484e3c14a1f1293b Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Sat, 9 Dec 2023 23:13:50 +0200 Subject: [PATCH 251/316] create or update funcs refactoring --- .../observability_v1alpha1_vector.yaml | 1 + go.mod | 62 +- go.sum | 614 +++--------------- pkg/utils/k8s/k8s.go | 449 ++++--------- pkg/vector/vectoragent/vectoragent_config.go | 2 +- .../vectoragent/vectoragent_controller.go | 4 +- .../vectoragent/vectoragent_daemonset.go | 4 +- .../vectoragent/vectoragent_podmonitor.go | 2 +- pkg/vector/vectoragent/vectoragent_rbac.go | 6 +- pkg/vector/vectoragent/vectoragent_service.go | 2 +- 10 files changed, 261 insertions(+), 885 deletions(-) diff --git a/config/samples/observability_v1alpha1_vector.yaml b/config/samples/observability_v1alpha1_vector.yaml index 2b61d0ab..48008cc9 100644 --- a/config/samples/observability_v1alpha1_vector.yaml +++ b/config/samples/observability_v1alpha1_vector.yaml @@ -9,3 +9,4 @@ spec: internalMetrics: false api: enabled: true + healthcheck: true diff --git a/go.mod b/go.mod index 79038422..fca7f8aa 100644 --- a/go.mod +++ b/go.mod @@ -3,31 +3,38 @@ module github.com/kaasops/vector-operator go 1.21 require ( - github.com/go-logr/logr v1.2.3 + github.com/VictoriaMetrics/operator/api v0.0.0-20231128174956-7965dba77210 + github.com/go-logr/logr v1.2.4 github.com/mitchellh/mapstructure v1.5.0 github.com/onsi/ginkgo v1.16.5 - github.com/onsi/gomega v1.19.0 + github.com/onsi/gomega v1.20.1 github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.60.1 - github.com/stretchr/testify v1.8.1 + github.com/stretchr/testify v1.8.2 gopkg.in/yaml.v2 v2.4.0 - k8s.io/api v0.25.0 - k8s.io/apimachinery v0.25.0 - k8s.io/client-go v0.25.0 + k8s.io/api v0.25.6 + k8s.io/apimachinery v0.25.6 + k8s.io/client-go v0.25.6 k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73 sigs.k8s.io/controller-runtime v0.12.3 sigs.k8s.io/yaml v1.4.0 ) require ( - cloud.google.com/go v0.97.0 // indirect + cloud.google.com/go/compute v1.19.3 // indirect + cloud.google.com/go/compute/metadata v0.2.3 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest v0.11.27 // indirect github.com/Azure/go-autorest/autorest/adal v0.9.20 // indirect github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect + github.com/VictoriaMetrics/VictoriaMetrics v1.91.3 // indirect + github.com/VictoriaMetrics/fasthttp v1.2.0 // indirect + github.com/VictoriaMetrics/metrics v1.24.0 // indirect + github.com/VictoriaMetrics/metricsql v0.56.2 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/bmatcuk/doublestar/v4 v4.6.0 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/emicklei/go-restful/v3 v3.8.0 // indirect github.com/evanphx/json-patch v4.12.0+incompatible // indirect @@ -39,40 +46,47 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.2.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/uuid v1.1.2 // indirect + github.com/google/uuid v1.3.0 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.16.5 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/nxadm/tail v1.4.8 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.12.1 // indirect - github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.32.1 // indirect - github.com/prometheus/procfs v0.7.3 // indirect + github.com/prometheus/client_golang v1.15.1 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.43.0 // indirect + github.com/prometheus/procfs v0.9.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - go.uber.org/atomic v1.7.0 // indirect - go.uber.org/multierr v1.6.0 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fastjson v1.6.4 // indirect + github.com/valyala/fastrand v1.1.0 // indirect + github.com/valyala/fasttemplate v1.2.2 // indirect + github.com/valyala/histogram v1.2.0 // indirect + github.com/valyala/quicktemplate v1.7.0 // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/multierr v1.7.0 // indirect go.uber.org/zap v1.19.1 // indirect - golang.org/x/crypto v0.7.0 // indirect - golang.org/x/net v0.8.0 // indirect - golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect + golang.org/x/crypto v0.9.0 // indirect + golang.org/x/net v0.10.0 // indirect + golang.org/x/oauth2 v0.8.0 // indirect golang.org/x/sys v0.15.0 // indirect - golang.org/x/term v0.6.0 // indirect - golang.org/x/text v0.8.0 // indirect - golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect + golang.org/x/term v0.8.0 // indirect + golang.org/x/text v0.9.0 // indirect + golang.org/x/time v0.3.0 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.28.0 // indirect + google.golang.org/protobuf v1.30.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index e9375202..2aa08ec9 100644 --- a/go.sum +++ b/go.sum @@ -1,49 +1,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0 h1:3DXvAyifywvq64LfkKaMOmkWPS1CikIQdMe2lY9vxU8= -cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +cloud.google.com/go/compute v1.19.3 h1:DcTwsFgGev/wV5+q8o2fzgcHOaac+DKGC91ZlvpsQds= +cloud.google.com/go/compute v1.19.3/go.mod h1:qxvISKp/gYnXkSAD1ppcSOveRAmzxicEv/JlizULFrI= +cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.11.27 h1:F3R3q42aWytozkV8ihzcgMO4OA4cuqr3bNlsEuF6//A= @@ -61,33 +20,29 @@ github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/VictoriaMetrics/VictoriaMetrics v1.91.3 h1:mInejMsp7W3z4lrEzO4uQy59WnxIhMqwCeiogAId+jU= +github.com/VictoriaMetrics/VictoriaMetrics v1.91.3/go.mod h1:iDxknwOOdiyR+rBuv20cBVkRLA1jHhQ37eTS29segRw= +github.com/VictoriaMetrics/fasthttp v1.2.0 h1:nd9Wng4DlNtaI27WlYh5mGXCJOmee/2c2blTJwfyU9I= +github.com/VictoriaMetrics/fasthttp v1.2.0/go.mod h1:zv5YSmasAoSyv8sBVexfArzFDIGGTN4TfCKAtAw7IfE= +github.com/VictoriaMetrics/metrics v1.18.1/go.mod h1:ArjwVz7WpgpegX/JpB0zpNF2h2232kErkEnzH1sxMmA= +github.com/VictoriaMetrics/metrics v1.24.0 h1:ILavebReOjYctAGY5QU2F9X0MYvkcrG3aEn2RKa1Zkw= +github.com/VictoriaMetrics/metrics v1.24.0/go.mod h1:eFT25kvsTidQFHb6U0oa0rTrDRdz4xTYjpL8+UPohys= +github.com/VictoriaMetrics/metricsql v0.56.2 h1:quBAbYOlWMhmdgzFSCr1yjtVcdZYZrVQJ7nR9zor7ZM= +github.com/VictoriaMetrics/metricsql v0.56.2/go.mod h1:6pP1ZeLVJHqJrHlF6Ij3gmpQIznSsgktEcZgsAWYel0= +github.com/VictoriaMetrics/operator/api v0.0.0-20231128174956-7965dba77210 h1:lug0mgxNLNEWS5eEVqCBZoTg6ZTRdxdUK4jlV2doKF8= +github.com/VictoriaMetrics/operator/api v0.0.0-20231128174956-7965dba77210/go.mod h1:9xOZrc3kjanpgasau9iMeUM6vYIm37bdTpBRYB0nccY= +github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= +github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bmatcuk/doublestar/v4 v4.6.0 h1:HTuxyug8GyFbRkrffIpzNCSK4luc0TY3wzXvzIZhEXc= +github.com/bmatcuk/doublestar/v4 v4.6.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -95,13 +50,7 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/emicklei/go-restful/v3 v3.8.0 h1:eCZ8ulSerjdAiaNpF7GxXIE7ZCMo1moN1qX+S609eVw= github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= @@ -110,21 +59,11 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= @@ -133,35 +72,19 @@ github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTr github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU= github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= @@ -169,109 +92,61 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= +github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= @@ -279,13 +154,12 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.1.4 h1:GNapqRSid3zijZ9H77KrgVG4/8KqiyRsxcSxe+7ApXY= -github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= +github.com/onsi/ginkgo/v2 v2.1.6 h1:Fx2POJZfKRQcM1pH49qSZiYeu319wji004qX+GDovrU= +github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= -github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q= +github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -293,326 +167,146 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.60.1 h1:A46xpyCEQpMFymrNJOaL5aAu3ZWgEKwJUXZrB5D3IUM= github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.60.1/go.mod h1:MNl09GdaKb/vE8QdcCWyICDV7XAbGX6gKKQAS43XW1c= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI= +github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.43.0 h1:iq+BVjvYLei5f27wiuNiB1DN6DYQkp1c8Bx0Vykh5us= +github.com/prometheus/common v0.43.0/go.mod h1:NCvr5cQIh3Y/gy73/RdVtC9r8xxrxwJnB+2lB3BxrFc= +github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= +github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.30.0/go.mod h1:2rsYD01CKFrjjsvFxx75KlEUNpWNBY9JWD3K/7o2Cus= +github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ= +github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= +github.com/valyala/fastrand v1.1.0 h1:f+5HkLW4rsgzdNoleUOB69hyT9IlD2ZQh9GyDMfb5G8= +github.com/valyala/fastrand v1.1.0/go.mod h1:HWqCzkrkg6QXT8V2EXWvXCoow7vLwOFN002oeRzjapQ= +github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= +github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/valyala/histogram v1.2.0 h1:wyYGAZZt3CpwUiIb9AU/Zbllg1llXyrtApRS815OLoQ= +github.com/valyala/histogram v1.2.0/go.mod h1:Hb4kBwb4UxsaNbbbh+RRz8ZR6pdodR57tzWUS3BUzXY= +github.com/valyala/quicktemplate v1.7.0 h1:LUPTJmlVcb46OOUY3IeD9DojFpAVbsG+5WFTcjMJzCM= +github.com/valyala/quicktemplate v1.7.0/go.mod h1:sqKJnoaOF88V07vkO+9FL8fb9uZg/VPSJnLYn+LmLk8= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= -go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= +go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec= +go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI= go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= +golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -620,124 +314,17 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -747,29 +334,22 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= @@ -780,20 +360,15 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.25.0 h1:H+Q4ma2U/ww0iGB78ijZx6DRByPz6/733jIuFpX70e0= -k8s.io/api v0.25.0/go.mod h1:ttceV1GyV1i1rnmvzT3BST08N6nGt+dudGrquzVQWPk= +k8s.io/api v0.25.6 h1:LwDY2H6kD/3R8TekJYYaJWOdekNdXDO44eVpX6sNtJA= +k8s.io/api v0.25.6/go.mod h1:bVp01KUcl8VUHFBTJMOknWNo7XvR0cMbeTTuFg1zCUs= k8s.io/apiextensions-apiserver v0.25.0 h1:CJ9zlyXAbq0FIW8CD7HHyozCMBpDSiH7EdrSTCZcZFY= k8s.io/apiextensions-apiserver v0.25.0/go.mod h1:3pAjZiN4zw7R8aZC5gR0y3/vCkGlAjCazcg1me8iB/E= -k8s.io/apimachinery v0.25.0 h1:MlP0r6+3XbkUG2itd6vp3oxbtdQLQI94fD5gCS+gnoU= -k8s.io/apimachinery v0.25.0/go.mod h1:qMx9eAk0sZQGsXGu86fab8tZdffHbwUfsvzqKn4mfB0= -k8s.io/client-go v0.25.0 h1:CVWIaCETLMBNiTUta3d5nzRbXvY5Hy9Dpl+VvREpu5E= -k8s.io/client-go v0.25.0/go.mod h1:lxykvypVfKilxhTklov0wz1FoaUZ8X4EwbhS6rpRfN8= +k8s.io/apimachinery v0.25.6 h1:r6KIF2AHwLqFfZ0LcOA3I11SF62YZK83dxj1fn14NOQ= +k8s.io/apimachinery v0.25.6/go.mod h1:1S2i1QHkmxc8+EZCIxe/fX5hpldVXk4gvnJInMEb8D4= +k8s.io/client-go v0.25.6 h1:CHxACHi0DijmlYyUR7ooZoXnD5P8jYLgBHcxp775x/U= +k8s.io/client-go v0.25.6/go.mod h1:s9mMAGFYiH3Z66j7BESzu0GEradT9GQ2LjFf/YRrnyc= k8s.io/component-base v0.25.0 h1:haVKlLkPCFZhkcqB6WCvpVxftrg6+FK5x1ZuaIDaQ5Y= k8s.io/component-base v0.25.0/go.mod h1:F2Sumv9CnbBlqrpdf7rKZTmmd2meJq0HizeyY/yAFxk= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= @@ -803,9 +378,6 @@ k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 h1:MQ8BAZPZlWk3S9K4a9NCkI k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU= k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73 h1:H9TCJUUx+2VA0ZiD9lvtaX8fthFsMoD+Izn93E/hm8U= k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/controller-runtime v0.12.3 h1:FCM8xeY/FI8hoAfh/V4XbbYMY20gElh9yh+A98usMio= sigs.k8s.io/controller-runtime v0.12.3/go.mod h1:qKsk4WE6zW2Hfj0G4v10EnNB2jMG1C+NTb8h+DwCoU0= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= diff --git a/pkg/utils/k8s/k8s.go b/pkg/utils/k8s/k8s.go index 81a27097..49c3b008 100644 --- a/pkg/utils/k8s/k8s.go +++ b/pkg/utils/k8s/k8s.go @@ -23,21 +23,21 @@ import ( "fmt" "io" + victoriametricsv1beta1 "github.com/VictoriaMetrics/operator/api/v1beta1" monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" - "k8s.io/apimachinery/pkg/api/equality" api_errors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/discovery" "k8s.io/client-go/kubernetes" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) var ( - ErrNotSupported = errors.New("Not Supported type for create or update kubernetes resource") + ErrNotSupported = errors.New("not Supported type for create or update kubernetes resource") ) func NewNotSupportedError(obj client.Object) error { @@ -45,381 +45,170 @@ func NewNotSupportedError(obj client.Object) error { } func CreateOrUpdateResource(ctx context.Context, obj client.Object, c client.Client) error { - switch obj.(type) { + switch o := obj.(type) { case *appsv1.Deployment: - return createOrUpdateDeployment(ctx, obj, c) + return createOrUpdateDeployment(ctx, o, c) case *appsv1.StatefulSet: - return createOrUpdateStatefulSet(ctx, obj, c) + return createOrUpdateStatefulSet(ctx, o, c) case *appsv1.DaemonSet: - return createOrUpdateDaemonSet(ctx, obj, c) + return createOrUpdateDaemonSet(ctx, o, c) case *corev1.Secret: - return createOrUpdateSecret(ctx, obj, c) + return createOrUpdateSecret(ctx, o, c) case *corev1.Service: - return createOrUpdateService(ctx, obj, c) + return createOrUpdateService(ctx, o, c) case *corev1.ServiceAccount: - return createOrUpdateServiceAccount(ctx, obj, c) + return createOrUpdateServiceAccount(ctx, o, c) case *rbacv1.ClusterRole: - return createOrUpdateClusterRole(ctx, obj, c) + return createOrUpdateClusterRole(ctx, o, c) case *rbacv1.ClusterRoleBinding: - return createOrUpdateClusterRoleBinding(ctx, obj, c) + return createOrUpdateClusterRoleBinding(ctx, o, c) case *monitorv1.PodMonitor: - return createOrUpdatePodMonitor(ctx, obj, c) + return createOrUpdatePodMonitor(ctx, o, c) + case *victoriametricsv1beta1.VMPodScrape: + return createOrUpdatePodSrape(ctx, o, c) default: - return NewNotSupportedError(obj) + return NewNotSupportedError(o) } } -func createOrUpdateDeployment(ctx context.Context, obj runtime.Object, c client.Client) error { - desired := obj.(*appsv1.Deployment) - - // Create Deployment - err := c.Create(ctx, desired) - if api_errors.IsAlreadyExists(err) { - // If alredy exist - compare with existed - existing := &appsv1.Deployment{} - err := c.Get(ctx, client.ObjectKeyFromObject(desired), existing) - if err != nil { - return err - } - - // init Interface for compare - desiredFields := []interface{}{ - desired.GetAnnotations(), - desired.GetLabels(), - desired.Spec, - } - existingFields := []interface{}{ - existing.GetAnnotations(), - existing.GetLabels(), - existing.Spec, - } - - // Compare - // FIXME: DeepDerivative does not compare fields, if they omitted in desiredFields - if !equality.Semantic.DeepDerivative(desiredFields, existingFields) { - // Update if not equal - existing.Labels = desired.Labels - existing.Annotations = desired.Annotations - existing.Spec = desired.Spec - return c.Update(ctx, existing) - } +func createOrUpdateDeployment(ctx context.Context, desired *appsv1.Deployment, c client.Client) error { + existing := desired.DeepCopy() + _, err := controllerutil.CreateOrUpdate(ctx, c, existing, func() error { + existing.Labels = desired.Labels + existing.Annotations = desired.Annotations + existing.Spec = desired.Spec return nil + }) + if err != nil { + return fmt.Errorf("failed to create or update Deployment: %w", err) } - return err + return nil } -func createOrUpdateStatefulSet(ctx context.Context, obj runtime.Object, c client.Client) error { - desired := obj.(*appsv1.StatefulSet) - - // Create StatefulSet - err := c.Create(ctx, desired) - if api_errors.IsAlreadyExists(err) { - // If alredy exist - compare with existed - existing := &appsv1.StatefulSet{} - err := c.Get(ctx, client.ObjectKeyFromObject(desired), existing) - if err != nil { - return err - } - - // init Interface for compare - desiredFields := []interface{}{ - desired.GetAnnotations(), - desired.GetLabels(), - desired.Spec, - } - existingFields := []interface{}{ - existing.GetAnnotations(), - existing.GetLabels(), - existing.Spec, - } - - // Compare - // FIXME: DeepDerivative does not compare fields, if they omitted in desiredFields - if !equality.Semantic.DeepDerivative(desiredFields, existingFields) { - // Update if not equal - existing.Labels = desired.Labels - existing.Annotations = desired.Annotations - existing.Spec = desired.Spec - return c.Update(ctx, existing) - } +func createOrUpdateStatefulSet(ctx context.Context, desired *appsv1.StatefulSet, c client.Client) error { + existing := desired.DeepCopy() + _, err := controllerutil.CreateOrUpdate(ctx, c, existing, func() error { + existing.Labels = desired.Labels + existing.Annotations = desired.Annotations + existing.Spec = desired.Spec return nil + }) + if err != nil { + return fmt.Errorf("failed to create or update StatefulSet: %w", err) } - return err + return nil } -func createOrUpdateDaemonSet(ctx context.Context, obj runtime.Object, c client.Client) error { - desired := obj.(*appsv1.DaemonSet) - - // Create DaemonSet - err := c.Create(ctx, desired) - if api_errors.IsAlreadyExists(err) { - // If alredy exist - compare with existed - existing := &appsv1.DaemonSet{} - err := c.Get(ctx, client.ObjectKeyFromObject(desired), existing) - if err != nil { - return err - } - - // init Interface for compare - desiredFields := []interface{}{ - desired.GetAnnotations(), - desired.GetLabels(), - desired.Spec, - } - existingFields := []interface{}{ - existing.GetAnnotations(), - existing.GetLabels(), - existing.Spec, - } - - // Compare - // FIXME: DeepDerivative does not compare fields, if they omitted in desiredFields - if !equality.Semantic.DeepDerivative(desiredFields, existingFields) { - // Update if not equal - existing.Labels = desired.Labels - existing.Annotations = desired.Annotations - existing.Spec = desired.Spec - return c.Update(ctx, existing) - } +func createOrUpdateDaemonSet(ctx context.Context, desired *appsv1.DaemonSet, c client.Client) error { + existing := desired.DeepCopy() + _, err := controllerutil.CreateOrUpdate(ctx, c, existing, func() error { + existing.Labels = desired.Labels + existing.Annotations = desired.Annotations + existing.Spec = desired.Spec return nil + }) + if err != nil { + return fmt.Errorf("failed to create or update Daemonset: %w", err) } - return err + return nil } -func createOrUpdateSecret(ctx context.Context, obj runtime.Object, c client.Client) error { - desired := obj.(*corev1.Secret) - - // Create Secret - err := c.Create(ctx, desired) - if api_errors.IsAlreadyExists(err) { - // If alredy exist - compare with existed - existing := &corev1.Secret{} - err := c.Get(ctx, client.ObjectKeyFromObject(desired), existing) - if err != nil { - return err - } - - // init Interface for compare - desiredFields := []interface{}{ - desired.GetAnnotations(), - desired.GetLabels(), - desired.Data, - } - existingFields := []interface{}{ - existing.GetAnnotations(), - existing.GetLabels(), - existing.Data, - } - - // Compare - // FIXME: DeepDerivative does not compare fields, if they omitted in desiredFields - if !equality.Semantic.DeepDerivative(desiredFields, existingFields) { - // Update if not equal - existing.Labels = desired.Labels - existing.Annotations = desired.Annotations - existing.Data = desired.Data - return c.Update(ctx, existing) - } +func createOrUpdateSecret(ctx context.Context, desired *corev1.Secret, c client.Client) error { + existing := desired.DeepCopy() + _, err := controllerutil.CreateOrUpdate(ctx, c, existing, func() error { + existing.Labels = desired.Labels + existing.Annotations = desired.Annotations + existing.Data = desired.Data return nil + }) + if err != nil { + return fmt.Errorf("failed to create or update Secret: %w", err) } - return err + return nil } -func createOrUpdateService(ctx context.Context, obj runtime.Object, c client.Client) error { - desired := obj.(*corev1.Service) - - // Create Service - err := c.Create(ctx, desired) - if api_errors.IsAlreadyExists(err) { - // If alredy exist - compare with existed - existing := &corev1.Service{} - err := c.Get(ctx, client.ObjectKeyFromObject(desired), existing) - if err != nil { - return err - } - - // init Interface for compare - desiredFields := []interface{}{ - desired.GetAnnotations(), - desired.GetLabels(), - desired.Spec, - } - existingFields := []interface{}{ - existing.GetAnnotations(), - existing.GetLabels(), - existing.Spec, - } - - // Compare - // FIXME: DeepDerivative does not compare fields, if they omitted in desiredFields - if !equality.Semantic.DeepDerivative(desiredFields, existingFields) { - // Update if not equal - existing.Labels = desired.Labels - existing.Annotations = desired.Annotations - existing.Spec = desired.Spec - return c.Update(ctx, existing) - } +func createOrUpdateService(ctx context.Context, desired *corev1.Service, c client.Client) error { + existing := desired.DeepCopy() + _, err := controllerutil.CreateOrUpdate(ctx, c, existing, func() error { + existing.Labels = desired.Labels + existing.Annotations = desired.Annotations + existing.Spec = desired.Spec return nil + }) + if err != nil { + return fmt.Errorf("failed to create or update Deployment: %w", err) } - return err + return nil } -func createOrUpdateServiceAccount(ctx context.Context, obj runtime.Object, c client.Client) error { - desired := obj.(*corev1.ServiceAccount) - - // Create ServiceAccount - err := c.Create(ctx, desired) - if api_errors.IsAlreadyExists(err) { - // If alredy exist - compare with existed - existing := &corev1.ServiceAccount{} - err := c.Get(ctx, client.ObjectKeyFromObject(desired), existing) - if err != nil { - return err - } - - // init Interface for compare - desiredFields := []interface{}{ - desired.GetAnnotations(), - desired.GetLabels(), - } - existingFields := []interface{}{ - existing.GetAnnotations(), - existing.GetLabels(), - } - - // Compare - // FIXME: DeepDerivative does not compare fields, if they omitted in desiredFields - if !equality.Semantic.DeepDerivative(desiredFields, existingFields) { - // Update if not equal - existing.Labels = desired.Labels - existing.Annotations = desired.Annotations - return c.Update(ctx, existing) - } +func createOrUpdateServiceAccount(ctx context.Context, desired *corev1.ServiceAccount, c client.Client) error { + existing := desired.DeepCopy() + _, err := controllerutil.CreateOrUpdate(ctx, c, existing, func() error { + existing.Labels = desired.Labels + existing.Annotations = desired.Annotations return nil + }) + if err != nil { + return fmt.Errorf("failed to create or update ServiceAccount: %w", err) } - return err + return nil } -func createOrUpdateClusterRole(ctx context.Context, obj runtime.Object, c client.Client) error { - desired := obj.(*rbacv1.ClusterRole) - - // Create ClusterRole - err := c.Create(ctx, desired) - if api_errors.IsAlreadyExists(err) { - // If alredy exist - compare with existed - existing := &rbacv1.ClusterRole{} - err := c.Get(ctx, client.ObjectKeyFromObject(desired), existing) - if err != nil { - return err - } - - // init Interface for compare - desiredFields := []interface{}{ - desired.GetAnnotations(), - desired.GetLabels(), - desired.Rules, - } - existingFields := []interface{}{ - existing.GetAnnotations(), - existing.GetLabels(), - existing.Rules, - } - - // Compare - // FIXME: DeepDerivative does not compare fields, if they omitted in desiredFields - if !equality.Semantic.DeepDerivative(desiredFields, existingFields) { - // Update if not equal - existing.Labels = desired.Labels - existing.Annotations = desired.Annotations - existing.Rules = desired.Rules - return c.Update(ctx, existing) - } +func createOrUpdateClusterRole(ctx context.Context, desired *rbacv1.ClusterRole, c client.Client) error { + existing := desired.DeepCopy() + _, err := controllerutil.CreateOrUpdate(ctx, c, existing, func() error { + existing.Labels = desired.Labels + existing.Annotations = desired.Annotations + existing.Rules = desired.Rules return nil + }) + if err != nil { + return fmt.Errorf("failed to create or update ClusterRole: %w", err) } - return err + return nil } -func createOrUpdateClusterRoleBinding(ctx context.Context, obj runtime.Object, c client.Client) error { - desired := obj.(*rbacv1.ClusterRoleBinding) - - // Create ClusterRoleBinding - err := c.Create(ctx, desired) - if api_errors.IsAlreadyExists(err) { - // If alredy exist - compare with existed - existing := &rbacv1.ClusterRoleBinding{} - err := c.Get(ctx, client.ObjectKeyFromObject(desired), existing) - if err != nil { - return err - } - - // Init Interface for compare - desiredFields := []interface{}{ - desired.GetAnnotations(), - desired.GetLabels(), - desired.RoleRef, - desired.Subjects, - } - existingFields := []interface{}{ - existing.GetAnnotations(), - existing.GetLabels(), - existing.RoleRef, - existing.Subjects, - } - - // Compare - // FIXME: DeepDerivative does not compare fields, if they omitted in desiredFields - if !equality.Semantic.DeepDerivative(desiredFields, existingFields) { - // Update if not equal - existing.Labels = desired.Labels - existing.Annotations = desired.Annotations - existing.RoleRef = desired.RoleRef - existing.Subjects = desired.Subjects - return c.Update(ctx, existing) - } +func createOrUpdateClusterRoleBinding(ctx context.Context, desired *rbacv1.ClusterRoleBinding, c client.Client) error { + existing := desired.DeepCopy() + _, err := controllerutil.CreateOrUpdate(ctx, c, existing, func() error { + existing.Labels = desired.Labels + existing.Annotations = desired.Annotations + existing.RoleRef = desired.RoleRef + existing.Subjects = desired.Subjects return nil + }) + if err != nil { + return fmt.Errorf("failed to create or update ClusterRoleBinding: %w", err) } - return err + return nil } -// - -func createOrUpdatePodMonitor(ctx context.Context, obj runtime.Object, c client.Client) error { - desired := obj.(*monitorv1.PodMonitor) - - // Create PodMonitor - err := c.Create(ctx, desired) - if api_errors.IsAlreadyExists(err) { - // If alredy exist - compare with existed - existing := &monitorv1.PodMonitor{} - err := c.Get(ctx, client.ObjectKeyFromObject(desired), existing) - if err != nil { - return err - } - - // init Interface for compare - desiredFields := []interface{}{ - desired.GetAnnotations(), - desired.GetLabels(), - desired.Spec, - } - existingFields := []interface{}{ - existing.GetAnnotations(), - existing.GetLabels(), - existing.Spec, - } +func createOrUpdatePodMonitor(ctx context.Context, desired *monitorv1.PodMonitor, c client.Client) error { + existing := desired.DeepCopy() + _, err := controllerutil.CreateOrUpdate(ctx, c, existing, func() error { + existing.Labels = desired.Labels + existing.Annotations = desired.Annotations + existing.Spec = desired.Spec + return nil + }) + if err != nil { + return fmt.Errorf("failed to create or update PodMonitor: %w", err) + } + return nil +} - // Compare - // FIXME: DeepDerivative does not compare fields, if they omitted in desiredFields - if !equality.Semantic.DeepDerivative(desiredFields, existingFields) { - // Update if not equal - existing.Labels = desired.Labels - existing.Annotations = desired.Annotations - existing.Spec = desired.Spec - return c.Update(ctx, existing) - } +func createOrUpdatePodSrape(ctx context.Context, desired *victoriametricsv1beta1.VMPodScrape, c client.Client) error { + existing := desired.DeepCopy() + _, err := controllerutil.CreateOrUpdate(ctx, c, existing, func() error { + existing.Labels = desired.Labels + existing.Annotations = desired.Annotations + existing.Spec = desired.Spec return nil + }) + if err != nil { + return fmt.Errorf("failed to create or update VMPodScrape: %w", err) } - return err + return nil } // Something else: diff --git a/pkg/vector/vectoragent/vectoragent_config.go b/pkg/vector/vectoragent/vectoragent_config.go index 9f8811bb..f3ade890 100644 --- a/pkg/vector/vectoragent/vectoragent_config.go +++ b/pkg/vector/vectoragent/vectoragent_config.go @@ -36,7 +36,7 @@ func (ctrl *Controller) createVectorAgentConfig(ctx context.Context) (*corev1.Se "agent.json": data, } secret := &corev1.Secret{ - ObjectMeta: ctrl.objectMetaVectorAgent(labels), + ObjectMeta: ctrl.objectMetaVectorAgent(labels, ctrl.Vector.Namespace), Data: config, } diff --git a/pkg/vector/vectoragent/vectoragent_controller.go b/pkg/vector/vectoragent/vectoragent_controller.go index b80b4abd..5272f886 100644 --- a/pkg/vector/vectoragent/vectoragent_controller.go +++ b/pkg/vector/vectoragent/vectoragent_controller.go @@ -150,10 +150,10 @@ func (ctrl *Controller) labelsForVectorAgent() map[string]string { } } -func (ctrl *Controller) objectMetaVectorAgent(labels map[string]string) metav1.ObjectMeta { +func (ctrl *Controller) objectMetaVectorAgent(labels map[string]string, namespace string) metav1.ObjectMeta { return metav1.ObjectMeta{ Name: ctrl.Vector.Name + "-agent", - Namespace: ctrl.Vector.Namespace, + Namespace: namespace, Labels: labels, OwnerReferences: ctrl.getControllerReference(), } diff --git a/pkg/vector/vectoragent/vectoragent_daemonset.go b/pkg/vector/vectoragent/vectoragent_daemonset.go index 3fa54370..cff55e2e 100644 --- a/pkg/vector/vectoragent/vectoragent_daemonset.go +++ b/pkg/vector/vectoragent/vectoragent_daemonset.go @@ -37,11 +37,11 @@ func (ctrl *Controller) createVectorAgentDaemonSet() *appsv1.DaemonSet { } daemonset := &appsv1.DaemonSet{ - ObjectMeta: ctrl.objectMetaVectorAgent(labels), + ObjectMeta: ctrl.objectMetaVectorAgent(labels, ctrl.Vector.Namespace), Spec: appsv1.DaemonSetSpec{ Selector: &metav1.LabelSelector{MatchLabels: labels}, Template: corev1.PodTemplateSpec{ - ObjectMeta: ctrl.objectMetaVectorAgent(labels), + ObjectMeta: ctrl.objectMetaVectorAgent(labels, ctrl.Vector.Namespace), Spec: corev1.PodSpec{ ServiceAccountName: ctrl.getNameVectorAgent(), Volumes: ctrl.generateVectorAgentVolume(), diff --git a/pkg/vector/vectoragent/vectoragent_podmonitor.go b/pkg/vector/vectoragent/vectoragent_podmonitor.go index 5cbe31a3..80523151 100644 --- a/pkg/vector/vectoragent/vectoragent_podmonitor.go +++ b/pkg/vector/vectoragent/vectoragent_podmonitor.go @@ -10,7 +10,7 @@ func (ctrl *Controller) createVectorAgentPodMonitor() *monitorv1.PodMonitor { labels := ctrl.labelsForVectorAgent() podmonitor := &monitorv1.PodMonitor{ - ObjectMeta: ctrl.objectMetaVectorAgent(labels), + ObjectMeta: ctrl.objectMetaVectorAgent(labels, ctrl.Vector.Namespace), Spec: monitorv1.PodMonitorSpec{ PodMetricsEndpoints: []monitorv1.PodMetricsEndpoint{ { diff --git a/pkg/vector/vectoragent/vectoragent_rbac.go b/pkg/vector/vectoragent/vectoragent_rbac.go index 190c2d8a..16969707 100644 --- a/pkg/vector/vectoragent/vectoragent_rbac.go +++ b/pkg/vector/vectoragent/vectoragent_rbac.go @@ -25,7 +25,7 @@ func (ctrl *Controller) createVectorAgentServiceAccount() *corev1.ServiceAccount labels := ctrl.labelsForVectorAgent() serviceAccount := &corev1.ServiceAccount{ - ObjectMeta: ctrl.objectMetaVectorAgent(labels), + ObjectMeta: ctrl.objectMetaVectorAgent(labels, ctrl.Vector.Namespace), } return serviceAccount @@ -35,7 +35,7 @@ func (ctrl *Controller) createVectorAgentClusterRole() *rbacv1.ClusterRole { labels := ctrl.labelsForVectorAgent() clusterRole := &rbacv1.ClusterRole{ - ObjectMeta: ctrl.objectMetaVectorAgent(labels), + ObjectMeta: ctrl.objectMetaVectorAgent(labels, ""), Rules: []rbacv1.PolicyRule{ { APIGroups: []string{""}, @@ -52,7 +52,7 @@ func (ctrl *Controller) createVectorAgentClusterRoleBinding() *rbacv1.ClusterRol labels := ctrl.labelsForVectorAgent() clusterRoleBinding := &rbacv1.ClusterRoleBinding{ - ObjectMeta: ctrl.objectMetaVectorAgent(labels), + ObjectMeta: ctrl.objectMetaVectorAgent(labels, ""), RoleRef: rbacv1.RoleRef{ Kind: "ClusterRole", APIGroup: "rbac.authorization.k8s.io", diff --git a/pkg/vector/vectoragent/vectoragent_service.go b/pkg/vector/vectoragent/vectoragent_service.go index 06e41327..8790a976 100644 --- a/pkg/vector/vectoragent/vectoragent_service.go +++ b/pkg/vector/vectoragent/vectoragent_service.go @@ -30,7 +30,7 @@ func (ctrl *Controller) createVectorAgentService() *corev1.Service { labels := ctrl.labelsForVectorAgent() return &corev1.Service{ - ObjectMeta: ctrl.objectMetaVectorAgent(labels), + ObjectMeta: ctrl.objectMetaVectorAgent(labels, ctrl.Vector.Namespace), Spec: corev1.ServiceSpec{ Ports: []corev1.ServicePort{ { From 9d623c7eaaa3e164172ec6708a31442fbff1cacf Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Wed, 13 Dec 2023 14:57:21 +0200 Subject: [PATCH 252/316] validate label selector --- controllers/pipeline_controller.go | 2 +- pkg/config/config.go | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/controllers/pipeline_controller.go b/controllers/pipeline_controller.go index d55caafb..93647b4f 100644 --- a/controllers/pipeline_controller.go +++ b/controllers/pipeline_controller.go @@ -112,7 +112,7 @@ func (r *PipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c vaCtrl := vectoragent.NewController(vector, r.Client, r.Clientset) vaCtrl.SetDefault() // Get Vector Config file - byteConfig, _ := config.BuildByteConfig(vaCtrl, pipelineCR) + byteConfig, err := config.BuildByteConfig(vaCtrl, pipelineCR) if err != nil { if err := pipeline.SetFailedStatus(ctx, r.Client, pipelineCR, err.Error()); err != nil { return ctrl.Result{}, err diff --git a/pkg/config/config.go b/pkg/config/config.go index 4bbd7c48..ac6300dd 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -28,12 +28,14 @@ import ( "github.com/kaasops/vector-operator/pkg/vector/vectoragent" "github.com/mitchellh/mapstructure" "gopkg.in/yaml.v2" + "k8s.io/apimachinery/pkg/labels" goyaml "sigs.k8s.io/yaml" ) var ( ErrNotAllowedSourceType error = errors.New("type kubernetes_logs only allowed") ErrClusterScopeNotAllowed error = errors.New("logs from external namespace not allowed") + ErrInvalidSelector error = errors.New("invalid selector") ) func New(vector *vectorv1alpha1.Vector) *VectorConfig { @@ -88,6 +90,14 @@ func BuildConfig(vaCtrl *vectoragent.Controller, pipelines ...pipeline.Pipeline) if v.Type != KubernetesSourceType { return nil, ErrNotAllowedSourceType } + _, err := labels.Parse(v.ExtraLabelSelector) + if err != nil { + return nil, ErrInvalidSelector + } + _, err = labels.Parse(v.ExtraNamespaceLabelSelector) + if err != nil { + return nil, ErrInvalidSelector + } if v.ExtraNamespaceLabelSelector == "" { v.ExtraNamespaceLabelSelector = k8s.NamespaceNameToLabel(pipeline.GetNamespace()) } From 9b0e8dfbae0459d176e33481d7266d5798152940 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Wed, 13 Dec 2023 15:07:19 +0200 Subject: [PATCH 253/316] update helm release --- helm/charts/vector-operator/Chart.yaml | 4 +- helm/index.yaml | 73 +++++++++++++---------- helm/packages/vector-operator-0.0.34.tgz | Bin 0 -> 32959 bytes 3 files changed, 45 insertions(+), 32 deletions(-) create mode 100644 helm/packages/vector-operator-0.0.34.tgz diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index cdae7586..dae8a5c8 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.33 +version: 0.0.34 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.0.33" +appVersion: "v0.0.34" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/index.yaml b/helm/index.yaml index cce0660e..5b673dec 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.0.34 + created: "2023-12-13T15:05:48.993680741+02:00" + description: A Helm chart to install Vector Operator + digest: 7f7909d3e2d092967ab8fa9ba191bca3684d19806908a4d54512f7d1836c1e3e + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.34.tgz + version: 0.0.34 - apiVersion: v2 appVersion: v0.0.33 - created: "2023-12-05T11:28:06.125648974+02:00" + created: "2023-12-13T15:05:48.992781703+02:00" description: A Helm chart to install Vector Operator digest: fcde3c94a0fa6caa5f3d333226c95b7c85ede8489d46277e1222a868ed4ec8c3 home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.0.33 - apiVersion: v2 appVersion: v0.0.32 - created: "2023-12-05T11:28:06.124757003+02:00" + created: "2023-12-13T15:05:48.991736673+02:00" description: A Helm chart to install Vector Operator digest: 26323037ec47f1703ea930a99ab4ec8fb93b44975ce969514ea68d4130017015 home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.0.32 - apiVersion: v2 appVersion: v0.0.31 - created: "2023-12-05T11:28:06.123447797+02:00" + created: "2023-12-13T15:05:48.99071941+02:00" description: A Helm chart to install Vector Operator digest: 45b924c07a825e0f7cd3fb534a6ffd16604790d13be1aff59150c045474754e3 home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.0.31 - apiVersion: v2 appVersion: v0.0.30 - created: "2023-12-05T11:28:06.122609568+02:00" + created: "2023-12-13T15:05:48.989433472+02:00" description: A Helm chart to install Vector Operator digest: 03beda549d15f50325028ea29af5f2065ac0b8adf3078bf7dc1312981aa5e7db home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.0.30 - apiVersion: v2 appVersion: v0.0.29 - created: "2023-12-05T11:28:06.121744431+02:00" + created: "2023-12-13T15:05:48.988508317+02:00" description: A Helm chart to install Vector Operator digest: 0f025fc3a924b37b8c4131c4d8cfa437d2d4e557ab9476ed3e69a00232c7dca6 home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.0.29 - apiVersion: v2 appVersion: v0.0.28 - created: "2023-12-05T11:28:06.12089191+02:00" + created: "2023-12-13T15:05:48.987576519+02:00" description: A Helm chart to install Vector Operator digest: af856d41314313e04f15e7143409a9c564c6ca610b0d2eaec3112add8573e668 home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.0.28 - apiVersion: v2 appVersion: v0.0.27 - created: "2023-12-05T11:28:06.119473271+02:00" + created: "2023-12-13T15:05:48.986655288+02:00" description: A Helm chart to install Vector Operator digest: 631e2ff02bbd7f247cb486494fd2af60c57cc551066a6a3858226551bc1745a4 home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.0.27 - apiVersion: v2 appVersion: v0.0.26 - created: "2023-12-05T11:28:06.118512664+02:00" + created: "2023-12-13T15:05:48.98531776+02:00" description: A Helm chart to install Vector Operator digest: 760a2833f4c1a33466982419b079ff18d996331ebacc40cf93b0f55229cdb7db home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.0.26 - apiVersion: v2 appVersion: v0.0.25 - created: "2023-12-05T11:28:06.117653997+02:00" + created: "2023-12-13T15:05:48.984428155+02:00" description: A Helm chart to install Vector Operator digest: fd22b996b071b6d85740ccf76e85cb640fa717c2620748d206d3f4fdd44cbcc2 home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.0.25 - apiVersion: v2 appVersion: v0.0.24 - created: "2023-12-05T11:28:06.116783358+02:00" + created: "2023-12-13T15:05:48.983490221+02:00" description: A Helm chart to install Vector Operator digest: ea257e60ecde063a0d1ed52ce5e3283245b8f0e2daba58ea3a5adb0ba82d7799 home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.0.24 - apiVersion: v2 appVersion: v0.0.23 - created: "2023-12-05T11:28:06.115233362+02:00" + created: "2023-12-13T15:05:48.982580785+02:00" description: A Helm chart to install Vector Operator digest: 546d202b3b9263f789b88335263191098dfcabd5d8698105f37cad24d56a8ed0 home: https://github.com/kaasops/vector-operator @@ -146,7 +159,7 @@ entries: version: 0.0.23 - apiVersion: v2 appVersion: v0.0.22 - created: "2023-12-05T11:28:06.1138291+02:00" + created: "2023-12-13T15:05:48.981135766+02:00" description: A Helm chart to install Vector Operator digest: bf96ddc8ac61e9d6beb8bc763fbf3fa6025d950b29d70d80de6e8a0ea45e0411 home: https://github.com/kaasops/vector-operator @@ -159,7 +172,7 @@ entries: version: 0.0.22 - apiVersion: v2 appVersion: v0.0.21 - created: "2023-12-05T11:28:06.112300943+02:00" + created: "2023-12-13T15:05:48.980181049+02:00" description: A Helm chart to install Vector Operator digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d home: https://github.com/kaasops/vector-operator @@ -172,7 +185,7 @@ entries: version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2023-12-05T11:28:06.110444277+02:00" + created: "2023-12-13T15:05:48.97926056+02:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -185,7 +198,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2023-12-05T11:28:06.109527429+02:00" + created: "2023-12-13T15:05:48.978669192+02:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -198,7 +211,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2023-12-05T11:28:06.108649646+02:00" + created: "2023-12-13T15:05:48.97772768+02:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -211,7 +224,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2023-12-05T11:28:06.107668183+02:00" + created: "2023-12-13T15:05:48.977081183+02:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -224,7 +237,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2023-12-05T11:28:06.106785725+02:00" + created: "2023-12-13T15:05:48.97650951+02:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -237,7 +250,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2023-12-05T11:28:06.10594046+02:00" + created: "2023-12-13T15:05:48.975919778+02:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -250,7 +263,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2023-12-05T11:28:06.104960994+02:00" + created: "2023-12-13T15:05:48.975143364+02:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -263,7 +276,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2023-12-05T11:28:06.104063052+02:00" + created: "2023-12-13T15:05:48.974474092+02:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -276,7 +289,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2023-12-05T11:28:06.103442551+02:00" + created: "2023-12-13T15:05:48.973325008+02:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -289,7 +302,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2023-12-05T11:28:06.102826936+02:00" + created: "2023-12-13T15:05:48.972638831+02:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -302,7 +315,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2023-12-05T11:28:06.10231291+02:00" + created: "2023-12-13T15:05:48.972061782+02:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -315,7 +328,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2023-12-05T11:28:06.127665753+02:00" + created: "2023-12-13T15:05:48.996018778+02:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -328,7 +341,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2023-12-05T11:28:06.12720499+02:00" + created: "2023-12-13T15:05:48.995505199+02:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -341,7 +354,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2023-12-05T11:28:06.126655608+02:00" + created: "2023-12-13T15:05:48.994760335+02:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -354,7 +367,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2023-12-05T11:28:06.126089492+02:00" + created: "2023-12-13T15:05:48.994174418+02:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -367,7 +380,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2023-12-05T11:28:06.101782713+02:00" + created: "2023-12-13T15:05:48.971446797+02:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -378,4 +391,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2023-12-05T11:28:06.101170623+02:00" +generated: "2023-12-13T15:05:48.964762256+02:00" diff --git a/helm/packages/vector-operator-0.0.34.tgz b/helm/packages/vector-operator-0.0.34.tgz new file mode 100644 index 0000000000000000000000000000000000000000..2d69494c2d2eabbb352072a1965ece4c297451a9 GIT binary patch literal 32959 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 Date: Wed, 13 Dec 2023 23:01:47 +0200 Subject: [PATCH 254/316] fix configcheck get secret --- pkg/config/config.go | 5 ++--- pkg/config/configcheck/configcheck.go | 6 ++++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index ac6300dd..f6c00fa9 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -35,7 +35,6 @@ import ( var ( ErrNotAllowedSourceType error = errors.New("type kubernetes_logs only allowed") ErrClusterScopeNotAllowed error = errors.New("logs from external namespace not allowed") - ErrInvalidSelector error = errors.New("invalid selector") ) func New(vector *vectorv1alpha1.Vector) *VectorConfig { @@ -92,11 +91,11 @@ func BuildConfig(vaCtrl *vectoragent.Controller, pipelines ...pipeline.Pipeline) } _, err := labels.Parse(v.ExtraLabelSelector) if err != nil { - return nil, ErrInvalidSelector + return nil, fmt.Errorf("invalid pod selector for source %s: %w", k, err) } _, err = labels.Parse(v.ExtraNamespaceLabelSelector) if err != nil { - return nil, ErrInvalidSelector + return nil, fmt.Errorf("invalid namespace selector for source %s: %w", k, err) } if v.ExtraNamespaceLabelSelector == "" { v.ExtraNamespaceLabelSelector = k8s.NamespaceNameToLabel(pipeline.GetNamespace()) diff --git a/pkg/config/configcheck/configcheck.go b/pkg/config/configcheck/configcheck.go index ee7a48ee..9ea882f7 100644 --- a/pkg/config/configcheck/configcheck.go +++ b/pkg/config/configcheck/configcheck.go @@ -131,6 +131,12 @@ func (cc *ConfigCheck) Run(ctx context.Context) (string, error) { return "", err } + vectorConfigCheckSecret, err = k8s.GetSecret(ctx, types.NamespacedName{Namespace: cc.Namespace, Name: vectorConfigCheckSecret.Name}, cc.Client) + + if err != nil { + return "", err + } + // Set OwnerReference to pod if err = controllerutil.SetOwnerReference(vectorConfigCheckSecret, vectorConfigCheckPod, cc.Client.Scheme()); err != nil { return "", err From 1cd56794fd884e733936f59e3ee1b0fd3edfd19a Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Thu, 14 Dec 2023 09:54:54 +0200 Subject: [PATCH 255/316] update helm --- helm/index.yaml | 64 +++++++++++------------ helm/packages/vector-operator-0.0.34.tgz | Bin 32959 -> 32957 bytes 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/helm/index.yaml b/helm/index.yaml index 5b673dec..2481c4ce 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -3,9 +3,9 @@ entries: vector-operator: - apiVersion: v2 appVersion: v0.0.34 - created: "2023-12-13T15:05:48.993680741+02:00" + created: "2023-12-14T09:52:38.025537364+02:00" description: A Helm chart to install Vector Operator - digest: 7f7909d3e2d092967ab8fa9ba191bca3684d19806908a4d54512f7d1836c1e3e + digest: 01e9d488ee78c8603d821f96344edee568ebcb42049d586de37b8df39b372bd4 home: https://github.com/kaasops/vector-operator name: vector-operator sources: @@ -16,7 +16,7 @@ entries: version: 0.0.34 - apiVersion: v2 appVersion: v0.0.33 - created: "2023-12-13T15:05:48.992781703+02:00" + created: "2023-12-14T09:52:38.024632606+02:00" description: A Helm chart to install Vector Operator digest: fcde3c94a0fa6caa5f3d333226c95b7c85ede8489d46277e1222a868ed4ec8c3 home: https://github.com/kaasops/vector-operator @@ -29,7 +29,7 @@ entries: version: 0.0.33 - apiVersion: v2 appVersion: v0.0.32 - created: "2023-12-13T15:05:48.991736673+02:00" + created: "2023-12-14T09:52:38.023708527+02:00" description: A Helm chart to install Vector Operator digest: 26323037ec47f1703ea930a99ab4ec8fb93b44975ce969514ea68d4130017015 home: https://github.com/kaasops/vector-operator @@ -42,7 +42,7 @@ entries: version: 0.0.32 - apiVersion: v2 appVersion: v0.0.31 - created: "2023-12-13T15:05:48.99071941+02:00" + created: "2023-12-14T09:52:38.022586777+02:00" description: A Helm chart to install Vector Operator digest: 45b924c07a825e0f7cd3fb534a6ffd16604790d13be1aff59150c045474754e3 home: https://github.com/kaasops/vector-operator @@ -55,7 +55,7 @@ entries: version: 0.0.31 - apiVersion: v2 appVersion: v0.0.30 - created: "2023-12-13T15:05:48.989433472+02:00" + created: "2023-12-14T09:52:38.020806007+02:00" description: A Helm chart to install Vector Operator digest: 03beda549d15f50325028ea29af5f2065ac0b8adf3078bf7dc1312981aa5e7db home: https://github.com/kaasops/vector-operator @@ -68,7 +68,7 @@ entries: version: 0.0.30 - apiVersion: v2 appVersion: v0.0.29 - created: "2023-12-13T15:05:48.988508317+02:00" + created: "2023-12-14T09:52:38.018952333+02:00" description: A Helm chart to install Vector Operator digest: 0f025fc3a924b37b8c4131c4d8cfa437d2d4e557ab9476ed3e69a00232c7dca6 home: https://github.com/kaasops/vector-operator @@ -81,7 +81,7 @@ entries: version: 0.0.29 - apiVersion: v2 appVersion: v0.0.28 - created: "2023-12-13T15:05:48.987576519+02:00" + created: "2023-12-14T09:52:38.018044466+02:00" description: A Helm chart to install Vector Operator digest: af856d41314313e04f15e7143409a9c564c6ca610b0d2eaec3112add8573e668 home: https://github.com/kaasops/vector-operator @@ -94,7 +94,7 @@ entries: version: 0.0.28 - apiVersion: v2 appVersion: v0.0.27 - created: "2023-12-13T15:05:48.986655288+02:00" + created: "2023-12-14T09:52:38.016719699+02:00" description: A Helm chart to install Vector Operator digest: 631e2ff02bbd7f247cb486494fd2af60c57cc551066a6a3858226551bc1745a4 home: https://github.com/kaasops/vector-operator @@ -107,7 +107,7 @@ entries: version: 0.0.27 - apiVersion: v2 appVersion: v0.0.26 - created: "2023-12-13T15:05:48.98531776+02:00" + created: "2023-12-14T09:52:38.014330906+02:00" description: A Helm chart to install Vector Operator digest: 760a2833f4c1a33466982419b079ff18d996331ebacc40cf93b0f55229cdb7db home: https://github.com/kaasops/vector-operator @@ -120,7 +120,7 @@ entries: version: 0.0.26 - apiVersion: v2 appVersion: v0.0.25 - created: "2023-12-13T15:05:48.984428155+02:00" + created: "2023-12-14T09:52:38.012893769+02:00" description: A Helm chart to install Vector Operator digest: fd22b996b071b6d85740ccf76e85cb640fa717c2620748d206d3f4fdd44cbcc2 home: https://github.com/kaasops/vector-operator @@ -133,7 +133,7 @@ entries: version: 0.0.25 - apiVersion: v2 appVersion: v0.0.24 - created: "2023-12-13T15:05:48.983490221+02:00" + created: "2023-12-14T09:52:38.011604297+02:00" description: A Helm chart to install Vector Operator digest: ea257e60ecde063a0d1ed52ce5e3283245b8f0e2daba58ea3a5adb0ba82d7799 home: https://github.com/kaasops/vector-operator @@ -146,7 +146,7 @@ entries: version: 0.0.24 - apiVersion: v2 appVersion: v0.0.23 - created: "2023-12-13T15:05:48.982580785+02:00" + created: "2023-12-14T09:52:38.01027713+02:00" description: A Helm chart to install Vector Operator digest: 546d202b3b9263f789b88335263191098dfcabd5d8698105f37cad24d56a8ed0 home: https://github.com/kaasops/vector-operator @@ -159,7 +159,7 @@ entries: version: 0.0.23 - apiVersion: v2 appVersion: v0.0.22 - created: "2023-12-13T15:05:48.981135766+02:00" + created: "2023-12-14T09:52:38.008363788+02:00" description: A Helm chart to install Vector Operator digest: bf96ddc8ac61e9d6beb8bc763fbf3fa6025d950b29d70d80de6e8a0ea45e0411 home: https://github.com/kaasops/vector-operator @@ -172,7 +172,7 @@ entries: version: 0.0.22 - apiVersion: v2 appVersion: v0.0.21 - created: "2023-12-13T15:05:48.980181049+02:00" + created: "2023-12-14T09:52:38.006840497+02:00" description: A Helm chart to install Vector Operator digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d home: https://github.com/kaasops/vector-operator @@ -185,7 +185,7 @@ entries: version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2023-12-13T15:05:48.97926056+02:00" + created: "2023-12-14T09:52:38.005592858+02:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -198,7 +198,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2023-12-13T15:05:48.978669192+02:00" + created: "2023-12-14T09:52:38.004740552+02:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -211,7 +211,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2023-12-13T15:05:48.97772768+02:00" + created: "2023-12-14T09:52:38.003359261+02:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -224,7 +224,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2023-12-13T15:05:48.977081183+02:00" + created: "2023-12-14T09:52:38.002471719+02:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -237,7 +237,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2023-12-13T15:05:48.97650951+02:00" + created: "2023-12-14T09:52:38.001677769+02:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -250,7 +250,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2023-12-13T15:05:48.975919778+02:00" + created: "2023-12-14T09:52:38.000857725+02:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -263,7 +263,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2023-12-13T15:05:48.975143364+02:00" + created: "2023-12-14T09:52:38.000012946+02:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -276,7 +276,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2023-12-13T15:05:48.974474092+02:00" + created: "2023-12-14T09:52:37.999192652+02:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -289,7 +289,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2023-12-13T15:05:48.973325008+02:00" + created: "2023-12-14T09:52:37.997934871+02:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -302,7 +302,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2023-12-13T15:05:48.972638831+02:00" + created: "2023-12-14T09:52:37.997307718+02:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -315,7 +315,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2023-12-13T15:05:48.972061782+02:00" + created: "2023-12-14T09:52:37.996835349+02:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -328,7 +328,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2023-12-13T15:05:48.996018778+02:00" + created: "2023-12-14T09:52:38.027780362+02:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -341,7 +341,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2023-12-13T15:05:48.995505199+02:00" + created: "2023-12-14T09:52:38.02730167+02:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -354,7 +354,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2023-12-13T15:05:48.994760335+02:00" + created: "2023-12-14T09:52:38.026470975+02:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -367,7 +367,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2023-12-13T15:05:48.994174418+02:00" + created: "2023-12-14T09:52:38.02600111+02:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -380,7 +380,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2023-12-13T15:05:48.971446797+02:00" + created: "2023-12-14T09:52:37.996349983+02:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -391,4 +391,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2023-12-13T15:05:48.964762256+02:00" +generated: "2023-12-14T09:52:37.995680411+02:00" diff --git a/helm/packages/vector-operator-0.0.34.tgz b/helm/packages/vector-operator-0.0.34.tgz index 2d69494c2d2eabbb352072a1965ece4c297451a9..bd762ea2f9be5c9dedbbe919744b2d27362e6038 100644 GIT binary patch delta 32250 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

AL6m$$GQsoAiL7VB_EPuBUEF`K_EB%h!G* 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 delta 32250 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 From b3d753d64c87e3dc85e40fc4a5b2ddf3cf67cfb3 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Thu, 14 Dec 2023 11:17:14 +0200 Subject: [PATCH 256/316] update helm release --- helm/charts/vector-operator/Chart.yaml | 4 +- helm/index.yaml | 75 +++++++++++++---------- helm/packages/vector-operator-0.0.35.tgz | Bin 0 -> 32957 bytes 3 files changed, 46 insertions(+), 33 deletions(-) create mode 100644 helm/packages/vector-operator-0.0.35.tgz diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index dae8a5c8..6db9e48b 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.34 +version: 0.0.35 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.0.34" +appVersion: "v0.0.35" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/index.yaml b/helm/index.yaml index 2481c4ce..bebdbab3 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.0.35 + created: "2023-12-14T11:16:58.695388625+02:00" + description: A Helm chart to install Vector Operator + digest: d03ba759c42f2bd8d8f1df71702ee4f26a73b8bc28760ca7254af20d811cee8a + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.35.tgz + version: 0.0.35 - apiVersion: v2 appVersion: v0.0.34 - created: "2023-12-14T09:52:38.025537364+02:00" + created: "2023-12-14T11:16:58.693896005+02:00" description: A Helm chart to install Vector Operator digest: 01e9d488ee78c8603d821f96344edee568ebcb42049d586de37b8df39b372bd4 home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.0.34 - apiVersion: v2 appVersion: v0.0.33 - created: "2023-12-14T09:52:38.024632606+02:00" + created: "2023-12-14T11:16:58.692225822+02:00" description: A Helm chart to install Vector Operator digest: fcde3c94a0fa6caa5f3d333226c95b7c85ede8489d46277e1222a868ed4ec8c3 home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.0.33 - apiVersion: v2 appVersion: v0.0.32 - created: "2023-12-14T09:52:38.023708527+02:00" + created: "2023-12-14T11:16:58.691181657+02:00" description: A Helm chart to install Vector Operator digest: 26323037ec47f1703ea930a99ab4ec8fb93b44975ce969514ea68d4130017015 home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.0.32 - apiVersion: v2 appVersion: v0.0.31 - created: "2023-12-14T09:52:38.022586777+02:00" + created: "2023-12-14T11:16:58.690260654+02:00" description: A Helm chart to install Vector Operator digest: 45b924c07a825e0f7cd3fb534a6ffd16604790d13be1aff59150c045474754e3 home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.0.31 - apiVersion: v2 appVersion: v0.0.30 - created: "2023-12-14T09:52:38.020806007+02:00" + created: "2023-12-14T11:16:58.689396484+02:00" description: A Helm chart to install Vector Operator digest: 03beda549d15f50325028ea29af5f2065ac0b8adf3078bf7dc1312981aa5e7db home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.0.30 - apiVersion: v2 appVersion: v0.0.29 - created: "2023-12-14T09:52:38.018952333+02:00" + created: "2023-12-14T11:16:58.688231079+02:00" description: A Helm chart to install Vector Operator digest: 0f025fc3a924b37b8c4131c4d8cfa437d2d4e557ab9476ed3e69a00232c7dca6 home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.0.29 - apiVersion: v2 appVersion: v0.0.28 - created: "2023-12-14T09:52:38.018044466+02:00" + created: "2023-12-14T11:16:58.687331504+02:00" description: A Helm chart to install Vector Operator digest: af856d41314313e04f15e7143409a9c564c6ca610b0d2eaec3112add8573e668 home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.0.28 - apiVersion: v2 appVersion: v0.0.27 - created: "2023-12-14T09:52:38.016719699+02:00" + created: "2023-12-14T11:16:58.686452172+02:00" description: A Helm chart to install Vector Operator digest: 631e2ff02bbd7f247cb486494fd2af60c57cc551066a6a3858226551bc1745a4 home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.0.27 - apiVersion: v2 appVersion: v0.0.26 - created: "2023-12-14T09:52:38.014330906+02:00" + created: "2023-12-14T11:16:58.685307668+02:00" description: A Helm chart to install Vector Operator digest: 760a2833f4c1a33466982419b079ff18d996331ebacc40cf93b0f55229cdb7db home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.0.26 - apiVersion: v2 appVersion: v0.0.25 - created: "2023-12-14T09:52:38.012893769+02:00" + created: "2023-12-14T11:16:58.684327019+02:00" description: A Helm chart to install Vector Operator digest: fd22b996b071b6d85740ccf76e85cb640fa717c2620748d206d3f4fdd44cbcc2 home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.0.25 - apiVersion: v2 appVersion: v0.0.24 - created: "2023-12-14T09:52:38.011604297+02:00" + created: "2023-12-14T11:16:58.683457177+02:00" description: A Helm chart to install Vector Operator digest: ea257e60ecde063a0d1ed52ce5e3283245b8f0e2daba58ea3a5adb0ba82d7799 home: https://github.com/kaasops/vector-operator @@ -146,7 +159,7 @@ entries: version: 0.0.24 - apiVersion: v2 appVersion: v0.0.23 - created: "2023-12-14T09:52:38.01027713+02:00" + created: "2023-12-14T11:16:58.682591112+02:00" description: A Helm chart to install Vector Operator digest: 546d202b3b9263f789b88335263191098dfcabd5d8698105f37cad24d56a8ed0 home: https://github.com/kaasops/vector-operator @@ -159,7 +172,7 @@ entries: version: 0.0.23 - apiVersion: v2 appVersion: v0.0.22 - created: "2023-12-14T09:52:38.008363788+02:00" + created: "2023-12-14T11:16:58.681328372+02:00" description: A Helm chart to install Vector Operator digest: bf96ddc8ac61e9d6beb8bc763fbf3fa6025d950b29d70d80de6e8a0ea45e0411 home: https://github.com/kaasops/vector-operator @@ -172,7 +185,7 @@ entries: version: 0.0.22 - apiVersion: v2 appVersion: v0.0.21 - created: "2023-12-14T09:52:38.006840497+02:00" + created: "2023-12-14T11:16:58.680314102+02:00" description: A Helm chart to install Vector Operator digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d home: https://github.com/kaasops/vector-operator @@ -185,7 +198,7 @@ entries: version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2023-12-14T09:52:38.005592858+02:00" + created: "2023-12-14T11:16:58.679385211+02:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -198,7 +211,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2023-12-14T09:52:38.004740552+02:00" + created: "2023-12-14T11:16:58.678777265+02:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -211,7 +224,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2023-12-14T09:52:38.003359261+02:00" + created: "2023-12-14T11:16:58.677413743+02:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -224,7 +237,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2023-12-14T09:52:38.002471719+02:00" + created: "2023-12-14T11:16:58.676097695+02:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -237,7 +250,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2023-12-14T09:52:38.001677769+02:00" + created: "2023-12-14T11:16:58.675111959+02:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -250,7 +263,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2023-12-14T09:52:38.000857725+02:00" + created: "2023-12-14T11:16:58.674266595+02:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -263,7 +276,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2023-12-14T09:52:38.000012946+02:00" + created: "2023-12-14T11:16:58.673647935+02:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -276,7 +289,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2023-12-14T09:52:37.999192652+02:00" + created: "2023-12-14T11:16:58.673039593+02:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -289,7 +302,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2023-12-14T09:52:37.997934871+02:00" + created: "2023-12-14T11:16:58.672022082+02:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -302,7 +315,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2023-12-14T09:52:37.997307718+02:00" + created: "2023-12-14T11:16:58.671424177+02:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -315,7 +328,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2023-12-14T09:52:37.996835349+02:00" + created: "2023-12-14T11:16:58.670923898+02:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -328,7 +341,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2023-12-14T09:52:38.027780362+02:00" + created: "2023-12-14T11:16:58.698795207+02:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -341,7 +354,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2023-12-14T09:52:38.02730167+02:00" + created: "2023-12-14T11:16:58.697773628+02:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -354,7 +367,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2023-12-14T09:52:38.026470975+02:00" + created: "2023-12-14T11:16:58.696977417+02:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -367,7 +380,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2023-12-14T09:52:38.02600111+02:00" + created: "2023-12-14T11:16:58.696183112+02:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -380,7 +393,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2023-12-14T09:52:37.996349983+02:00" + created: "2023-12-14T11:16:58.670245697+02:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -391,4 +404,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2023-12-14T09:52:37.995680411+02:00" +generated: "2023-12-14T11:16:58.669486644+02:00" diff --git a/helm/packages/vector-operator-0.0.35.tgz b/helm/packages/vector-operator-0.0.35.tgz new file mode 100644 index 0000000000000000000000000000000000000000..278097cfd3c4703c7cd5336e1b9a506fffc3500b GIT binary patch literal 32957 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 Date: Fri, 15 Dec 2023 11:14:40 +0200 Subject: [PATCH 257/316] update desired obj after apply --- pkg/config/configcheck/configcheck.go | 6 ------ pkg/utils/k8s/k8s.go | 10 ++++++++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/pkg/config/configcheck/configcheck.go b/pkg/config/configcheck/configcheck.go index 9ea882f7..ee7a48ee 100644 --- a/pkg/config/configcheck/configcheck.go +++ b/pkg/config/configcheck/configcheck.go @@ -131,12 +131,6 @@ func (cc *ConfigCheck) Run(ctx context.Context) (string, error) { return "", err } - vectorConfigCheckSecret, err = k8s.GetSecret(ctx, types.NamespacedName{Namespace: cc.Namespace, Name: vectorConfigCheckSecret.Name}, cc.Client) - - if err != nil { - return "", err - } - // Set OwnerReference to pod if err = controllerutil.SetOwnerReference(vectorConfigCheckSecret, vectorConfigCheckPod, cc.Client.Scheme()); err != nil { return "", err diff --git a/pkg/utils/k8s/k8s.go b/pkg/utils/k8s/k8s.go index 49c3b008..3e5e54ce 100644 --- a/pkg/utils/k8s/k8s.go +++ b/pkg/utils/k8s/k8s.go @@ -82,6 +82,7 @@ func createOrUpdateDeployment(ctx context.Context, desired *appsv1.Deployment, c if err != nil { return fmt.Errorf("failed to create or update Deployment: %w", err) } + existing.DeepCopyInto(desired) return nil } @@ -96,6 +97,7 @@ func createOrUpdateStatefulSet(ctx context.Context, desired *appsv1.StatefulSet, if err != nil { return fmt.Errorf("failed to create or update StatefulSet: %w", err) } + existing.DeepCopyInto(desired) return nil } @@ -110,6 +112,7 @@ func createOrUpdateDaemonSet(ctx context.Context, desired *appsv1.DaemonSet, c c if err != nil { return fmt.Errorf("failed to create or update Daemonset: %w", err) } + existing.DeepCopyInto(desired) return nil } @@ -124,6 +127,7 @@ func createOrUpdateSecret(ctx context.Context, desired *corev1.Secret, c client. if err != nil { return fmt.Errorf("failed to create or update Secret: %w", err) } + existing.DeepCopyInto(desired) return nil } @@ -138,6 +142,7 @@ func createOrUpdateService(ctx context.Context, desired *corev1.Service, c clien if err != nil { return fmt.Errorf("failed to create or update Deployment: %w", err) } + existing.DeepCopyInto(desired) return nil } @@ -151,6 +156,7 @@ func createOrUpdateServiceAccount(ctx context.Context, desired *corev1.ServiceAc if err != nil { return fmt.Errorf("failed to create or update ServiceAccount: %w", err) } + existing.DeepCopyInto(desired) return nil } @@ -165,6 +171,7 @@ func createOrUpdateClusterRole(ctx context.Context, desired *rbacv1.ClusterRole, if err != nil { return fmt.Errorf("failed to create or update ClusterRole: %w", err) } + existing.DeepCopyInto(desired) return nil } @@ -180,6 +187,7 @@ func createOrUpdateClusterRoleBinding(ctx context.Context, desired *rbacv1.Clust if err != nil { return fmt.Errorf("failed to create or update ClusterRoleBinding: %w", err) } + existing.DeepCopyInto(desired) return nil } @@ -194,6 +202,7 @@ func createOrUpdatePodMonitor(ctx context.Context, desired *monitorv1.PodMonitor if err != nil { return fmt.Errorf("failed to create or update PodMonitor: %w", err) } + existing.DeepCopyInto(desired) return nil } @@ -208,6 +217,7 @@ func createOrUpdatePodSrape(ctx context.Context, desired *victoriametricsv1beta1 if err != nil { return fmt.Errorf("failed to create or update VMPodScrape: %w", err) } + existing.DeepCopyInto(desired) return nil } From d3f36b9341bac32681e5944f593637aac0a7d307 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Fri, 15 Dec 2023 11:16:00 +0200 Subject: [PATCH 258/316] update helm release --- helm/charts/vector-operator/Chart.yaml | 4 +- helm/index.yaml | 77 +++++++++++++---------- helm/packages/vector-operator-0.0.36.tgz | Bin 0 -> 32960 bytes 3 files changed, 47 insertions(+), 34 deletions(-) create mode 100644 helm/packages/vector-operator-0.0.36.tgz diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index 6db9e48b..ee5f2198 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.35 +version: 0.0.36 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.0.35" +appVersion: "v0.0.36" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/index.yaml b/helm/index.yaml index bebdbab3..4aede9fb 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.0.36 + created: "2023-12-15T11:15:46.904445897+02:00" + description: A Helm chart to install Vector Operator + digest: 972b6b4048b6d17b0616786e49915ef52cb7c9573cfb6eb359b6c19b66eabe31 + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.36.tgz + version: 0.0.36 - apiVersion: v2 appVersion: v0.0.35 - created: "2023-12-14T11:16:58.695388625+02:00" + created: "2023-12-15T11:15:46.903527613+02:00" description: A Helm chart to install Vector Operator digest: d03ba759c42f2bd8d8f1df71702ee4f26a73b8bc28760ca7254af20d811cee8a home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.0.35 - apiVersion: v2 appVersion: v0.0.34 - created: "2023-12-14T11:16:58.693896005+02:00" + created: "2023-12-15T11:15:46.902630521+02:00" description: A Helm chart to install Vector Operator digest: 01e9d488ee78c8603d821f96344edee568ebcb42049d586de37b8df39b372bd4 home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.0.34 - apiVersion: v2 appVersion: v0.0.33 - created: "2023-12-14T11:16:58.692225822+02:00" + created: "2023-12-15T11:15:46.901267841+02:00" description: A Helm chart to install Vector Operator digest: fcde3c94a0fa6caa5f3d333226c95b7c85ede8489d46277e1222a868ed4ec8c3 home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.0.33 - apiVersion: v2 appVersion: v0.0.32 - created: "2023-12-14T11:16:58.691181657+02:00" + created: "2023-12-15T11:15:46.900372099+02:00" description: A Helm chart to install Vector Operator digest: 26323037ec47f1703ea930a99ab4ec8fb93b44975ce969514ea68d4130017015 home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.0.32 - apiVersion: v2 appVersion: v0.0.31 - created: "2023-12-14T11:16:58.690260654+02:00" + created: "2023-12-15T11:15:46.899456182+02:00" description: A Helm chart to install Vector Operator digest: 45b924c07a825e0f7cd3fb534a6ffd16604790d13be1aff59150c045474754e3 home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.0.31 - apiVersion: v2 appVersion: v0.0.30 - created: "2023-12-14T11:16:58.689396484+02:00" + created: "2023-12-15T11:15:46.898191288+02:00" description: A Helm chart to install Vector Operator digest: 03beda549d15f50325028ea29af5f2065ac0b8adf3078bf7dc1312981aa5e7db home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.0.30 - apiVersion: v2 appVersion: v0.0.29 - created: "2023-12-14T11:16:58.688231079+02:00" + created: "2023-12-15T11:15:46.897043786+02:00" description: A Helm chart to install Vector Operator digest: 0f025fc3a924b37b8c4131c4d8cfa437d2d4e557ab9476ed3e69a00232c7dca6 home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.0.29 - apiVersion: v2 appVersion: v0.0.28 - created: "2023-12-14T11:16:58.687331504+02:00" + created: "2023-12-15T11:15:46.896124342+02:00" description: A Helm chart to install Vector Operator digest: af856d41314313e04f15e7143409a9c564c6ca610b0d2eaec3112add8573e668 home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.0.28 - apiVersion: v2 appVersion: v0.0.27 - created: "2023-12-14T11:16:58.686452172+02:00" + created: "2023-12-15T11:15:46.895204298+02:00" description: A Helm chart to install Vector Operator digest: 631e2ff02bbd7f247cb486494fd2af60c57cc551066a6a3858226551bc1745a4 home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.0.27 - apiVersion: v2 appVersion: v0.0.26 - created: "2023-12-14T11:16:58.685307668+02:00" + created: "2023-12-15T11:15:46.893922056+02:00" description: A Helm chart to install Vector Operator digest: 760a2833f4c1a33466982419b079ff18d996331ebacc40cf93b0f55229cdb7db home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.0.26 - apiVersion: v2 appVersion: v0.0.25 - created: "2023-12-14T11:16:58.684327019+02:00" + created: "2023-12-15T11:15:46.893047219+02:00" description: A Helm chart to install Vector Operator digest: fd22b996b071b6d85740ccf76e85cb640fa717c2620748d206d3f4fdd44cbcc2 home: https://github.com/kaasops/vector-operator @@ -146,7 +159,7 @@ entries: version: 0.0.25 - apiVersion: v2 appVersion: v0.0.24 - created: "2023-12-14T11:16:58.683457177+02:00" + created: "2023-12-15T11:15:46.892184999+02:00" description: A Helm chart to install Vector Operator digest: ea257e60ecde063a0d1ed52ce5e3283245b8f0e2daba58ea3a5adb0ba82d7799 home: https://github.com/kaasops/vector-operator @@ -159,7 +172,7 @@ entries: version: 0.0.24 - apiVersion: v2 appVersion: v0.0.23 - created: "2023-12-14T11:16:58.682591112+02:00" + created: "2023-12-15T11:15:46.891083389+02:00" description: A Helm chart to install Vector Operator digest: 546d202b3b9263f789b88335263191098dfcabd5d8698105f37cad24d56a8ed0 home: https://github.com/kaasops/vector-operator @@ -172,7 +185,7 @@ entries: version: 0.0.23 - apiVersion: v2 appVersion: v0.0.22 - created: "2023-12-14T11:16:58.681328372+02:00" + created: "2023-12-15T11:15:46.89012078+02:00" description: A Helm chart to install Vector Operator digest: bf96ddc8ac61e9d6beb8bc763fbf3fa6025d950b29d70d80de6e8a0ea45e0411 home: https://github.com/kaasops/vector-operator @@ -185,7 +198,7 @@ entries: version: 0.0.22 - apiVersion: v2 appVersion: v0.0.21 - created: "2023-12-14T11:16:58.680314102+02:00" + created: "2023-12-15T11:15:46.889193267+02:00" description: A Helm chart to install Vector Operator digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d home: https://github.com/kaasops/vector-operator @@ -198,7 +211,7 @@ entries: version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2023-12-14T11:16:58.679385211+02:00" + created: "2023-12-15T11:15:46.888276433+02:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -211,7 +224,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2023-12-14T11:16:58.678777265+02:00" + created: "2023-12-15T11:15:46.887155662+02:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -224,7 +237,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2023-12-14T11:16:58.677413743+02:00" + created: "2023-12-15T11:15:46.886543471+02:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -237,7 +250,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2023-12-14T11:16:58.676097695+02:00" + created: "2023-12-15T11:15:46.886001966+02:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -250,7 +263,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2023-12-14T11:16:58.675111959+02:00" + created: "2023-12-15T11:15:46.885464525+02:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -263,7 +276,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2023-12-14T11:16:58.674266595+02:00" + created: "2023-12-15T11:15:46.884894187+02:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -276,7 +289,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2023-12-14T11:16:58.673647935+02:00" + created: "2023-12-15T11:15:46.884301711+02:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -289,7 +302,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2023-12-14T11:16:58.673039593+02:00" + created: "2023-12-15T11:15:46.883374962+02:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -302,7 +315,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2023-12-14T11:16:58.672022082+02:00" + created: "2023-12-15T11:15:46.882729875+02:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -315,7 +328,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2023-12-14T11:16:58.671424177+02:00" + created: "2023-12-15T11:15:46.882118113+02:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -328,7 +341,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2023-12-14T11:16:58.670923898+02:00" + created: "2023-12-15T11:15:46.881642748+02:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -341,7 +354,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2023-12-14T11:16:58.698795207+02:00" + created: "2023-12-15T11:15:46.9066745+02:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -354,7 +367,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2023-12-14T11:16:58.697773628+02:00" + created: "2023-12-15T11:15:46.906214251+02:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -367,7 +380,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2023-12-14T11:16:58.696977417+02:00" + created: "2023-12-15T11:15:46.90572244+02:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -380,7 +393,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2023-12-14T11:16:58.696183112+02:00" + created: "2023-12-15T11:15:46.90492736+02:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -393,7 +406,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2023-12-14T11:16:58.670245697+02:00" + created: "2023-12-15T11:15:46.881113576+02:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -404,4 +417,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2023-12-14T11:16:58.669486644+02:00" +generated: "2023-12-15T11:15:46.880481489+02:00" diff --git a/helm/packages/vector-operator-0.0.36.tgz b/helm/packages/vector-operator-0.0.36.tgz new file mode 100644 index 0000000000000000000000000000000000000000..62e3d8ad212bc15119d1effc144a223496ca2469 GIT binary patch literal 32960 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 O0RR66*hP;3@&o`C{^$Du literal 0 HcmV?d00001 From ac81025b6d5415afae95225003dbd077c251602c Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Fri, 15 Dec 2023 12:26:20 +0200 Subject: [PATCH 259/316] reconcile OwnerReferences --- pkg/utils/k8s/k8s.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pkg/utils/k8s/k8s.go b/pkg/utils/k8s/k8s.go index 3e5e54ce..0791e648 100644 --- a/pkg/utils/k8s/k8s.go +++ b/pkg/utils/k8s/k8s.go @@ -76,6 +76,7 @@ func createOrUpdateDeployment(ctx context.Context, desired *appsv1.Deployment, c _, err := controllerutil.CreateOrUpdate(ctx, c, existing, func() error { existing.Labels = desired.Labels existing.Annotations = desired.Annotations + existing.OwnerReferences = desired.OwnerReferences existing.Spec = desired.Spec return nil }) @@ -91,6 +92,7 @@ func createOrUpdateStatefulSet(ctx context.Context, desired *appsv1.StatefulSet, _, err := controllerutil.CreateOrUpdate(ctx, c, existing, func() error { existing.Labels = desired.Labels existing.Annotations = desired.Annotations + existing.OwnerReferences = desired.OwnerReferences existing.Spec = desired.Spec return nil }) @@ -106,6 +108,7 @@ func createOrUpdateDaemonSet(ctx context.Context, desired *appsv1.DaemonSet, c c _, err := controllerutil.CreateOrUpdate(ctx, c, existing, func() error { existing.Labels = desired.Labels existing.Annotations = desired.Annotations + existing.OwnerReferences = desired.OwnerReferences existing.Spec = desired.Spec return nil }) @@ -121,6 +124,7 @@ func createOrUpdateSecret(ctx context.Context, desired *corev1.Secret, c client. _, err := controllerutil.CreateOrUpdate(ctx, c, existing, func() error { existing.Labels = desired.Labels existing.Annotations = desired.Annotations + existing.OwnerReferences = desired.OwnerReferences existing.Data = desired.Data return nil }) @@ -136,6 +140,7 @@ func createOrUpdateService(ctx context.Context, desired *corev1.Service, c clien _, err := controllerutil.CreateOrUpdate(ctx, c, existing, func() error { existing.Labels = desired.Labels existing.Annotations = desired.Annotations + existing.OwnerReferences = desired.OwnerReferences existing.Spec = desired.Spec return nil }) @@ -151,6 +156,7 @@ func createOrUpdateServiceAccount(ctx context.Context, desired *corev1.ServiceAc _, err := controllerutil.CreateOrUpdate(ctx, c, existing, func() error { existing.Labels = desired.Labels existing.Annotations = desired.Annotations + existing.OwnerReferences = desired.OwnerReferences return nil }) if err != nil { @@ -180,6 +186,7 @@ func createOrUpdateClusterRoleBinding(ctx context.Context, desired *rbacv1.Clust _, err := controllerutil.CreateOrUpdate(ctx, c, existing, func() error { existing.Labels = desired.Labels existing.Annotations = desired.Annotations + existing.OwnerReferences = desired.OwnerReferences existing.RoleRef = desired.RoleRef existing.Subjects = desired.Subjects return nil @@ -196,6 +203,7 @@ func createOrUpdatePodMonitor(ctx context.Context, desired *monitorv1.PodMonitor _, err := controllerutil.CreateOrUpdate(ctx, c, existing, func() error { existing.Labels = desired.Labels existing.Annotations = desired.Annotations + existing.OwnerReferences = desired.OwnerReferences existing.Spec = desired.Spec return nil }) @@ -211,6 +219,7 @@ func createOrUpdatePodSrape(ctx context.Context, desired *victoriametricsv1beta1 _, err := controllerutil.CreateOrUpdate(ctx, c, existing, func() error { existing.Labels = desired.Labels existing.Annotations = desired.Annotations + existing.OwnerReferences = desired.OwnerReferences existing.Spec = desired.Spec return nil }) From a63cb249f9c8558afe8ef0624c678e9979cbef0d Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Fri, 15 Dec 2023 12:27:40 +0200 Subject: [PATCH 260/316] update helm release --- helm/charts/vector-operator/Chart.yaml | 4 +- helm/index.yaml | 79 +++++++++++++---------- helm/packages/vector-operator-0.0.37.tgz | Bin 0 -> 32956 bytes 3 files changed, 48 insertions(+), 35 deletions(-) create mode 100644 helm/packages/vector-operator-0.0.37.tgz diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index ee5f2198..dc266009 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.36 +version: 0.0.37 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.0.36" +appVersion: "v0.0.37" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/index.yaml b/helm/index.yaml index 4aede9fb..a328f87c 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.0.37 + created: "2023-12-15T12:27:32.371164207+02:00" + description: A Helm chart to install Vector Operator + digest: 65e10ee46e6855ba95f51e21ca14abc4411191b260c6b96ec72c775e73c1e331 + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.37.tgz + version: 0.0.37 - apiVersion: v2 appVersion: v0.0.36 - created: "2023-12-15T11:15:46.904445897+02:00" + created: "2023-12-15T12:27:32.370202048+02:00" description: A Helm chart to install Vector Operator digest: 972b6b4048b6d17b0616786e49915ef52cb7c9573cfb6eb359b6c19b66eabe31 home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.0.36 - apiVersion: v2 appVersion: v0.0.35 - created: "2023-12-15T11:15:46.903527613+02:00" + created: "2023-12-15T12:27:32.369186979+02:00" description: A Helm chart to install Vector Operator digest: d03ba759c42f2bd8d8f1df71702ee4f26a73b8bc28760ca7254af20d811cee8a home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.0.35 - apiVersion: v2 appVersion: v0.0.34 - created: "2023-12-15T11:15:46.902630521+02:00" + created: "2023-12-15T12:27:32.367894834+02:00" description: A Helm chart to install Vector Operator digest: 01e9d488ee78c8603d821f96344edee568ebcb42049d586de37b8df39b372bd4 home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.0.34 - apiVersion: v2 appVersion: v0.0.33 - created: "2023-12-15T11:15:46.901267841+02:00" + created: "2023-12-15T12:27:32.366888813+02:00" description: A Helm chart to install Vector Operator digest: fcde3c94a0fa6caa5f3d333226c95b7c85ede8489d46277e1222a868ed4ec8c3 home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.0.33 - apiVersion: v2 appVersion: v0.0.32 - created: "2023-12-15T11:15:46.900372099+02:00" + created: "2023-12-15T12:27:32.365612178+02:00" description: A Helm chart to install Vector Operator digest: 26323037ec47f1703ea930a99ab4ec8fb93b44975ce969514ea68d4130017015 home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.0.32 - apiVersion: v2 appVersion: v0.0.31 - created: "2023-12-15T11:15:46.899456182+02:00" + created: "2023-12-15T12:27:32.364488382+02:00" description: A Helm chart to install Vector Operator digest: 45b924c07a825e0f7cd3fb534a6ffd16604790d13be1aff59150c045474754e3 home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.0.31 - apiVersion: v2 appVersion: v0.0.30 - created: "2023-12-15T11:15:46.898191288+02:00" + created: "2023-12-15T12:27:32.363276767+02:00" description: A Helm chart to install Vector Operator digest: 03beda549d15f50325028ea29af5f2065ac0b8adf3078bf7dc1312981aa5e7db home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.0.30 - apiVersion: v2 appVersion: v0.0.29 - created: "2023-12-15T11:15:46.897043786+02:00" + created: "2023-12-15T12:27:32.362425613+02:00" description: A Helm chart to install Vector Operator digest: 0f025fc3a924b37b8c4131c4d8cfa437d2d4e557ab9476ed3e69a00232c7dca6 home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.0.29 - apiVersion: v2 appVersion: v0.0.28 - created: "2023-12-15T11:15:46.896124342+02:00" + created: "2023-12-15T12:27:32.361567662+02:00" description: A Helm chart to install Vector Operator digest: af856d41314313e04f15e7143409a9c564c6ca610b0d2eaec3112add8573e668 home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.0.28 - apiVersion: v2 appVersion: v0.0.27 - created: "2023-12-15T11:15:46.895204298+02:00" + created: "2023-12-15T12:27:32.360678622+02:00" description: A Helm chart to install Vector Operator digest: 631e2ff02bbd7f247cb486494fd2af60c57cc551066a6a3858226551bc1745a4 home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.0.27 - apiVersion: v2 appVersion: v0.0.26 - created: "2023-12-15T11:15:46.893922056+02:00" + created: "2023-12-15T12:27:32.359426653+02:00" description: A Helm chart to install Vector Operator digest: 760a2833f4c1a33466982419b079ff18d996331ebacc40cf93b0f55229cdb7db home: https://github.com/kaasops/vector-operator @@ -146,7 +159,7 @@ entries: version: 0.0.26 - apiVersion: v2 appVersion: v0.0.25 - created: "2023-12-15T11:15:46.893047219+02:00" + created: "2023-12-15T12:27:32.358440184+02:00" description: A Helm chart to install Vector Operator digest: fd22b996b071b6d85740ccf76e85cb640fa717c2620748d206d3f4fdd44cbcc2 home: https://github.com/kaasops/vector-operator @@ -159,7 +172,7 @@ entries: version: 0.0.25 - apiVersion: v2 appVersion: v0.0.24 - created: "2023-12-15T11:15:46.892184999+02:00" + created: "2023-12-15T12:27:32.357595187+02:00" description: A Helm chart to install Vector Operator digest: ea257e60ecde063a0d1ed52ce5e3283245b8f0e2daba58ea3a5adb0ba82d7799 home: https://github.com/kaasops/vector-operator @@ -172,7 +185,7 @@ entries: version: 0.0.24 - apiVersion: v2 appVersion: v0.0.23 - created: "2023-12-15T11:15:46.891083389+02:00" + created: "2023-12-15T12:27:32.356716609+02:00" description: A Helm chart to install Vector Operator digest: 546d202b3b9263f789b88335263191098dfcabd5d8698105f37cad24d56a8ed0 home: https://github.com/kaasops/vector-operator @@ -185,7 +198,7 @@ entries: version: 0.0.23 - apiVersion: v2 appVersion: v0.0.22 - created: "2023-12-15T11:15:46.89012078+02:00" + created: "2023-12-15T12:27:32.355508538+02:00" description: A Helm chart to install Vector Operator digest: bf96ddc8ac61e9d6beb8bc763fbf3fa6025d950b29d70d80de6e8a0ea45e0411 home: https://github.com/kaasops/vector-operator @@ -198,7 +211,7 @@ entries: version: 0.0.22 - apiVersion: v2 appVersion: v0.0.21 - created: "2023-12-15T11:15:46.889193267+02:00" + created: "2023-12-15T12:27:32.354647616+02:00" description: A Helm chart to install Vector Operator digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d home: https://github.com/kaasops/vector-operator @@ -211,7 +224,7 @@ entries: version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2023-12-15T11:15:46.888276433+02:00" + created: "2023-12-15T12:27:32.353794449+02:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -224,7 +237,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2023-12-15T11:15:46.887155662+02:00" + created: "2023-12-15T12:27:32.352938763+02:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -237,7 +250,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2023-12-15T11:15:46.886543471+02:00" + created: "2023-12-15T12:27:32.352206304+02:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -250,7 +263,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2023-12-15T11:15:46.886001966+02:00" + created: "2023-12-15T12:27:32.351561407+02:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -263,7 +276,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2023-12-15T11:15:46.885464525+02:00" + created: "2023-12-15T12:27:32.350937083+02:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -276,7 +289,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2023-12-15T11:15:46.884894187+02:00" + created: "2023-12-15T12:27:32.350321686+02:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -289,7 +302,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2023-12-15T11:15:46.884301711+02:00" + created: "2023-12-15T12:27:32.349779199+02:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -302,7 +315,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2023-12-15T11:15:46.883374962+02:00" + created: "2023-12-15T12:27:32.349228269+02:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -315,7 +328,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2023-12-15T11:15:46.882729875+02:00" + created: "2023-12-15T12:27:32.348272819+02:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -328,7 +341,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2023-12-15T11:15:46.882118113+02:00" + created: "2023-12-15T12:27:32.347682474+02:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -341,7 +354,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2023-12-15T11:15:46.881642748+02:00" + created: "2023-12-15T12:27:32.34720873+02:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -354,7 +367,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2023-12-15T11:15:46.9066745+02:00" + created: "2023-12-15T12:27:32.373121981+02:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -367,7 +380,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2023-12-15T11:15:46.906214251+02:00" + created: "2023-12-15T12:27:32.372650157+02:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -380,7 +393,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2023-12-15T11:15:46.90572244+02:00" + created: "2023-12-15T12:27:32.372185663+02:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -393,7 +406,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2023-12-15T11:15:46.90492736+02:00" + created: "2023-12-15T12:27:32.371679529+02:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -406,7 +419,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2023-12-15T11:15:46.881113576+02:00" + created: "2023-12-15T12:27:32.346735759+02:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -417,4 +430,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2023-12-15T11:15:46.880481489+02:00" +generated: "2023-12-15T12:27:32.346160039+02:00" diff --git a/helm/packages/vector-operator-0.0.37.tgz b/helm/packages/vector-operator-0.0.37.tgz new file mode 100644 index 0000000000000000000000000000000000000000..8260bd96eab940ef44265af6b8b582bc6530ebbb GIT binary patch literal 32956 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 N|Nnvm%-8_(1OU9B0v!MV literal 0 HcmV?d00001 From 949418ca215e605b52cc09c82db09b790507714b Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Fri, 15 Dec 2023 14:23:10 +0200 Subject: [PATCH 261/316] disable require-healthy --- pkg/config/configcheck/configcheck_pod.go | 2 +- pkg/vector/vectoragent/vectoragent_daemonset.go | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pkg/config/configcheck/configcheck_pod.go b/pkg/config/configcheck/configcheck_pod.go index 51f8ff55..bf78483e 100644 --- a/pkg/config/configcheck/configcheck_pod.go +++ b/pkg/config/configcheck/configcheck_pod.go @@ -47,7 +47,7 @@ func (cc *ConfigCheck) createVectorConfigCheckPod() *corev1.Pod { Name: "config-check", Image: cc.Image, Resources: cc.Resources, - Args: []string{"validate", "/etc/vector/*.json"}, + Args: []string{"--require-healthy=false", "validate", "/etc/vector/*.json"}, Env: cc.generateVectorConfigCheckEnvs(), SecurityContext: cc.ContainerSecurityContext, VolumeMounts: cc.generateVectorConfigCheckVolumeMounts(), diff --git a/pkg/vector/vectoragent/vectoragent_daemonset.go b/pkg/vector/vectoragent/vectoragent_daemonset.go index cff55e2e..780bfd1a 100644 --- a/pkg/vector/vectoragent/vectoragent_daemonset.go +++ b/pkg/vector/vectoragent/vectoragent_daemonset.go @@ -204,9 +204,8 @@ func (ctrl *Controller) VectorAgentContainer() *corev1.Container { return &corev1.Container{ Name: ctrl.getNameVectorAgent(), Image: ctrl.Vector.Spec.Agent.Image, - Args: []string{"--config-dir", "/etc/vector", "--watch-config", "--require-healthy", "false"}, - // Command: []string{"/bin/sleep", "1000000000000000"}, - Env: ctrl.generateVectorAgentEnvs(), + Args: []string{"--config-dir", "/etc/vector", "--watch-config"}, + Env: ctrl.generateVectorAgentEnvs(), Ports: []corev1.ContainerPort{ { Name: "prom-exporter", From a983c91fff102cd66641679c31772139de954aa4 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Fri, 15 Dec 2023 14:23:45 +0200 Subject: [PATCH 262/316] update helm --- helm/charts/vector-operator/Chart.yaml | 4 +- helm/index.yaml | 81 +++++++++++++---------- helm/packages/vector-operator-0.0.38.tgz | Bin 0 -> 32957 bytes 3 files changed, 49 insertions(+), 36 deletions(-) create mode 100644 helm/packages/vector-operator-0.0.38.tgz diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index dc266009..e3d3a647 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.37 +version: 0.0.38 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.0.37" +appVersion: "v0.0.38" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/index.yaml b/helm/index.yaml index a328f87c..c3d4990e 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.0.38 + created: "2023-12-15T14:23:40.585872968+02:00" + description: A Helm chart to install Vector Operator + digest: 565b148184400900f5572b06ceebaac6340af5e5fd1122b308bdbfcbc2d2040a + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.38.tgz + version: 0.0.38 - apiVersion: v2 appVersion: v0.0.37 - created: "2023-12-15T12:27:32.371164207+02:00" + created: "2023-12-15T14:23:40.584850945+02:00" description: A Helm chart to install Vector Operator digest: 65e10ee46e6855ba95f51e21ca14abc4411191b260c6b96ec72c775e73c1e331 home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.0.37 - apiVersion: v2 appVersion: v0.0.36 - created: "2023-12-15T12:27:32.370202048+02:00" + created: "2023-12-15T14:23:40.583008067+02:00" description: A Helm chart to install Vector Operator digest: 972b6b4048b6d17b0616786e49915ef52cb7c9573cfb6eb359b6c19b66eabe31 home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.0.36 - apiVersion: v2 appVersion: v0.0.35 - created: "2023-12-15T12:27:32.369186979+02:00" + created: "2023-12-15T14:23:40.581838218+02:00" description: A Helm chart to install Vector Operator digest: d03ba759c42f2bd8d8f1df71702ee4f26a73b8bc28760ca7254af20d811cee8a home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.0.35 - apiVersion: v2 appVersion: v0.0.34 - created: "2023-12-15T12:27:32.367894834+02:00" + created: "2023-12-15T14:23:40.580876081+02:00" description: A Helm chart to install Vector Operator digest: 01e9d488ee78c8603d821f96344edee568ebcb42049d586de37b8df39b372bd4 home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.0.34 - apiVersion: v2 appVersion: v0.0.33 - created: "2023-12-15T12:27:32.366888813+02:00" + created: "2023-12-15T14:23:40.579959143+02:00" description: A Helm chart to install Vector Operator digest: fcde3c94a0fa6caa5f3d333226c95b7c85ede8489d46277e1222a868ed4ec8c3 home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.0.33 - apiVersion: v2 appVersion: v0.0.32 - created: "2023-12-15T12:27:32.365612178+02:00" + created: "2023-12-15T14:23:40.578705373+02:00" description: A Helm chart to install Vector Operator digest: 26323037ec47f1703ea930a99ab4ec8fb93b44975ce969514ea68d4130017015 home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.0.32 - apiVersion: v2 appVersion: v0.0.31 - created: "2023-12-15T12:27:32.364488382+02:00" + created: "2023-12-15T14:23:40.577809702+02:00" description: A Helm chart to install Vector Operator digest: 45b924c07a825e0f7cd3fb534a6ffd16604790d13be1aff59150c045474754e3 home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.0.31 - apiVersion: v2 appVersion: v0.0.30 - created: "2023-12-15T12:27:32.363276767+02:00" + created: "2023-12-15T14:23:40.576935861+02:00" description: A Helm chart to install Vector Operator digest: 03beda549d15f50325028ea29af5f2065ac0b8adf3078bf7dc1312981aa5e7db home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.0.30 - apiVersion: v2 appVersion: v0.0.29 - created: "2023-12-15T12:27:32.362425613+02:00" + created: "2023-12-15T14:23:40.576061107+02:00" description: A Helm chart to install Vector Operator digest: 0f025fc3a924b37b8c4131c4d8cfa437d2d4e557ab9476ed3e69a00232c7dca6 home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.0.29 - apiVersion: v2 appVersion: v0.0.28 - created: "2023-12-15T12:27:32.361567662+02:00" + created: "2023-12-15T14:23:40.57454591+02:00" description: A Helm chart to install Vector Operator digest: af856d41314313e04f15e7143409a9c564c6ca610b0d2eaec3112add8573e668 home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.0.28 - apiVersion: v2 appVersion: v0.0.27 - created: "2023-12-15T12:27:32.360678622+02:00" + created: "2023-12-15T14:23:40.573568119+02:00" description: A Helm chart to install Vector Operator digest: 631e2ff02bbd7f247cb486494fd2af60c57cc551066a6a3858226551bc1745a4 home: https://github.com/kaasops/vector-operator @@ -146,7 +159,7 @@ entries: version: 0.0.27 - apiVersion: v2 appVersion: v0.0.26 - created: "2023-12-15T12:27:32.359426653+02:00" + created: "2023-12-15T14:23:40.57254696+02:00" description: A Helm chart to install Vector Operator digest: 760a2833f4c1a33466982419b079ff18d996331ebacc40cf93b0f55229cdb7db home: https://github.com/kaasops/vector-operator @@ -159,7 +172,7 @@ entries: version: 0.0.26 - apiVersion: v2 appVersion: v0.0.25 - created: "2023-12-15T12:27:32.358440184+02:00" + created: "2023-12-15T14:23:40.57166925+02:00" description: A Helm chart to install Vector Operator digest: fd22b996b071b6d85740ccf76e85cb640fa717c2620748d206d3f4fdd44cbcc2 home: https://github.com/kaasops/vector-operator @@ -172,7 +185,7 @@ entries: version: 0.0.25 - apiVersion: v2 appVersion: v0.0.24 - created: "2023-12-15T12:27:32.357595187+02:00" + created: "2023-12-15T14:23:40.570295984+02:00" description: A Helm chart to install Vector Operator digest: ea257e60ecde063a0d1ed52ce5e3283245b8f0e2daba58ea3a5adb0ba82d7799 home: https://github.com/kaasops/vector-operator @@ -185,7 +198,7 @@ entries: version: 0.0.24 - apiVersion: v2 appVersion: v0.0.23 - created: "2023-12-15T12:27:32.356716609+02:00" + created: "2023-12-15T14:23:40.569413488+02:00" description: A Helm chart to install Vector Operator digest: 546d202b3b9263f789b88335263191098dfcabd5d8698105f37cad24d56a8ed0 home: https://github.com/kaasops/vector-operator @@ -198,7 +211,7 @@ entries: version: 0.0.23 - apiVersion: v2 appVersion: v0.0.22 - created: "2023-12-15T12:27:32.355508538+02:00" + created: "2023-12-15T14:23:40.568547106+02:00" description: A Helm chart to install Vector Operator digest: bf96ddc8ac61e9d6beb8bc763fbf3fa6025d950b29d70d80de6e8a0ea45e0411 home: https://github.com/kaasops/vector-operator @@ -211,7 +224,7 @@ entries: version: 0.0.22 - apiVersion: v2 appVersion: v0.0.21 - created: "2023-12-15T12:27:32.354647616+02:00" + created: "2023-12-15T14:23:40.567272611+02:00" description: A Helm chart to install Vector Operator digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d home: https://github.com/kaasops/vector-operator @@ -224,7 +237,7 @@ entries: version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2023-12-15T12:27:32.353794449+02:00" + created: "2023-12-15T14:23:40.566363557+02:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -237,7 +250,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2023-12-15T12:27:32.352938763+02:00" + created: "2023-12-15T14:23:40.565826735+02:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -250,7 +263,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2023-12-15T12:27:32.352206304+02:00" + created: "2023-12-15T14:23:40.565287346+02:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -263,7 +276,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2023-12-15T12:27:32.351561407+02:00" + created: "2023-12-15T14:23:40.564726157+02:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -276,7 +289,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2023-12-15T12:27:32.350937083+02:00" + created: "2023-12-15T14:23:40.563704327+02:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -289,7 +302,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2023-12-15T12:27:32.350321686+02:00" + created: "2023-12-15T14:23:40.56309653+02:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -302,7 +315,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2023-12-15T12:27:32.349779199+02:00" + created: "2023-12-15T14:23:40.562528401+02:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -315,7 +328,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2023-12-15T12:27:32.349228269+02:00" + created: "2023-12-15T14:23:40.561963197+02:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -328,7 +341,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2023-12-15T12:27:32.348272819+02:00" + created: "2023-12-15T14:23:40.561417543+02:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -341,7 +354,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2023-12-15T12:27:32.347682474+02:00" + created: "2023-12-15T14:23:40.560838806+02:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -354,7 +367,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2023-12-15T12:27:32.34720873+02:00" + created: "2023-12-15T14:23:40.559844542+02:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -367,7 +380,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2023-12-15T12:27:32.373121981+02:00" + created: "2023-12-15T14:23:40.587924675+02:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -380,7 +393,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2023-12-15T12:27:32.372650157+02:00" + created: "2023-12-15T14:23:40.587252028+02:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -393,7 +406,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2023-12-15T12:27:32.372185663+02:00" + created: "2023-12-15T14:23:40.586794342+02:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -406,7 +419,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2023-12-15T12:27:32.371679529+02:00" + created: "2023-12-15T14:23:40.586338615+02:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -419,7 +432,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2023-12-15T12:27:32.346735759+02:00" + created: "2023-12-15T14:23:40.559189756+02:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -430,4 +443,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2023-12-15T12:27:32.346160039+02:00" +generated: "2023-12-15T14:23:40.558446878+02:00" diff --git a/helm/packages/vector-operator-0.0.38.tgz b/helm/packages/vector-operator-0.0.38.tgz new file mode 100644 index 0000000000000000000000000000000000000000..cb7624f45e2afd653da1cf3b3263d71a8f7ba3d1 GIT binary patch literal 32957 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 literal 0 HcmV?d00001 From 52c7390e0497dfe3869ad92a1f852cb9b1c6903a Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Mon, 18 Dec 2023 11:11:25 +0200 Subject: [PATCH 263/316] vector configcheck configurable --- api/v1alpha1/vector_types.go | 1 + .../observability.kaasops.io_vectors.yaml | 2 + .../observability_v1alpha1_vector.yaml | 10 +++++ controllers/vector_controller.go | 38 ++++++++++--------- 4 files changed, 33 insertions(+), 18 deletions(-) diff --git a/api/v1alpha1/vector_types.go b/api/v1alpha1/vector_types.go index 7b48af4c..22b89487 100644 --- a/api/v1alpha1/vector_types.go +++ b/api/v1alpha1/vector_types.go @@ -142,6 +142,7 @@ type ApiSpec struct { // ConfigCheck is the Schema for control params for ConfigCheck pods type ConfigCheck struct { + Disabled bool `json:"disabled,omitempty"` // Image - docker image settings for Vector Agent // if no specified operator uses default config version // +optional diff --git a/config/crd/bases/observability.kaasops.io_vectors.yaml b/config/crd/bases/observability.kaasops.io_vectors.yaml index e9f6b3a0..48461772 100644 --- a/config/crd/bases/observability.kaasops.io_vectors.yaml +++ b/config/crd/bases/observability.kaasops.io_vectors.yaml @@ -1837,6 +1837,8 @@ spec: type: array type: object type: object + disabled: + type: boolean image: description: Image - docker image settings for Vector Agent if no specified operator uses default config version diff --git a/config/samples/observability_v1alpha1_vector.yaml b/config/samples/observability_v1alpha1_vector.yaml index 48008cc9..6322c289 100644 --- a/config/samples/observability_v1alpha1_vector.yaml +++ b/config/samples/observability_v1alpha1_vector.yaml @@ -10,3 +10,13 @@ spec: api: enabled: true healthcheck: true + configCheck: + disabled: true + resources: + limits: + cpu: 500m + memory: 800Mi + requests: + cpu: 10m + memory: 20Mi + diff --git a/controllers/vector_controller.go b/controllers/vector_controller.go index a4c7cbab..719cf39f 100644 --- a/controllers/vector_controller.go +++ b/controllers/vector_controller.go @@ -192,28 +192,30 @@ func (r *VectorReconciler) createOrUpdateVector(ctx context.Context, client clie } cfgHash := hash.Get(byteConfig) - if vaCtrl.Vector.Status.LastAppliedConfigHash == nil || *vaCtrl.Vector.Status.LastAppliedConfigHash != cfgHash { - configCheck := configcheck.New( - byteConfig, - vaCtrl.Client, - vaCtrl.ClientSet, - vaCtrl.Vector, - r.ConfigCheckTimeout, - ) - configCheck.Initiator = configcheck.ConfigCheckInitiatorVector - reason, err := configCheck.Run(ctx) - if err != nil { - if errors.Is(err, configcheck.ValidationError) { - if err := vaCtrl.SetFailedStatus(ctx, reason); err != nil { - return ctrl.Result{}, err + if !vaCtrl.Vector.Spec.Agent.ConfigCheck.Disabled { + if vaCtrl.Vector.Status.LastAppliedConfigHash == nil || *vaCtrl.Vector.Status.LastAppliedConfigHash != cfgHash { + configCheck := configcheck.New( + byteConfig, + vaCtrl.Client, + vaCtrl.ClientSet, + vaCtrl.Vector, + r.ConfigCheckTimeout, + ) + configCheck.Initiator = configcheck.ConfigCheckInitiatorVector + reason, err := configCheck.Run(ctx) + if err != nil { + if errors.Is(err, configcheck.ValidationError) { + if err := vaCtrl.SetFailedStatus(ctx, reason); err != nil { + return ctrl.Result{}, err + } + log.Error(err, "Invalid config") + return ctrl.Result{}, nil } - log.Error(err, "Invalid config") - return ctrl.Result{}, nil + return ctrl.Result{}, err } - return ctrl.Result{}, err } - } + vaCtrl.Config = byteConfig // Start Reconcile Vector Agent From 7009ec6c96c67e1ea00ccb4c8c7f5c9630c39ab3 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Mon, 18 Dec 2023 11:12:32 +0200 Subject: [PATCH 264/316] update helm release --- helm/charts/vector-operator/Chart.yaml | 4 +- .../observability.kaasops.io_vectors.yaml | 2 + helm/index.yaml | 83 ++++++++++-------- helm/packages/vector-operator-0.0.39.tgz | Bin 0 -> 32965 bytes 4 files changed, 52 insertions(+), 37 deletions(-) create mode 100644 helm/packages/vector-operator-0.0.39.tgz diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index e3d3a647..19e956c4 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.38 +version: 0.0.39 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.0.38" +appVersion: "v0.0.39" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml index e9f6b3a0..48461772 100644 --- a/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml +++ b/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml @@ -1837,6 +1837,8 @@ spec: type: array type: object type: object + disabled: + type: boolean image: description: Image - docker image settings for Vector Agent if no specified operator uses default config version diff --git a/helm/index.yaml b/helm/index.yaml index c3d4990e..09c94426 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.0.39 + created: "2023-12-18T11:12:18.341448445+02:00" + description: A Helm chart to install Vector Operator + digest: e1de38c869bf896bfb9f5f615c329d3ccce3bd91cb64bb6fa1c783a40ea290a2 + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.39.tgz + version: 0.0.39 - apiVersion: v2 appVersion: v0.0.38 - created: "2023-12-15T14:23:40.585872968+02:00" + created: "2023-12-18T11:12:18.340555026+02:00" description: A Helm chart to install Vector Operator digest: 565b148184400900f5572b06ceebaac6340af5e5fd1122b308bdbfcbc2d2040a home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.0.38 - apiVersion: v2 appVersion: v0.0.37 - created: "2023-12-15T14:23:40.584850945+02:00" + created: "2023-12-18T11:12:18.339646199+02:00" description: A Helm chart to install Vector Operator digest: 65e10ee46e6855ba95f51e21ca14abc4411191b260c6b96ec72c775e73c1e331 home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.0.37 - apiVersion: v2 appVersion: v0.0.36 - created: "2023-12-15T14:23:40.583008067+02:00" + created: "2023-12-18T11:12:18.33825308+02:00" description: A Helm chart to install Vector Operator digest: 972b6b4048b6d17b0616786e49915ef52cb7c9573cfb6eb359b6c19b66eabe31 home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.0.36 - apiVersion: v2 appVersion: v0.0.35 - created: "2023-12-15T14:23:40.581838218+02:00" + created: "2023-12-18T11:12:18.337102292+02:00" description: A Helm chart to install Vector Operator digest: d03ba759c42f2bd8d8f1df71702ee4f26a73b8bc28760ca7254af20d811cee8a home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.0.35 - apiVersion: v2 appVersion: v0.0.34 - created: "2023-12-15T14:23:40.580876081+02:00" + created: "2023-12-18T11:12:18.336129065+02:00" description: A Helm chart to install Vector Operator digest: 01e9d488ee78c8603d821f96344edee568ebcb42049d586de37b8df39b372bd4 home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.0.34 - apiVersion: v2 appVersion: v0.0.33 - created: "2023-12-15T14:23:40.579959143+02:00" + created: "2023-12-18T11:12:18.335217127+02:00" description: A Helm chart to install Vector Operator digest: fcde3c94a0fa6caa5f3d333226c95b7c85ede8489d46277e1222a868ed4ec8c3 home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.0.33 - apiVersion: v2 appVersion: v0.0.32 - created: "2023-12-15T14:23:40.578705373+02:00" + created: "2023-12-18T11:12:18.333824269+02:00" description: A Helm chart to install Vector Operator digest: 26323037ec47f1703ea930a99ab4ec8fb93b44975ce969514ea68d4130017015 home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.0.32 - apiVersion: v2 appVersion: v0.0.31 - created: "2023-12-15T14:23:40.577809702+02:00" + created: "2023-12-18T11:12:18.332792168+02:00" description: A Helm chart to install Vector Operator digest: 45b924c07a825e0f7cd3fb534a6ffd16604790d13be1aff59150c045474754e3 home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.0.31 - apiVersion: v2 appVersion: v0.0.30 - created: "2023-12-15T14:23:40.576935861+02:00" + created: "2023-12-18T11:12:18.33183807+02:00" description: A Helm chart to install Vector Operator digest: 03beda549d15f50325028ea29af5f2065ac0b8adf3078bf7dc1312981aa5e7db home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.0.30 - apiVersion: v2 appVersion: v0.0.29 - created: "2023-12-15T14:23:40.576061107+02:00" + created: "2023-12-18T11:12:18.330651478+02:00" description: A Helm chart to install Vector Operator digest: 0f025fc3a924b37b8c4131c4d8cfa437d2d4e557ab9476ed3e69a00232c7dca6 home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.0.29 - apiVersion: v2 appVersion: v0.0.28 - created: "2023-12-15T14:23:40.57454591+02:00" + created: "2023-12-18T11:12:18.328898431+02:00" description: A Helm chart to install Vector Operator digest: af856d41314313e04f15e7143409a9c564c6ca610b0d2eaec3112add8573e668 home: https://github.com/kaasops/vector-operator @@ -146,7 +159,7 @@ entries: version: 0.0.28 - apiVersion: v2 appVersion: v0.0.27 - created: "2023-12-15T14:23:40.573568119+02:00" + created: "2023-12-18T11:12:18.327693983+02:00" description: A Helm chart to install Vector Operator digest: 631e2ff02bbd7f247cb486494fd2af60c57cc551066a6a3858226551bc1745a4 home: https://github.com/kaasops/vector-operator @@ -159,7 +172,7 @@ entries: version: 0.0.27 - apiVersion: v2 appVersion: v0.0.26 - created: "2023-12-15T14:23:40.57254696+02:00" + created: "2023-12-18T11:12:18.326768591+02:00" description: A Helm chart to install Vector Operator digest: 760a2833f4c1a33466982419b079ff18d996331ebacc40cf93b0f55229cdb7db home: https://github.com/kaasops/vector-operator @@ -172,7 +185,7 @@ entries: version: 0.0.26 - apiVersion: v2 appVersion: v0.0.25 - created: "2023-12-15T14:23:40.57166925+02:00" + created: "2023-12-18T11:12:18.325725899+02:00" description: A Helm chart to install Vector Operator digest: fd22b996b071b6d85740ccf76e85cb640fa717c2620748d206d3f4fdd44cbcc2 home: https://github.com/kaasops/vector-operator @@ -185,7 +198,7 @@ entries: version: 0.0.25 - apiVersion: v2 appVersion: v0.0.24 - created: "2023-12-15T14:23:40.570295984+02:00" + created: "2023-12-18T11:12:18.324209369+02:00" description: A Helm chart to install Vector Operator digest: ea257e60ecde063a0d1ed52ce5e3283245b8f0e2daba58ea3a5adb0ba82d7799 home: https://github.com/kaasops/vector-operator @@ -198,7 +211,7 @@ entries: version: 0.0.24 - apiVersion: v2 appVersion: v0.0.23 - created: "2023-12-15T14:23:40.569413488+02:00" + created: "2023-12-18T11:12:18.323096236+02:00" description: A Helm chart to install Vector Operator digest: 546d202b3b9263f789b88335263191098dfcabd5d8698105f37cad24d56a8ed0 home: https://github.com/kaasops/vector-operator @@ -211,7 +224,7 @@ entries: version: 0.0.23 - apiVersion: v2 appVersion: v0.0.22 - created: "2023-12-15T14:23:40.568547106+02:00" + created: "2023-12-18T11:12:18.322021757+02:00" description: A Helm chart to install Vector Operator digest: bf96ddc8ac61e9d6beb8bc763fbf3fa6025d950b29d70d80de6e8a0ea45e0411 home: https://github.com/kaasops/vector-operator @@ -224,7 +237,7 @@ entries: version: 0.0.22 - apiVersion: v2 appVersion: v0.0.21 - created: "2023-12-15T14:23:40.567272611+02:00" + created: "2023-12-18T11:12:18.320958335+02:00" description: A Helm chart to install Vector Operator digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d home: https://github.com/kaasops/vector-operator @@ -237,7 +250,7 @@ entries: version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2023-12-15T14:23:40.566363557+02:00" + created: "2023-12-18T11:12:18.319440781+02:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -250,7 +263,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2023-12-15T14:23:40.565826735+02:00" + created: "2023-12-18T11:12:18.318779315+02:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -263,7 +276,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2023-12-15T14:23:40.565287346+02:00" + created: "2023-12-18T11:12:18.318202989+02:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -276,7 +289,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2023-12-15T14:23:40.564726157+02:00" + created: "2023-12-18T11:12:18.31761794+02:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -289,7 +302,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2023-12-15T14:23:40.563704327+02:00" + created: "2023-12-18T11:12:18.317053791+02:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -302,7 +315,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2023-12-15T14:23:40.56309653+02:00" + created: "2023-12-18T11:12:18.316303122+02:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -315,7 +328,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2023-12-15T14:23:40.562528401+02:00" + created: "2023-12-18T11:12:18.315101925+02:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -328,7 +341,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2023-12-15T14:23:40.561963197+02:00" + created: "2023-12-18T11:12:18.314219362+02:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -341,7 +354,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2023-12-15T14:23:40.561417543+02:00" + created: "2023-12-18T11:12:18.313292106+02:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -354,7 +367,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2023-12-15T14:23:40.560838806+02:00" + created: "2023-12-18T11:12:18.312397344+02:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -367,7 +380,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2023-12-15T14:23:40.559844542+02:00" + created: "2023-12-18T11:12:18.311589754+02:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -380,7 +393,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2023-12-15T14:23:40.587924675+02:00" + created: "2023-12-18T11:12:18.343526429+02:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -393,7 +406,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2023-12-15T14:23:40.587252028+02:00" + created: "2023-12-18T11:12:18.342966812+02:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -406,7 +419,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2023-12-15T14:23:40.586794342+02:00" + created: "2023-12-18T11:12:18.342419242+02:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -419,7 +432,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2023-12-15T14:23:40.586338615+02:00" + created: "2023-12-18T11:12:18.341924788+02:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -432,7 +445,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2023-12-15T14:23:40.559189756+02:00" + created: "2023-12-18T11:12:18.31047193+02:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -443,4 +456,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2023-12-15T14:23:40.558446878+02:00" +generated: "2023-12-18T11:12:18.309795334+02:00" diff --git a/helm/packages/vector-operator-0.0.39.tgz b/helm/packages/vector-operator-0.0.39.tgz new file mode 100644 index 0000000000000000000000000000000000000000..0c7468360c2a9e1934064ddf17b796bad2ca4204 GIT binary patch literal 32965 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 literal 0 HcmV?d00001 From ccd74cb34d462c4a9883790bd26c6b6f43c3cdf0 Mon Sep 17 00:00:00 2001 From: Vladimir <31961982+zvlb@users.noreply.github.com> Date: Wed, 10 Jan 2024 12:32:07 +0200 Subject: [PATCH 265/316] Add access to podmonitor in quick-start quide --- docs/quick-start.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/quick-start.md b/docs/quick-start.md index d737e642..8a76173b 100644 --- a/docs/quick-start.md +++ b/docs/quick-start.md @@ -195,6 +195,12 @@ rules: - get - patch - update +- apiGroups: + - monitoring.coreos.com + resources: + - podmonitors + verbs: + - '*' EOF # Create ClusterRoleBinding for Vector Operator @@ -388,4 +394,4 @@ kubectl delete clusterrolebinding vector-operator kubectl delete -f config/crd/bases/observability.kaasops.io_vectors.yaml kubectl delete -f config/crd/bases/observability.kaasops.io_vectorpipelines.yaml kubectl delete -f config/crd/bases/observability.kaasops.io_clustervectorpipelines.yaml -``` \ No newline at end of file +``` From 7ae26c312035a8d8e8e7063104c5e12ee2cbb264 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Tue, 2 Apr 2024 12:45:34 +0300 Subject: [PATCH 266/316] added annotations --- api/v1alpha1/vector_types.go | 27 ++++++++--------- .../observability.kaasops.io_vectors.yaml | 28 +++++++++++++---- pkg/config/configcheck/configcheck.go | 6 ++++ pkg/config/configcheck/configcheck_pod.go | 8 +++-- pkg/utils/k8s/k8s.go | 30 ++++++++++++------- pkg/vector/vectoragent/vectoragent_config.go | 3 +- .../vectoragent/vectoragent_controller.go | 7 ++++- .../vectoragent/vectoragent_daemonset.go | 5 ++-- .../vectoragent/vectoragent_podmonitor.go | 3 +- pkg/vector/vectoragent/vectoragent_rbac.go | 9 ++++-- pkg/vector/vectoragent/vectoragent_service.go | 3 +- 11 files changed, 88 insertions(+), 41 deletions(-) diff --git a/api/v1alpha1/vector_types.go b/api/v1alpha1/vector_types.go index 22b89487..3b57f662 100644 --- a/api/v1alpha1/vector_types.go +++ b/api/v1alpha1/vector_types.go @@ -56,6 +56,9 @@ type VectorAgent struct { ImagePullSecrets []v1.LocalObjectReference `json:"imagePullSecrets,omitempty"` // ImagePullPolicy of pods ImagePullPolicy v1.PullPolicy `json:"imagePullPolicy,omitempty"` + // Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + // +optional + Annotations map[string]string `json:"annotations,omitempty"` // Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ // if not specified - default setting will be used // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Resources",xDescriptors="urn:alm:descriptor:com.tectonic.ui:resourceRequirements" @@ -99,32 +102,30 @@ type VectorAgent struct { HostNetwork bool `json:"hostNetwork,omitempty"` // Env that will be added to Vector pod Env []v1.EnvVar `json:"env,omitempty"` - - DataDir string `json:"dataDir,omitempty"` - Api ApiSpec `json:"api,omitempty"` - + // The directory used for persisting Vector state, such as on-disk buffers, file checkpoints, and more. Please make sure the Vector project has write permissions to this directory. + // https://vector.dev/docs/reference/configuration/global-options/#data_dir + DataDir string `json:"dataDir,omitempty"` + // Vector API params. Allows to interact with a running Vector instance. + // https://vector.dev/docs/reference/api/ + Api ApiSpec `json:"api,omitempty"` // Enable internal metrics exporter // +optional InternalMetrics bool `json:"internalMetrics,omitempty"` - // List of volumes that can be mounted by containers belonging to the pod. // +optional Volumes []v1.Volume `json:"volumes,omitempty"` - // Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. // +optional ReadinessProbe *v1.Probe `json:"readinessProbe,omitempty"` - // Periodic probe of container liveness. Container will be restarted if the probe fails. // +optional LivenessProbe *v1.Probe `json:"livenessProbe,omitempty"` - // Pod volumes to mount into the container's filesystem. // +optional VolumeMounts []v1.VolumeMount `json:"volumeMounts,omitempty"` - + // Control params for ConfigCheck pods ConfigCheck ConfigCheck `json:"configCheck,omitempty"` - // Compress config file + // Compress config file to fix: metadata.annotations: Too long: must have at most 262144 characters CompressConfigFile bool `json:"compressConfigFile,omitempty"` ConfigReloaderImage string `json:"configReloaderImage,omitempty"` ConfigReloaderResources v1.ResourceRequirements `json:"configReloaderResources,omitempty"` @@ -158,11 +159,9 @@ type ConfigCheck struct { // Tolerations If specified, the pod's tolerations. // +optional Tolerations *[]v1.Toleration `json:"tolerations,omitempty"` - // SecurityContext holds pod-level security attributes and common container settings. - // This defaults to the default PodSecurityContext. - // +optional - // Tolerations If specified, the pod's tolerations. + // Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. // +optional + Annotations map[string]string `json:"annotations,omitempty"` } // VectorAggregator is the Schema for the Vector Aggregator diff --git a/config/crd/bases/observability.kaasops.io_vectors.yaml b/config/crd/bases/observability.kaasops.io_vectors.yaml index 48461772..2509005f 100644 --- a/config/crd/bases/observability.kaasops.io_vectors.yaml +++ b/config/crd/bases/observability.kaasops.io_vectors.yaml @@ -914,9 +914,16 @@ spec: type: array type: object type: object + annotations: + additionalProperties: + type: string + description: Annotations is an unstructured key value map stored + with a resource that may be set by external tools to store and + retrieve arbitrary metadata. + type: object api: - description: ApiSpec is the Schema for the Vector Agent GraphQL - API - https://vector.dev/docs/reference/api/ + description: Vector API params. Allows to interact with a running + Vector instance. https://vector.dev/docs/reference/api/ properties: enabled: type: boolean @@ -929,11 +936,11 @@ spec: type: boolean type: object compressConfigFile: - description: Compress config file + description: 'Compress config file to fix: metadata.annotations: + Too long: must have at most 262144 characters' type: boolean configCheck: - description: ConfigCheck is the Schema for control params for - ConfigCheck pods + description: Control params for ConfigCheck pods properties: affinity: description: Affinity If specified, the pod's scheduling constraints. @@ -1837,6 +1844,13 @@ spec: type: array type: object type: object + annotations: + additionalProperties: + type: string + description: Annotations is an unstructured key value map + stored with a resource that may be set by external tools + to store and retrieve arbitrary metadata. + type: object disabled: type: boolean image: @@ -2116,6 +2130,10 @@ spec: type: object type: object dataDir: + description: The directory used for persisting Vector state, such + as on-disk buffers, file checkpoints, and more. Please make + sure the Vector project has write permissions to this directory. + https://vector.dev/docs/reference/configuration/global-options/#data_dir type: string env: description: Env that will be added to Vector pod diff --git a/pkg/config/configcheck/configcheck.go b/pkg/config/configcheck/configcheck.go index ee7a48ee..412becfc 100644 --- a/pkg/config/configcheck/configcheck.go +++ b/pkg/config/configcheck/configcheck.go @@ -59,6 +59,7 @@ type ConfigCheck struct { ConfigReloaderImage string ConfigReloaderResources corev1.ResourceRequirements ConfigCheckTimeout time.Duration + Annotations map[string]string } func New( @@ -103,6 +104,7 @@ func New( ConfigReloaderImage: va.Spec.Agent.ConfigReloaderImage, ConfigReloaderResources: va.Spec.Agent.ConfigReloaderResources, ConfigCheckTimeout: timeout, + Annotations: va.Spec.Agent.ConfigCheck.Annotations, } } @@ -170,6 +172,10 @@ func labelsForVectorConfigCheck() map[string]string { } } +func (cc *ConfigCheck) annotationsForVectorConfigCheck() map[string]string { + return cc.Annotations +} + func (cc *ConfigCheck) getNameVectorConfigCheck() string { n := "configcheck" + "-" + cc.Name + "-" + cc.Hash diff --git a/pkg/config/configcheck/configcheck_pod.go b/pkg/config/configcheck/configcheck_pod.go index bf78483e..eeb1bf40 100644 --- a/pkg/config/configcheck/configcheck_pod.go +++ b/pkg/config/configcheck/configcheck_pod.go @@ -23,6 +23,7 @@ import ( func (cc *ConfigCheck) createVectorConfigCheckPod() *corev1.Pod { labels := labelsForVectorConfigCheck() + annotations := cc.annotationsForVectorConfigCheck() var initContainers []corev1.Container if cc.CompressedConfig { @@ -31,9 +32,10 @@ func (cc *ConfigCheck) createVectorConfigCheckPod() *corev1.Pod { pod := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ - Name: cc.getNameVectorConfigCheck(), - Namespace: cc.Namespace, - Labels: labels, + Name: cc.getNameVectorConfigCheck(), + Namespace: cc.Namespace, + Labels: labels, + Annotations: annotations, }, Spec: corev1.PodSpec{ ServiceAccountName: "vector-configcheck", diff --git a/pkg/utils/k8s/k8s.go b/pkg/utils/k8s/k8s.go index 0791e648..abb68b7f 100644 --- a/pkg/utils/k8s/k8s.go +++ b/pkg/utils/k8s/k8s.go @@ -75,7 +75,7 @@ func createOrUpdateDeployment(ctx context.Context, desired *appsv1.Deployment, c existing := desired.DeepCopy() _, err := controllerutil.CreateOrUpdate(ctx, c, existing, func() error { existing.Labels = desired.Labels - existing.Annotations = desired.Annotations + existing.Annotations = mergeMaps(desired.Annotations, existing.Annotations) existing.OwnerReferences = desired.OwnerReferences existing.Spec = desired.Spec return nil @@ -91,7 +91,7 @@ func createOrUpdateStatefulSet(ctx context.Context, desired *appsv1.StatefulSet, existing := desired.DeepCopy() _, err := controllerutil.CreateOrUpdate(ctx, c, existing, func() error { existing.Labels = desired.Labels - existing.Annotations = desired.Annotations + existing.Annotations = mergeMaps(desired.Annotations, existing.Annotations) existing.OwnerReferences = desired.OwnerReferences existing.Spec = desired.Spec return nil @@ -107,7 +107,7 @@ func createOrUpdateDaemonSet(ctx context.Context, desired *appsv1.DaemonSet, c c existing := desired.DeepCopy() _, err := controllerutil.CreateOrUpdate(ctx, c, existing, func() error { existing.Labels = desired.Labels - existing.Annotations = desired.Annotations + existing.Annotations = mergeMaps(desired.Annotations, existing.Annotations) existing.OwnerReferences = desired.OwnerReferences existing.Spec = desired.Spec return nil @@ -123,7 +123,7 @@ func createOrUpdateSecret(ctx context.Context, desired *corev1.Secret, c client. existing := desired.DeepCopy() _, err := controllerutil.CreateOrUpdate(ctx, c, existing, func() error { existing.Labels = desired.Labels - existing.Annotations = desired.Annotations + existing.Annotations = mergeMaps(desired.Annotations, existing.Annotations) existing.OwnerReferences = desired.OwnerReferences existing.Data = desired.Data return nil @@ -139,7 +139,7 @@ func createOrUpdateService(ctx context.Context, desired *corev1.Service, c clien existing := desired.DeepCopy() _, err := controllerutil.CreateOrUpdate(ctx, c, existing, func() error { existing.Labels = desired.Labels - existing.Annotations = desired.Annotations + existing.Annotations = mergeMaps(desired.Annotations, existing.Annotations) existing.OwnerReferences = desired.OwnerReferences existing.Spec = desired.Spec return nil @@ -155,7 +155,7 @@ func createOrUpdateServiceAccount(ctx context.Context, desired *corev1.ServiceAc existing := desired.DeepCopy() _, err := controllerutil.CreateOrUpdate(ctx, c, existing, func() error { existing.Labels = desired.Labels - existing.Annotations = desired.Annotations + existing.Annotations = mergeMaps(desired.Annotations, existing.Annotations) existing.OwnerReferences = desired.OwnerReferences return nil }) @@ -170,7 +170,7 @@ func createOrUpdateClusterRole(ctx context.Context, desired *rbacv1.ClusterRole, existing := desired.DeepCopy() _, err := controllerutil.CreateOrUpdate(ctx, c, existing, func() error { existing.Labels = desired.Labels - existing.Annotations = desired.Annotations + existing.Annotations = mergeMaps(desired.Annotations, existing.Annotations) existing.Rules = desired.Rules return nil }) @@ -185,7 +185,7 @@ func createOrUpdateClusterRoleBinding(ctx context.Context, desired *rbacv1.Clust existing := desired.DeepCopy() _, err := controllerutil.CreateOrUpdate(ctx, c, existing, func() error { existing.Labels = desired.Labels - existing.Annotations = desired.Annotations + existing.Annotations = mergeMaps(desired.Annotations, existing.Annotations) existing.OwnerReferences = desired.OwnerReferences existing.RoleRef = desired.RoleRef existing.Subjects = desired.Subjects @@ -202,7 +202,7 @@ func createOrUpdatePodMonitor(ctx context.Context, desired *monitorv1.PodMonitor existing := desired.DeepCopy() _, err := controllerutil.CreateOrUpdate(ctx, c, existing, func() error { existing.Labels = desired.Labels - existing.Annotations = desired.Annotations + existing.Annotations = mergeMaps(desired.Annotations, existing.Annotations) existing.OwnerReferences = desired.OwnerReferences existing.Spec = desired.Spec return nil @@ -218,7 +218,7 @@ func createOrUpdatePodSrape(ctx context.Context, desired *victoriametricsv1beta1 existing := desired.DeepCopy() _, err := controllerutil.CreateOrUpdate(ctx, c, existing, func() error { existing.Labels = desired.Labels - existing.Annotations = desired.Annotations + existing.Annotations = mergeMaps(desired.Annotations, existing.Annotations) existing.OwnerReferences = desired.OwnerReferences existing.Spec = desired.Spec return nil @@ -339,3 +339,13 @@ func ResourceExists(dc discovery.DiscoveryInterface, apiGroupVersion, kind strin return false, nil } + +func mergeMaps(new, old map[string]string) map[string]string { + if old == nil { + old = make(map[string]string, len(new)) + } + for k, v := range new { + old[k] = v + } + return old +} diff --git a/pkg/vector/vectoragent/vectoragent_config.go b/pkg/vector/vectoragent/vectoragent_config.go index f3ade890..faee97b6 100644 --- a/pkg/vector/vectoragent/vectoragent_config.go +++ b/pkg/vector/vectoragent/vectoragent_config.go @@ -27,6 +27,7 @@ import ( func (ctrl *Controller) createVectorAgentConfig(ctx context.Context) (*corev1.Secret, error) { log := log.FromContext(ctx).WithValues("vector-agent-rbac", ctrl.Vector.Name) labels := ctrl.labelsForVectorAgent() + annotations := ctrl.annotationsForVectorAgent() var data []byte = ctrl.Config if ctrl.Vector.Spec.Agent.CompressConfigFile { @@ -36,7 +37,7 @@ func (ctrl *Controller) createVectorAgentConfig(ctx context.Context) (*corev1.Se "agent.json": data, } secret := &corev1.Secret{ - ObjectMeta: ctrl.objectMetaVectorAgent(labels, ctrl.Vector.Namespace), + ObjectMeta: ctrl.objectMetaVectorAgent(labels, annotations, ctrl.Vector.Namespace), Data: config, } diff --git a/pkg/vector/vectoragent/vectoragent_controller.go b/pkg/vector/vectoragent/vectoragent_controller.go index 5272f886..ca79e368 100644 --- a/pkg/vector/vectoragent/vectoragent_controller.go +++ b/pkg/vector/vectoragent/vectoragent_controller.go @@ -150,11 +150,16 @@ func (ctrl *Controller) labelsForVectorAgent() map[string]string { } } -func (ctrl *Controller) objectMetaVectorAgent(labels map[string]string, namespace string) metav1.ObjectMeta { +func (ctrl *Controller) annotationsForVectorAgent() map[string]string { + return ctrl.Vector.Spec.Agent.Annotations +} + +func (ctrl *Controller) objectMetaVectorAgent(labels map[string]string, annotations map[string]string, namespace string) metav1.ObjectMeta { return metav1.ObjectMeta{ Name: ctrl.Vector.Name + "-agent", Namespace: namespace, Labels: labels, + Annotations: annotations, OwnerReferences: ctrl.getControllerReference(), } } diff --git a/pkg/vector/vectoragent/vectoragent_daemonset.go b/pkg/vector/vectoragent/vectoragent_daemonset.go index 780bfd1a..f86706cd 100644 --- a/pkg/vector/vectoragent/vectoragent_daemonset.go +++ b/pkg/vector/vectoragent/vectoragent_daemonset.go @@ -24,6 +24,7 @@ import ( func (ctrl *Controller) createVectorAgentDaemonSet() *appsv1.DaemonSet { labels := ctrl.labelsForVectorAgent() + annotations := ctrl.annotationsForVectorAgent() var initContainers []corev1.Container var containers []corev1.Container containers = append(containers, *ctrl.VectorAgentContainer()) @@ -37,11 +38,11 @@ func (ctrl *Controller) createVectorAgentDaemonSet() *appsv1.DaemonSet { } daemonset := &appsv1.DaemonSet{ - ObjectMeta: ctrl.objectMetaVectorAgent(labels, ctrl.Vector.Namespace), + ObjectMeta: ctrl.objectMetaVectorAgent(labels, annotations, ctrl.Vector.Namespace), Spec: appsv1.DaemonSetSpec{ Selector: &metav1.LabelSelector{MatchLabels: labels}, Template: corev1.PodTemplateSpec{ - ObjectMeta: ctrl.objectMetaVectorAgent(labels, ctrl.Vector.Namespace), + ObjectMeta: ctrl.objectMetaVectorAgent(labels, annotations, ctrl.Vector.Namespace), Spec: corev1.PodSpec{ ServiceAccountName: ctrl.getNameVectorAgent(), Volumes: ctrl.generateVectorAgentVolume(), diff --git a/pkg/vector/vectoragent/vectoragent_podmonitor.go b/pkg/vector/vectoragent/vectoragent_podmonitor.go index 80523151..afbee177 100644 --- a/pkg/vector/vectoragent/vectoragent_podmonitor.go +++ b/pkg/vector/vectoragent/vectoragent_podmonitor.go @@ -8,9 +8,10 @@ import ( func (ctrl *Controller) createVectorAgentPodMonitor() *monitorv1.PodMonitor { labels := ctrl.labelsForVectorAgent() + annotations := ctrl.annotationsForVectorAgent() podmonitor := &monitorv1.PodMonitor{ - ObjectMeta: ctrl.objectMetaVectorAgent(labels, ctrl.Vector.Namespace), + ObjectMeta: ctrl.objectMetaVectorAgent(labels, annotations, ctrl.Vector.Namespace), Spec: monitorv1.PodMonitorSpec{ PodMetricsEndpoints: []monitorv1.PodMetricsEndpoint{ { diff --git a/pkg/vector/vectoragent/vectoragent_rbac.go b/pkg/vector/vectoragent/vectoragent_rbac.go index 16969707..6858c16d 100644 --- a/pkg/vector/vectoragent/vectoragent_rbac.go +++ b/pkg/vector/vectoragent/vectoragent_rbac.go @@ -23,9 +23,10 @@ import ( func (ctrl *Controller) createVectorAgentServiceAccount() *corev1.ServiceAccount { labels := ctrl.labelsForVectorAgent() + annotations := ctrl.annotationsForVectorAgent() serviceAccount := &corev1.ServiceAccount{ - ObjectMeta: ctrl.objectMetaVectorAgent(labels, ctrl.Vector.Namespace), + ObjectMeta: ctrl.objectMetaVectorAgent(labels, annotations, ctrl.Vector.Namespace), } return serviceAccount @@ -33,9 +34,10 @@ func (ctrl *Controller) createVectorAgentServiceAccount() *corev1.ServiceAccount func (ctrl *Controller) createVectorAgentClusterRole() *rbacv1.ClusterRole { labels := ctrl.labelsForVectorAgent() + annotations := ctrl.annotationsForVectorAgent() clusterRole := &rbacv1.ClusterRole{ - ObjectMeta: ctrl.objectMetaVectorAgent(labels, ""), + ObjectMeta: ctrl.objectMetaVectorAgent(labels, annotations, ""), Rules: []rbacv1.PolicyRule{ { APIGroups: []string{""}, @@ -50,9 +52,10 @@ func (ctrl *Controller) createVectorAgentClusterRole() *rbacv1.ClusterRole { func (ctrl *Controller) createVectorAgentClusterRoleBinding() *rbacv1.ClusterRoleBinding { labels := ctrl.labelsForVectorAgent() + annotations := ctrl.annotationsForVectorAgent() clusterRoleBinding := &rbacv1.ClusterRoleBinding{ - ObjectMeta: ctrl.objectMetaVectorAgent(labels, ""), + ObjectMeta: ctrl.objectMetaVectorAgent(labels, annotations, ""), RoleRef: rbacv1.RoleRef{ Kind: "ClusterRole", APIGroup: "rbac.authorization.k8s.io", diff --git a/pkg/vector/vectoragent/vectoragent_service.go b/pkg/vector/vectoragent/vectoragent_service.go index 8790a976..1d346274 100644 --- a/pkg/vector/vectoragent/vectoragent_service.go +++ b/pkg/vector/vectoragent/vectoragent_service.go @@ -28,9 +28,10 @@ const ( func (ctrl *Controller) createVectorAgentService() *corev1.Service { labels := ctrl.labelsForVectorAgent() + annotations := ctrl.annotationsForVectorAgent() return &corev1.Service{ - ObjectMeta: ctrl.objectMetaVectorAgent(labels, ctrl.Vector.Namespace), + ObjectMeta: ctrl.objectMetaVectorAgent(labels, annotations, ctrl.Vector.Namespace), Spec: corev1.ServiceSpec{ Ports: []corev1.ServicePort{ { From 2640afcdecb0fd5f68e000a2e971355519bfff6f Mon Sep 17 00:00:00 2001 From: zvlb Date: Tue, 7 May 2024 12:02:25 +0300 Subject: [PATCH 267/316] Prepare v0.0.40 release Signed-off-by: zvlb --- CHANGELOG.md | 3 + helm/charts/vector-operator/Chart.yaml | 4 +- .../observability.kaasops.io_vectors.yaml | 28 ++++-- helm/index.yaml | 85 ++++++++++-------- helm/packages/vector-operator-0.0.40.tgz | Bin 0 -> 33310 bytes 5 files changed, 77 insertions(+), 43 deletions(-) create mode 100644 helm/packages/vector-operator-0.0.40.tgz diff --git a/CHANGELOG.md b/CHANGELOG.md index c0d0b124..fb1df8b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## v0.0.40 +- [[138]](https://github.com/kaasops/vector-operator/issues/138), [[142]](https://github.com/kaasops/vector-operator/issues/142) **Feature** Add control for Vector DaemonSet annotation + ## v0.0.33 - [[134]](https://github.com/kaasops/vector-operator/pull/134) **Feature** Add use_apiserver_cache option and config build refactoring diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index 19e956c4..3aac2609 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.39 +version: 0.0.40 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.0.39" +appVersion: "v0.0.40" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml index 48461772..2509005f 100644 --- a/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml +++ b/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml @@ -914,9 +914,16 @@ spec: type: array type: object type: object + annotations: + additionalProperties: + type: string + description: Annotations is an unstructured key value map stored + with a resource that may be set by external tools to store and + retrieve arbitrary metadata. + type: object api: - description: ApiSpec is the Schema for the Vector Agent GraphQL - API - https://vector.dev/docs/reference/api/ + description: Vector API params. Allows to interact with a running + Vector instance. https://vector.dev/docs/reference/api/ properties: enabled: type: boolean @@ -929,11 +936,11 @@ spec: type: boolean type: object compressConfigFile: - description: Compress config file + description: 'Compress config file to fix: metadata.annotations: + Too long: must have at most 262144 characters' type: boolean configCheck: - description: ConfigCheck is the Schema for control params for - ConfigCheck pods + description: Control params for ConfigCheck pods properties: affinity: description: Affinity If specified, the pod's scheduling constraints. @@ -1837,6 +1844,13 @@ spec: type: array type: object type: object + annotations: + additionalProperties: + type: string + description: Annotations is an unstructured key value map + stored with a resource that may be set by external tools + to store and retrieve arbitrary metadata. + type: object disabled: type: boolean image: @@ -2116,6 +2130,10 @@ spec: type: object type: object dataDir: + description: The directory used for persisting Vector state, such + as on-disk buffers, file checkpoints, and more. Please make + sure the Vector project has write permissions to this directory. + https://vector.dev/docs/reference/configuration/global-options/#data_dir type: string env: description: Env that will be added to Vector pod diff --git a/helm/index.yaml b/helm/index.yaml index 09c94426..09f5f1f5 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.0.40 + created: "2024-05-07T12:01:59.961007+03:00" + description: A Helm chart to install Vector Operator + digest: c50e673e811b8d4c03ad45c92ce23b9bc54eef4665091e70fab9a4b7e4c6c3f1 + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.40.tgz + version: 0.0.40 - apiVersion: v2 appVersion: v0.0.39 - created: "2023-12-18T11:12:18.341448445+02:00" + created: "2024-05-07T12:01:59.960184+03:00" description: A Helm chart to install Vector Operator digest: e1de38c869bf896bfb9f5f615c329d3ccce3bd91cb64bb6fa1c783a40ea290a2 home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.0.39 - apiVersion: v2 appVersion: v0.0.38 - created: "2023-12-18T11:12:18.340555026+02:00" + created: "2024-05-07T12:01:59.959349+03:00" description: A Helm chart to install Vector Operator digest: 565b148184400900f5572b06ceebaac6340af5e5fd1122b308bdbfcbc2d2040a home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.0.38 - apiVersion: v2 appVersion: v0.0.37 - created: "2023-12-18T11:12:18.339646199+02:00" + created: "2024-05-07T12:01:59.958197+03:00" description: A Helm chart to install Vector Operator digest: 65e10ee46e6855ba95f51e21ca14abc4411191b260c6b96ec72c775e73c1e331 home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.0.37 - apiVersion: v2 appVersion: v0.0.36 - created: "2023-12-18T11:12:18.33825308+02:00" + created: "2024-05-07T12:01:59.957399+03:00" description: A Helm chart to install Vector Operator digest: 972b6b4048b6d17b0616786e49915ef52cb7c9573cfb6eb359b6c19b66eabe31 home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.0.36 - apiVersion: v2 appVersion: v0.0.35 - created: "2023-12-18T11:12:18.337102292+02:00" + created: "2024-05-07T12:01:59.956626+03:00" description: A Helm chart to install Vector Operator digest: d03ba759c42f2bd8d8f1df71702ee4f26a73b8bc28760ca7254af20d811cee8a home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.0.35 - apiVersion: v2 appVersion: v0.0.34 - created: "2023-12-18T11:12:18.336129065+02:00" + created: "2024-05-07T12:01:59.955826+03:00" description: A Helm chart to install Vector Operator digest: 01e9d488ee78c8603d821f96344edee568ebcb42049d586de37b8df39b372bd4 home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.0.34 - apiVersion: v2 appVersion: v0.0.33 - created: "2023-12-18T11:12:18.335217127+02:00" + created: "2024-05-07T12:01:59.954638+03:00" description: A Helm chart to install Vector Operator digest: fcde3c94a0fa6caa5f3d333226c95b7c85ede8489d46277e1222a868ed4ec8c3 home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.0.33 - apiVersion: v2 appVersion: v0.0.32 - created: "2023-12-18T11:12:18.333824269+02:00" + created: "2024-05-07T12:01:59.95383+03:00" description: A Helm chart to install Vector Operator digest: 26323037ec47f1703ea930a99ab4ec8fb93b44975ce969514ea68d4130017015 home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.0.32 - apiVersion: v2 appVersion: v0.0.31 - created: "2023-12-18T11:12:18.332792168+02:00" + created: "2024-05-07T12:01:59.953024+03:00" description: A Helm chart to install Vector Operator digest: 45b924c07a825e0f7cd3fb534a6ffd16604790d13be1aff59150c045474754e3 home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.0.31 - apiVersion: v2 appVersion: v0.0.30 - created: "2023-12-18T11:12:18.33183807+02:00" + created: "2024-05-07T12:01:59.952256+03:00" description: A Helm chart to install Vector Operator digest: 03beda549d15f50325028ea29af5f2065ac0b8adf3078bf7dc1312981aa5e7db home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.0.30 - apiVersion: v2 appVersion: v0.0.29 - created: "2023-12-18T11:12:18.330651478+02:00" + created: "2024-05-07T12:01:59.951028+03:00" description: A Helm chart to install Vector Operator digest: 0f025fc3a924b37b8c4131c4d8cfa437d2d4e557ab9476ed3e69a00232c7dca6 home: https://github.com/kaasops/vector-operator @@ -146,7 +159,7 @@ entries: version: 0.0.29 - apiVersion: v2 appVersion: v0.0.28 - created: "2023-12-18T11:12:18.328898431+02:00" + created: "2024-05-07T12:01:59.950259+03:00" description: A Helm chart to install Vector Operator digest: af856d41314313e04f15e7143409a9c564c6ca610b0d2eaec3112add8573e668 home: https://github.com/kaasops/vector-operator @@ -159,7 +172,7 @@ entries: version: 0.0.28 - apiVersion: v2 appVersion: v0.0.27 - created: "2023-12-18T11:12:18.327693983+02:00" + created: "2024-05-07T12:01:59.949486+03:00" description: A Helm chart to install Vector Operator digest: 631e2ff02bbd7f247cb486494fd2af60c57cc551066a6a3858226551bc1745a4 home: https://github.com/kaasops/vector-operator @@ -172,7 +185,7 @@ entries: version: 0.0.27 - apiVersion: v2 appVersion: v0.0.26 - created: "2023-12-18T11:12:18.326768591+02:00" + created: "2024-05-07T12:01:59.948041+03:00" description: A Helm chart to install Vector Operator digest: 760a2833f4c1a33466982419b079ff18d996331ebacc40cf93b0f55229cdb7db home: https://github.com/kaasops/vector-operator @@ -185,7 +198,7 @@ entries: version: 0.0.26 - apiVersion: v2 appVersion: v0.0.25 - created: "2023-12-18T11:12:18.325725899+02:00" + created: "2024-05-07T12:01:59.947206+03:00" description: A Helm chart to install Vector Operator digest: fd22b996b071b6d85740ccf76e85cb640fa717c2620748d206d3f4fdd44cbcc2 home: https://github.com/kaasops/vector-operator @@ -198,7 +211,7 @@ entries: version: 0.0.25 - apiVersion: v2 appVersion: v0.0.24 - created: "2023-12-18T11:12:18.324209369+02:00" + created: "2024-05-07T12:01:59.946416+03:00" description: A Helm chart to install Vector Operator digest: ea257e60ecde063a0d1ed52ce5e3283245b8f0e2daba58ea3a5adb0ba82d7799 home: https://github.com/kaasops/vector-operator @@ -211,7 +224,7 @@ entries: version: 0.0.24 - apiVersion: v2 appVersion: v0.0.23 - created: "2023-12-18T11:12:18.323096236+02:00" + created: "2024-05-07T12:01:59.945623+03:00" description: A Helm chart to install Vector Operator digest: 546d202b3b9263f789b88335263191098dfcabd5d8698105f37cad24d56a8ed0 home: https://github.com/kaasops/vector-operator @@ -224,7 +237,7 @@ entries: version: 0.0.23 - apiVersion: v2 appVersion: v0.0.22 - created: "2023-12-18T11:12:18.322021757+02:00" + created: "2024-05-07T12:01:59.944612+03:00" description: A Helm chart to install Vector Operator digest: bf96ddc8ac61e9d6beb8bc763fbf3fa6025d950b29d70d80de6e8a0ea45e0411 home: https://github.com/kaasops/vector-operator @@ -237,7 +250,7 @@ entries: version: 0.0.22 - apiVersion: v2 appVersion: v0.0.21 - created: "2023-12-18T11:12:18.320958335+02:00" + created: "2024-05-07T12:01:59.943791+03:00" description: A Helm chart to install Vector Operator digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d home: https://github.com/kaasops/vector-operator @@ -250,7 +263,7 @@ entries: version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2023-12-18T11:12:18.319440781+02:00" + created: "2024-05-07T12:01:59.943012+03:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -263,7 +276,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2023-12-18T11:12:18.318779315+02:00" + created: "2024-05-07T12:01:59.942501+03:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -276,7 +289,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2023-12-18T11:12:18.318202989+02:00" + created: "2024-05-07T12:01:59.941667+03:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -289,7 +302,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2023-12-18T11:12:18.31761794+02:00" + created: "2024-05-07T12:01:59.941093+03:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -302,7 +315,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2023-12-18T11:12:18.317053791+02:00" + created: "2024-05-07T12:01:59.940593+03:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -315,7 +328,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2023-12-18T11:12:18.316303122+02:00" + created: "2024-05-07T12:01:59.940044+03:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -328,7 +341,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2023-12-18T11:12:18.315101925+02:00" + created: "2024-05-07T12:01:59.939519+03:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -341,7 +354,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2023-12-18T11:12:18.314219362+02:00" + created: "2024-05-07T12:01:59.93897+03:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -354,7 +367,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2023-12-18T11:12:18.313292106+02:00" + created: "2024-05-07T12:01:59.93735+03:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -367,7 +380,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2023-12-18T11:12:18.312397344+02:00" + created: "2024-05-07T12:01:59.936802+03:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -380,7 +393,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2023-12-18T11:12:18.311589754+02:00" + created: "2024-05-07T12:01:59.936344+03:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -393,7 +406,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2023-12-18T11:12:18.343526429+02:00" + created: "2024-05-07T12:01:59.96261+03:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -406,7 +419,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2023-12-18T11:12:18.342966812+02:00" + created: "2024-05-07T12:01:59.962184+03:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -419,7 +432,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2023-12-18T11:12:18.342419242+02:00" + created: "2024-05-07T12:01:59.961794+03:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -432,7 +445,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2023-12-18T11:12:18.341924788+02:00" + created: "2024-05-07T12:01:59.961408+03:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -445,7 +458,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2023-12-18T11:12:18.31047193+02:00" + created: "2024-05-07T12:01:59.935855+03:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -456,4 +469,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2023-12-18T11:12:18.309795334+02:00" +generated: "2024-05-07T12:01:59.934718+03:00" diff --git a/helm/packages/vector-operator-0.0.40.tgz b/helm/packages/vector-operator-0.0.40.tgz new file mode 100644 index 0000000000000000000000000000000000000000..589563467b72900315d40ba3ea3cda987988c3d4 GIT binary patch literal 33310 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 literal 0 HcmV?d00001 From 3fccc62bff0bba7a713ff051c18ab8717f209729 Mon Sep 17 00:00:00 2001 From: aa1ex <115736127+aa1ex@users.noreply.github.com> Date: Thu, 19 Sep 2024 11:29:35 +0300 Subject: [PATCH 268/316] Migration go.kubebuilder.io/v3 to go.kubebuilder.io/v4 (#147) * WIP: kubebuilder migration from go/v3 to go/v4 Signed-off-by: Aleksandr Aleksandrov --- Dockerfile | 15 +- Makefile | 218 +- PROJECT | 10 +- api/v1alpha1/clustervectorpipeline.go | 2 +- api/v1alpha1/vectorpipeline.go | 2 +- api/v1alpha1/zz_generated.deepcopy.go | 17 +- cmd/main.go | 290 ++ ...ity.kaasops.io_clustervectorpipelines.yaml | 20 +- ...ervability.kaasops.io_vectorpipelines.yaml | 20 +- .../observability.kaasops.io_vectors.yaml | 4477 +++++++++-------- config/crd/kustomization.yaml | 19 +- ...cainjection_in_clustervectorpipelines.yaml | 7 - .../cainjection_in_vectorpipelines.yaml | 7 - .../crd/patches/cainjection_in_vectors.yaml | 7 - .../webhook_in_clustervectorpipelines.yaml | 16 - .../patches/webhook_in_vectorpipelines.yaml | 16 - config/crd/patches/webhook_in_vectors.yaml | 16 - config/default/kustomization.yaml | 161 +- config/default/manager_auth_proxy_patch.yaml | 39 - config/default/manager_config_patch.yaml | 20 - config/default/manager_metrics_patch.yaml | 4 + .../metrics_service.yaml} | 4 +- config/manager/controller_manager_config.yaml | 21 - config/manager/kustomization.yaml | 12 +- config/manager/manager.yaml | 29 +- ...vector-operator.clusterserviceversion.yaml | 75 - config/manifests/kustomization.yaml | 27 - .../network-policy/allow-metrics-traffic.yaml | 26 + config/network-policy/kustomization.yaml | 2 + config/prometheus/monitor.yaml | 14 +- .../clustervectorpipeline_editor_role.yaml | 3 + .../clustervectorpipeline_viewer_role.yaml | 3 + config/rbac/kustomization.yaml | 27 +- config/rbac/leader_election_role.yaml | 3 + config/rbac/leader_election_role_binding.yaml | 3 + ...proxy_role.yaml => metrics_auth_role.yaml} | 2 +- ...ng.yaml => metrics_auth_role_binding.yaml} | 4 +- ...sterrole.yaml => metrics_reader_role.yaml} | 0 config/rbac/role.yaml | 58 +- config/rbac/role_binding.yaml | 3 + config/rbac/service_account.yaml | 3 + config/rbac/vector_editor_role.yaml | 3 + config/rbac/vector_viewer_role.yaml | 3 + config/rbac/vectorpipeline_editor_role.yaml | 3 + config/rbac/vectorpipeline_viewer_role.yaml | 3 + config/samples/kustomization.yaml | 4 +- config/scorecard/bases/config.yaml | 7 - config/scorecard/kustomization.yaml | 16 - config/scorecard/patches/basic.config.yaml | 10 - config/scorecard/patches/olm.config.yaml | 50 - go.mod | 157 +- go.sum | 710 ++- hack/boilerplate.go.txt | 2 +- hack/simple-test.sh | 4 +- {pkg => internal}/config/config.go | 6 +- .../config/configcheck/configcheck.go | 2 +- .../config/configcheck/configcheck_config.go | 2 +- .../config/configcheck/configcheck_error.go | 0 .../config/configcheck/configcheck_pod.go | 0 .../config/configcheck/configcheck_rbac.go | 0 {pkg => internal}/config/configcheck/const.go | 0 {pkg => internal}/config/default.go | 0 {pkg => internal}/config/types.go | 0 {pkg => internal}/config/utils.go | 0 .../controller}/suite_test.go | 57 +- .../controller}/vector_controller.go | 29 +- internal/controller/vector_controller_test.go | 88 + .../controller/vectorpipeline_controller.go | 18 +- .../vectorpipeline_controller_test.go | 88 + {pkg => internal}/pipeline/hash.go | 2 +- {pkg => internal}/pipeline/pipeline.go | 0 .../utils/compression/compression.go | 0 {pkg => internal}/utils/hash/hash.go | 0 {pkg => internal}/utils/hash/hash_test.go | 2 +- {pkg => internal}/utils/k8s/k8s.go | 4 +- {pkg => internal}/utils/k8s/k8s_test.go | 75 +- {pkg => internal}/utils/k8s/label.go | 0 .../vector/vectoragent/vectoragent.go | 4 +- .../vector/vectoragent/vectoragent_config.go | 2 +- .../vectoragent/vectoragent_controller.go | 8 +- .../vectoragent/vectoragent_daemonset.go | 0 .../vector/vectoragent/vectoragent_default.go | 0 .../vectoragent/vectoragent_podmonitor.go | 0 .../vector/vectoragent/vectoragent_rbac.go | 0 .../vector/vectoragent/vectoragent_service.go | 0 main.go | 209 - test/e2e/e2e_suite_test.go | 32 + test/e2e/e2e_test.go | 122 + test/utils/utils.go | 140 + 89 files changed, 4404 insertions(+), 3160 deletions(-) create mode 100644 cmd/main.go delete mode 100644 config/crd/patches/cainjection_in_clustervectorpipelines.yaml delete mode 100644 config/crd/patches/cainjection_in_vectorpipelines.yaml delete mode 100644 config/crd/patches/cainjection_in_vectors.yaml delete mode 100644 config/crd/patches/webhook_in_clustervectorpipelines.yaml delete mode 100644 config/crd/patches/webhook_in_vectorpipelines.yaml delete mode 100644 config/crd/patches/webhook_in_vectors.yaml delete mode 100644 config/default/manager_auth_proxy_patch.yaml delete mode 100644 config/default/manager_config_patch.yaml create mode 100644 config/default/manager_metrics_patch.yaml rename config/{rbac/auth_proxy_service.yaml => default/metrics_service.yaml} (70%) delete mode 100644 config/manager/controller_manager_config.yaml delete mode 100644 config/manifests/bases/vector-operator.clusterserviceversion.yaml delete mode 100644 config/manifests/kustomization.yaml create mode 100644 config/network-policy/allow-metrics-traffic.yaml create mode 100644 config/network-policy/kustomization.yaml rename config/rbac/{auth_proxy_role.yaml => metrics_auth_role.yaml} (90%) rename config/rbac/{auth_proxy_role_binding.yaml => metrics_auth_role_binding.yaml} (79%) rename config/rbac/{auth_proxy_client_clusterrole.yaml => metrics_reader_role.yaml} (100%) delete mode 100644 config/scorecard/bases/config.yaml delete mode 100644 config/scorecard/kustomization.yaml delete mode 100644 config/scorecard/patches/basic.config.yaml delete mode 100644 config/scorecard/patches/olm.config.yaml rename {pkg => internal}/config/config.go (96%) rename {pkg => internal}/config/configcheck/configcheck.go (99%) rename {pkg => internal}/config/configcheck/configcheck_config.go (95%) rename {pkg => internal}/config/configcheck/configcheck_error.go (100%) rename {pkg => internal}/config/configcheck/configcheck_pod.go (100%) rename {pkg => internal}/config/configcheck/configcheck_rbac.go (100%) rename {pkg => internal}/config/configcheck/const.go (100%) rename {pkg => internal}/config/default.go (100%) rename {pkg => internal}/config/types.go (100%) rename {pkg => internal}/config/utils.go (100%) rename {controllers => internal/controller}/suite_test.go (54%) rename {controllers => internal/controller}/vector_controller.go (88%) create mode 100644 internal/controller/vector_controller_test.go rename controllers/pipeline_controller.go => internal/controller/vectorpipeline_controller.go (93%) create mode 100644 internal/controller/vectorpipeline_controller_test.go rename {pkg => internal}/pipeline/hash.go (95%) rename {pkg => internal}/pipeline/pipeline.go (100%) rename {pkg => internal}/utils/compression/compression.go (100%) rename {pkg => internal}/utils/hash/hash.go (100%) rename {pkg => internal}/utils/hash/hash_test.go (95%) rename {pkg => internal}/utils/k8s/k8s.go (99%) rename {pkg => internal}/utils/k8s/k8s_test.go (91%) rename {pkg => internal}/utils/k8s/label.go (100%) rename {pkg => internal}/vector/vectoragent/vectoragent.go (93%) rename {pkg => internal}/vector/vectoragent/vectoragent_config.go (95%) rename {pkg => internal}/vector/vectoragent/vectoragent_controller.go (97%) rename {pkg => internal}/vector/vectoragent/vectoragent_daemonset.go (100%) rename {pkg => internal}/vector/vectoragent/vectoragent_default.go (100%) rename {pkg => internal}/vector/vectoragent/vectoragent_podmonitor.go (100%) rename {pkg => internal}/vector/vectoragent/vectoragent_rbac.go (100%) rename {pkg => internal}/vector/vectoragent/vectoragent_service.go (100%) delete mode 100644 main.go create mode 100644 test/e2e/e2e_suite_test.go create mode 100644 test/e2e/e2e_test.go create mode 100644 test/utils/utils.go diff --git a/Dockerfile b/Dockerfile index 31dcc569..4ba18b68 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,7 @@ # Build the manager binary -FROM golang:1.21 as builder +FROM golang:1.22 AS builder +ARG TARGETOS +ARG TARGETARCH WORKDIR /workspace # Copy the Go Modules manifests @@ -10,13 +12,16 @@ COPY go.sum go.sum RUN go mod download # Copy the go source -COPY main.go main.go +COPY cmd/main.go cmd/main.go COPY api/ api/ -COPY controllers/ controllers/ -COPY pkg/ pkg/ +COPY internal/ internal/ # Build -RUN CGO_ENABLED=0 GOOS=linux GOARCH=$TARGETARCH go build -a -o manager main.go +# the GOARCH has not a default value to allow the binary be built according to the host where the command +# was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO +# the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore, +# by leaving it empty we can ensure that the container and binary shipped on it will have the same platform. +RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager cmd/main.go # Use distroless as minimal base image to package the manager binary # Refer to https://github.com/GoogleContainerTools/distroless for more details diff --git a/Makefile b/Makefile index e1eba2b9..ead1afaf 100644 --- a/Makefile +++ b/Makefile @@ -1,56 +1,7 @@ -# VERSION defines the project version for the bundle. -# Update this value when you upgrade the version of your project. -# To re-generate a bundle for another specific version without changing the standard setup, you can: -# - use the VERSION as arg of the bundle target (e.g make bundle VERSION=0.0.2) -# - use environment variables to overwrite this value (e.g export VERSION=0.0.2) -VERSION ?= 0.0.1 -DOCKER_PLATFORM ?= linux/amd64 - -# CHANNELS define the bundle channels used in the bundle. -# Add a new line here if you would like to change its default config. (E.g CHANNELS = "candidate,fast,stable") -# To re-generate a bundle for other specific channels without changing the standard setup, you can: -# - use the CHANNELS as arg of the bundle target (e.g make bundle CHANNELS=candidate,fast,stable) -# - use environment variables to overwrite this value (e.g export CHANNELS="candidate,fast,stable") -ifneq ($(origin CHANNELS), undefined) -BUNDLE_CHANNELS := --channels=$(CHANNELS) -endif - -# DEFAULT_CHANNEL defines the default channel used in the bundle. -# Add a new line here if you would like to change its default config. (E.g DEFAULT_CHANNEL = "stable") -# To re-generate a bundle for any other default channel without changing the default setup, you can: -# - use the DEFAULT_CHANNEL as arg of the bundle target (e.g make bundle DEFAULT_CHANNEL=stable) -# - use environment variables to overwrite this value (e.g export DEFAULT_CHANNEL="stable") -ifneq ($(origin DEFAULT_CHANNEL), undefined) -BUNDLE_DEFAULT_CHANNEL := --default-channel=$(DEFAULT_CHANNEL) -endif -BUNDLE_METADATA_OPTS ?= $(BUNDLE_CHANNELS) $(BUNDLE_DEFAULT_CHANNEL) - -# IMAGE_TAG_BASE defines the docker.io namespace and part of the image name for remote images. -# This variable is used to construct full image tags for bundle and catalog images. -# -# For example, running 'make bundle-build bundle-push catalog-build catalog-push' will build and push both -# kaasops.io/vector-operator-bundle:$VERSION and kaasops.io/vector-operator-catalog:$VERSION. -IMAGE_TAG_BASE ?= kaasops/vector-operator - -# BUNDLE_IMG defines the image:tag used for the bundle. -# You can use it as an arg. (E.g make bundle-build BUNDLE_IMG=/:) -BUNDLE_IMG ?= $(IMAGE_TAG_BASE)-bundle:v$(VERSION) - -# BUNDLE_GEN_FLAGS are the flags passed to the operator-sdk generate bundle command -BUNDLE_GEN_FLAGS ?= -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS) - -# USE_IMAGE_DIGESTS defines if images are resolved via tags or digests -# You can enable this value if you would like to use SHA Based Digests -# To enable set flag to true -USE_IMAGE_DIGESTS ?= false -ifeq ($(USE_IMAGE_DIGESTS), true) - BUNDLE_GEN_FLAGS += --use-image-digests -endif - # Image URL to use all building/pushing image targets IMG ?= controller:latest # ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. -ENVTEST_K8S_VERSION = 1.24.2 +ENVTEST_K8S_VERSION = 1.31.0 # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) ifeq (,$(shell go env GOBIN)) @@ -59,6 +10,12 @@ else GOBIN=$(shell go env GOBIN) endif +# CONTAINER_TOOL defines the container tool to be used for building images. +# Be aware that the target commands are only tested with Docker which is +# scaffolded by default. However, you might want to replace it to use other +# tools. (i.e. podman) +CONTAINER_TOOL ?= docker + # Setting SHELL to bash allows bash commands to be executed by recipes. # Options are set to exit when a recipe line exits non-zero or a piped command fails. SHELL = /usr/bin/env bash -o pipefail @@ -71,7 +28,7 @@ all: build # The help target prints out all targets with their descriptions organized # beneath their categories. The categories are represented by '##@' and the -# target descriptions by '##'. The awk commands is responsible for reading the +# target descriptions by '##'. The awk command is responsible for reading the # entire set of makefiles included in this invocation, looking for lines of the # file as xyz: ## something, and then pretty-format the target and help. Then, # if there's a line with ##@ something, that gets pretty-printed as a category. @@ -104,25 +61,64 @@ vet: ## Run go vet against code. .PHONY: test test: manifests generate fmt vet envtest ## Run tests. - KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test ./... -coverprofile cover.out + KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out + +# Utilize Kind or modify the e2e tests to load the image locally, enabling compatibility with other vendors. +.PHONY: test-e2e # Run the e2e tests against a Kind k8s instance that is spun up. +test-e2e: + go test ./test/e2e/ -v -ginkgo.v + +.PHONY: lint +lint: golangci-lint ## Run golangci-lint linter + $(GOLANGCI_LINT) run + +.PHONY: lint-fix +lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes + $(GOLANGCI_LINT) run --fix ##@ Build .PHONY: build -build: generate fmt vet ## Build manager binary. - go build -o bin/manager main.go +build: manifests generate fmt vet ## Build manager binary. + go build -o bin/manager cmd/main.go .PHONY: run run: manifests generate fmt vet ## Run a controller from your host. - go run ./main.go + go run ./cmd/main.go +# If you wish to build the manager image targeting other platforms you can use the --platform flag. +# (i.e. docker build --platform linux/arm64). However, you must enable docker buildKit for it. +# More info: https://docs.docker.com/develop/develop-images/build_enhancements/ .PHONY: docker-build -docker-build: test ## Build docker image with the manager. - docker build --platform ${DOCKER_PLATFORM} -t ${IMG} . +docker-build: ## Build docker image with the manager. + $(CONTAINER_TOOL) build -t ${IMG} . .PHONY: docker-push docker-push: ## Push docker image with the manager. - docker push ${IMG} + $(CONTAINER_TOOL) push ${IMG} + +# PLATFORMS defines the target platforms for the manager image be built to provide support to multiple +# architectures. (i.e. make docker-buildx IMG=myregistry/mypoperator:0.0.1). To use this option you need to: +# - be able to use docker buildx. More info: https://docs.docker.com/build/buildx/ +# - have enabled BuildKit. More info: https://docs.docker.com/develop/develop-images/build_enhancements/ +# - be able to push the image to your registry (i.e. if you do not set a valid value via IMG=> then the export will fail) +# To adequately provide solutions that are compatible with multiple platforms, you should consider using this option. +PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le +.PHONY: docker-buildx +docker-buildx: ## Build and push docker image for the manager for cross-platform support + # copy existing Dockerfile and insert --platform=${BUILDPLATFORM} into Dockerfile.cross, and preserve the original Dockerfile + sed -e '1 s/\(^FROM\)/FROM --platform=\$$\{BUILDPLATFORM\}/; t' -e ' 1,// s//FROM --platform=\$$\{BUILDPLATFORM\}/' Dockerfile > Dockerfile.cross + - $(CONTAINER_TOOL) buildx create --name vector-operator-builder + $(CONTAINER_TOOL) buildx use vector-operator-builder + - $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile.cross . + - $(CONTAINER_TOOL) buildx rm vector-operator-builder + rm Dockerfile.cross + +.PHONY: build-installer +build-installer: manifests generate kustomize ## Generate a consolidated YAML with CRDs and deployment. + mkdir -p dist + cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} + $(KUSTOMIZE) build config/default > dist/install.yaml ##@ Deployment @@ -132,22 +128,22 @@ endif .PHONY: install install: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config. - $(KUSTOMIZE) build config/crd | kubectl apply -f - + $(KUSTOMIZE) build config/crd | $(KUBECTL) apply -f - .PHONY: uninstall uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. - $(KUSTOMIZE) build config/crd | kubectl delete --ignore-not-found=$(ignore-not-found) -f - + $(KUSTOMIZE) build config/crd | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f - .PHONY: deploy deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config. cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} - $(KUSTOMIZE) build config/default | kubectl apply -f - + $(KUSTOMIZE) build config/default | $(KUBECTL) apply -f - .PHONY: undeploy -undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. - $(KUSTOMIZE) build config/default | kubectl delete --ignore-not-found=$(ignore-not-found) -f - +undeploy: kustomize ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. + $(KUSTOMIZE) build config/default | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f - -##@ Build Dependencies +##@ Dependencies ## Location to install dependencies to LOCALBIN ?= $(shell pwd)/bin @@ -155,82 +151,50 @@ $(LOCALBIN): mkdir -p $(LOCALBIN) ## Tool Binaries +KUBECTL ?= kubectl KUSTOMIZE ?= $(LOCALBIN)/kustomize CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen ENVTEST ?= $(LOCALBIN)/setup-envtest +GOLANGCI_LINT = $(LOCALBIN)/golangci-lint ## Tool Versions -KUSTOMIZE_VERSION ?= v4.2.0 -CONTROLLER_TOOLS_VERSION ?= v0.9.2 +KUSTOMIZE_VERSION ?= v5.4.3 +CONTROLLER_TOOLS_VERSION ?= v0.16.1 +ENVTEST_VERSION ?= release-0.19 +GOLANGCI_LINT_VERSION ?= v1.59.1 -KUSTOMIZE_INSTALL_SCRIPT ?= "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" .PHONY: kustomize kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. $(KUSTOMIZE): $(LOCALBIN) - test -s $(LOCALBIN)/kustomize || { curl -s $(KUSTOMIZE_INSTALL_SCRIPT) | bash -s -- $(subst v,,$(KUSTOMIZE_VERSION)) $(LOCALBIN); } + $(call go-install-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v5,$(KUSTOMIZE_VERSION)) .PHONY: controller-gen controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. $(CONTROLLER_GEN): $(LOCALBIN) - test -s $(LOCALBIN)/controller-gen || GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_TOOLS_VERSION) + $(call go-install-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen,$(CONTROLLER_TOOLS_VERSION)) .PHONY: envtest -envtest: $(ENVTEST) ## Download envtest-setup locally if necessary. +envtest: $(ENVTEST) ## Download setup-envtest locally if necessary. $(ENVTEST): $(LOCALBIN) - test -s $(LOCALBIN)/setup-envtest || GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest - -.PHONY: bundle -bundle: manifests kustomize ## Generate bundle manifests and metadata, then validate generated files. - operator-sdk generate kustomize manifests -q - cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG) - $(KUSTOMIZE) build config/manifests | operator-sdk generate bundle $(BUNDLE_GEN_FLAGS) - operator-sdk bundle validate ./bundle - -.PHONY: bundle-build -bundle-build: ## Build the bundle image. - docker build -f bundle.Dockerfile -t $(BUNDLE_IMG) . - -.PHONY: bundle-push -bundle-push: ## Push the bundle image. - $(MAKE) docker-push IMG=$(BUNDLE_IMG) - -.PHONY: opm -OPM = ./bin/opm -opm: ## Download opm locally if necessary. -ifeq (,$(wildcard $(OPM))) -ifeq (,$(shell which opm 2>/dev/null)) - @{ \ - set -e ;\ - mkdir -p $(dir $(OPM)) ;\ - OS=$(shell go env GOOS) && ARCH=$(shell go env GOARCH) && \ - curl -sSLo $(OPM) https://github.com/operator-framework/operator-registry/releases/download/v1.23.0/$${OS}-$${ARCH}-opm ;\ - chmod +x $(OPM) ;\ - } -else -OPM = $(shell which opm) -endif -endif - -# A comma-separated list of bundle images (e.g. make catalog-build BUNDLE_IMGS=example.com/operator-bundle:v0.1.0,example.com/operator-bundle:v0.2.0). -# These images MUST exist in a registry and be pull-able. -BUNDLE_IMGS ?= $(BUNDLE_IMG) - -# The image tag given to the resulting catalog image (e.g. make catalog-build CATALOG_IMG=example.com/operator-catalog:v0.2.0). -CATALOG_IMG ?= $(IMAGE_TAG_BASE)-catalog:v$(VERSION) - -# Set CATALOG_BASE_IMG to an existing catalog image tag to add $BUNDLE_IMGS to that image. -ifneq ($(origin CATALOG_BASE_IMG), undefined) -FROM_INDEX_OPT := --from-index $(CATALOG_BASE_IMG) -endif - -# Build a catalog image by adding bundle images to an empty catalog using the operator package manager tool, 'opm'. -# This recipe invokes 'opm' in 'semver' bundle add mode. For more information on add modes, see: -# https://github.com/operator-framework/community-operators/blob/7f1438c/docs/packaging-operator.md#updating-your-existing-operator -.PHONY: catalog-build -catalog-build: opm ## Build a catalog image. - $(OPM) index add --container-tool docker --mode semver --tag $(CATALOG_IMG) --bundles $(BUNDLE_IMGS) $(FROM_INDEX_OPT) - -# Push the catalog image. -.PHONY: catalog-push -catalog-push: ## Push a catalog image. - $(MAKE) docker-push IMG=$(CATALOG_IMG) + $(call go-install-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest,$(ENVTEST_VERSION)) + +.PHONY: golangci-lint +golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary. +$(GOLANGCI_LINT): $(LOCALBIN) + $(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION)) + +# go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist +# $1 - target path with name of binary +# $2 - package url which can be installed +# $3 - specific version of package +define go-install-tool +@[ -f "$(1)-$(3)" ] || { \ +set -e; \ +package=$(2)@$(3) ;\ +echo "Downloading $${package}" ;\ +rm -f $(1) || true ;\ +GOBIN=$(LOCALBIN) go install $${package} ;\ +mv $(1) $(1)-$(3) ;\ +} ;\ +ln -sf $(1)-$(3) $(1) +endef diff --git a/PROJECT b/PROJECT index fca8aa8e..c04525fc 100644 --- a/PROJECT +++ b/PROJECT @@ -1,9 +1,10 @@ +# Code generated by tool. DO NOT EDIT. +# This file is used to track the info used to scaffold your project +# and allow the plugins properly work. +# More info: https://book.kubebuilder.io/reference/project-config.html domain: kaasops.io layout: -- go.kubebuilder.io/v3 -plugins: - manifests.sdk.operatorframework.io/v2: {} - scorecard.sdk.operatorframework.io/v2: {} +- go.kubebuilder.io/v4 projectName: vector-operator repo: github.com/kaasops/vector-operator resources: @@ -28,7 +29,6 @@ resources: - api: crdVersion: v1 namespaced: true - controller: true domain: kaasops.io group: observability kind: ClusterVectorPipeline diff --git a/api/v1alpha1/clustervectorpipeline.go b/api/v1alpha1/clustervectorpipeline.go index 3d09a4f7..be1bafec 100644 --- a/api/v1alpha1/clustervectorpipeline.go +++ b/api/v1alpha1/clustervectorpipeline.go @@ -3,7 +3,7 @@ package v1alpha1 import ( "context" - "github.com/kaasops/vector-operator/pkg/utils/k8s" + "github.com/kaasops/vector-operator/internal/utils/k8s" "sigs.k8s.io/controller-runtime/pkg/client" ) diff --git a/api/v1alpha1/vectorpipeline.go b/api/v1alpha1/vectorpipeline.go index 302d181b..b3e545b0 100644 --- a/api/v1alpha1/vectorpipeline.go +++ b/api/v1alpha1/vectorpipeline.go @@ -3,7 +3,7 @@ package v1alpha1 import ( "context" - "github.com/kaasops/vector-operator/pkg/utils/k8s" + "github.com/kaasops/vector-operator/internal/utils/k8s" "sigs.k8s.io/controller-runtime/pkg/client" ) diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index a16accea..0d828cc8 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -1,8 +1,7 @@ //go:build !ignore_autogenerated -// +build !ignore_autogenerated /* -Copyright 2022. +Copyright 2024. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -129,6 +128,13 @@ func (in *ConfigCheck) DeepCopyInto(out *ConfigCheck) { } } } + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigCheck. @@ -176,6 +182,13 @@ func (in *VectorAgent) DeepCopyInto(out *VectorAgent) { *out = make([]v1.LocalObjectReference, len(*in)) copy(*out, *in) } + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } in.Resources.DeepCopyInto(&out.Resources) if in.Affinity != nil { in, out := &in.Affinity, &out.Affinity diff --git a/cmd/main.go b/cmd/main.go new file mode 100644 index 00000000..69f2c138 --- /dev/null +++ b/cmd/main.go @@ -0,0 +1,290 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "crypto/tls" + "flag" + "github.com/kaasops/vector-operator/internal/utils/k8s" + monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/discovery" + "k8s.io/client-go/kubernetes" + "os" + "sigs.k8s.io/controller-runtime/pkg/cache" + "sigs.k8s.io/controller-runtime/pkg/client" + "sync" + "time" + + // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) + // to ensure that exec-entrypoint and run can make use of them. + _ "k8s.io/client-go/plugin/pkg/client/auth" + + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/healthz" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + "sigs.k8s.io/controller-runtime/pkg/metrics/filters" + metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" + "sigs.k8s.io/controller-runtime/pkg/webhook" + + "github.com/kaasops/vector-operator/api/v1alpha1" + "github.com/kaasops/vector-operator/internal/controller" + // +kubebuilder:scaffold:imports +) + +var ( + scheme = runtime.NewScheme() + setupLog = ctrl.Log.WithName("setup") +) + +func init() { + utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + + utilruntime.Must(v1alpha1.AddToScheme(scheme)) + utilruntime.Must(monitorv1.AddToScheme(scheme)) + // +kubebuilder:scaffold:scheme +} + +func main() { + var metricsAddr string + var enableLeaderElection bool + var probeAddr string + var secureMetrics bool + var enableHTTP2 bool + var tlsOpts []func(*tls.Config) + var watchNamespace string + var watchLabel string + var pipelineCheckWG sync.WaitGroup + var pipelineCheckTimeout time.Duration + var pipelineDeleteEventTimeout time.Duration + var configCheckTimeout time.Duration + + flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+ + "Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.") + flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") + flag.BoolVar(&enableLeaderElection, "leader-elect", false, + "Enable leader election for controller manager. "+ + "Enabling this will ensure there is only one active controller manager.") + flag.BoolVar(&secureMetrics, "metrics-secure", true, + "If set, the metrics endpoint is served securely via HTTPS. Use --metrics-secure=false to use HTTP instead.") + flag.BoolVar(&enableHTTP2, "enable-http2", false, + "If set, HTTP/2 will be enabled for the metrics and webhook servers") + flag.StringVar(&watchNamespace, "watch-namespace", "", "Namespace to filter the list of watched objects") + flag.StringVar(&watchLabel, "watch-name", "", "Filter the list of watched objects by checking the app.kubernetes.io/managed-by label") + flag.DurationVar(&pipelineCheckTimeout, "pipeline-check-timeout", 15*time.Second, "wait pipeline checks before force vector reconcile. Default: 15s") + flag.DurationVar(&pipelineDeleteEventTimeout, "pipeline-delete-timeout", 5*time.Second, "collect delete events timeout") + flag.DurationVar(&configCheckTimeout, "configcheck-timeout", 300*time.Second, "configcheck timeout") + opts := zap.Options{ + Development: true, + } + opts.BindFlags(flag.CommandLine) + flag.Parse() + + ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) + + // if the enable-http2 flag is false (the default), http/2 should be disabled + // due to its vulnerabilities. More specifically, disabling http/2 will + // prevent from being vulnerable to the HTTP/2 Stream Cancellation and + // Rapid Reset CVEs. For more information see: + // - https://github.com/advisories/GHSA-qppj-fm5r-hxr3 + // - https://github.com/advisories/GHSA-4374-p667-p6c8 + disableHTTP2 := func(c *tls.Config) { + setupLog.Info("disabling http/2") + c.NextProtos = []string{"http/1.1"} + } + + if !enableHTTP2 { + tlsOpts = append(tlsOpts, disableHTTP2) + } + + webhookServer := webhook.NewServer(webhook.Options{ + TLSOpts: tlsOpts, + }) + + // Metrics endpoint is enabled in 'config/default/kustomization.yaml'. The Metrics options configure the server. + // More info: + // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.0/pkg/metrics/server + // - https://book.kubebuilder.io/reference/metrics.html + metricsServerOptions := metricsserver.Options{ + BindAddress: metricsAddr, + SecureServing: secureMetrics, + // TODO(user): TLSOpts is used to allow configuring the TLS config used for the server. If certificates are + // not provided, self-signed certificates will be generated by default. This option is not recommended for + // production environments as self-signed certificates do not offer the same level of trust and security + // as certificates issued by a trusted Certificate Authority (CA). The primary risk is potentially allowing + // unauthorized access to sensitive metrics data. Consider replacing with CertDir, CertName, and KeyName + // to provide certificates, ensuring the server communicates using trusted and secure certificates. + TLSOpts: tlsOpts, + } + + if secureMetrics { + // FilterProvider is used to protect the metrics endpoint with authn/authz. + // These configurations ensure that only authorized users and service accounts + // can access the metrics endpoint. The RBAC are configured in 'config/rbac/kustomization.yaml'. More info: + // https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.0/pkg/metrics/filters#WithAuthenticationAndAuthorization + metricsServerOptions.FilterProvider = filters.WithAuthenticationAndAuthorization + } + + config := ctrl.GetConfigOrDie() + + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + setupLog.Error(err, "unable to create clientset") + os.Exit(1) + } + + dc, err := discovery.NewDiscoveryClientForConfig(config) + if err != nil { + setupLog.Error(err, "unable to create discovery client") + os.Exit(1) + } + + mgrOptions := ctrl.Options{ + Scheme: scheme, + Metrics: metricsServerOptions, + WebhookServer: webhookServer, + HealthProbeBindAddress: probeAddr, + LeaderElection: enableLeaderElection, + LeaderElectionID: "79cbe7f3.kaasops.io", + // LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily + // when the Manager ends. This requires the binary to immediately end when the + // Manager is stopped, otherwise, this setting is unsafe. Setting this significantly + // speeds up voluntary leader transitions as the new leader don't have to wait + // LeaseDuration time first. + // + // In the default scaffold provided, the program ends immediately after + // the manager stops, so would be fine to enable this option. However, + // if you are doing or is intended to do any operation such as perform cleanups + // after the manager stops then its usage might be unsafe. + // LeaderElectionReleaseOnCancel: true, + } + customMgrOptions, err := setupCustomCache(&mgrOptions, watchNamespace, watchLabel) + if err != nil { + setupLog.Error(err, "unable to set up custom cache settings") + os.Exit(1) + } + + mgr, err := ctrl.NewManager(config, *customMgrOptions) + if err != nil { + setupLog.Error(err, "unable to start manager") + os.Exit(1) + } + + if err = (&controller.VectorReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Clientset: clientset, + PipelineCheckWG: &pipelineCheckWG, + PipelineCheckTimeout: pipelineCheckTimeout, + ConfigCheckTimeout: configCheckTimeout, + DiscoveryClient: dc, + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "Vector") + os.Exit(1) + } + if err = (&controller.PipelineReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Clientset: clientset, + PipelineCheckWG: &pipelineCheckWG, + PipelineDeleteEventTimeout: pipelineDeleteEventTimeout, + ConfigCheckTimeout: configCheckTimeout, + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "VectorPipeline") + os.Exit(1) + } + // +kubebuilder:scaffold:builder + + if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { + setupLog.Error(err, "unable to set up health check") + os.Exit(1) + } + if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil { + setupLog.Error(err, "unable to set up ready check") + os.Exit(1) + } + + setupLog.Info("starting manager") + if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { + setupLog.Error(err, "problem running manager") + os.Exit(1) + } +} + +func setupCustomCache(mgrOptions *ctrl.Options, namespace string, watchLabel string) (*ctrl.Options, error) { + if namespace == "" && watchLabel == "" { + return mgrOptions, nil + } + + if namespace == "" { + namespace = cache.AllNamespaces + } + + var labelSelector labels.Selector + if watchLabel != "" { + labelSelector = labels.Set{k8s.ManagedByLabelKey: "vector-operator", k8s.NameLabelKey: watchLabel}.AsSelector() + } else { + labelSelector = labels.Everything() + } + + mgrOptions.Cache = cache.Options{ + ByObject: map[client.Object]cache.ByObject{ + &corev1.Pod{}: { + Namespaces: map[string]cache.Config{ + namespace: { + LabelSelector: labelSelector, + }, + }, + }, + &appsv1.DaemonSet{}: { + Namespaces: map[string]cache.Config{ + namespace: { + LabelSelector: labelSelector, + }, + }, + }, + &corev1.Service{}: { + Namespaces: map[string]cache.Config{ + namespace: { + LabelSelector: labelSelector, + }, + }, + }, + &corev1.Secret{}: { + Namespaces: map[string]cache.Config{ + namespace: { + LabelSelector: labelSelector, + }, + }, + }, + &corev1.ServiceAccount{}: { + Namespaces: map[string]cache.Config{ + namespace: { + LabelSelector: labelSelector, + }, + }, + }, + }, + } + + return mgrOptions, nil +} diff --git a/config/crd/bases/observability.kaasops.io_clustervectorpipelines.yaml b/config/crd/bases/observability.kaasops.io_clustervectorpipelines.yaml index 760cca22..bc1f9bb1 100644 --- a/config/crd/bases/observability.kaasops.io_clustervectorpipelines.yaml +++ b/config/crd/bases/observability.kaasops.io_clustervectorpipelines.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.9.2 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.16.1 name: clustervectorpipelines.observability.kaasops.io spec: group: observability.kaasops.io @@ -31,14 +30,19 @@ spec: API 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' + 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' + 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: type: object diff --git a/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml b/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml index 9861cbd1..7961d1cf 100644 --- a/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml +++ b/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.9.2 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.16.1 name: vectorpipelines.observability.kaasops.io spec: group: observability.kaasops.io @@ -32,14 +31,19 @@ spec: description: VectorPipeline is the Schema for the vectorpipelines API 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' + 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' + 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: type: object diff --git a/config/crd/bases/observability.kaasops.io_vectors.yaml b/config/crd/bases/observability.kaasops.io_vectors.yaml index 2509005f..14862094 100644 --- a/config/crd/bases/observability.kaasops.io_vectors.yaml +++ b/config/crd/bases/observability.kaasops.io_vectors.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.9.2 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.16.1 name: vectors.observability.kaasops.io spec: group: observability.kaasops.io @@ -28,14 +27,19 @@ spec: description: Vector is the Schema for the vectors API 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' + 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' + 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: type: object @@ -43,7 +47,9 @@ spec: description: VectorSpec defines the desired state of Vector properties: agent: - description: DisableAggregation DisableAggregation bool `json:"disableAggregation,omitempty"` + description: |- + DisableAggregation + DisableAggregation bool `json:"disableAggregation,omitempty"` Vector Agent properties: affinity: @@ -54,22 +60,20 @@ spec: the pod. properties: preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule pods - to nodes that satisfy the affinity expressions specified - by this field, but it may choose a node that violates - one or more of the expressions. The node that is most - preferred is the one with the greatest sum of weights, - i.e. for each node that meets all of the scheduling - requirements (resource request, requiredDuringScheduling - affinity expressions, etc.), compute a sum by iterating - through the elements of this field and adding "weight" - to the sum if the node matches the corresponding matchExpressions; - the node(s) with the highest sum are the most preferred. + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. items: - description: An empty preferred scheduling term matches - all objects with implicit weight 0 (i.e. it's a no-op). - A null preferred scheduling term matches no objects - (i.e. is also a no-op). + description: |- + An empty preferred scheduling term matches all objects with implicit weight 0 + (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). properties: preference: description: A node selector term, associated with @@ -79,78 +83,70 @@ spec: description: A list of node selector requirements by node's labels. items: - description: A node selector requirement is - a selector that contains values, a key, - and an operator that relates the key and - values. + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. properties: key: description: The label key that the selector applies to. type: string operator: - description: Represents a key's relationship - to a set of values. Valid operators - are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: - description: An array of string values. - If the operator is In or NotIn, the - values array must be non-empty. If the - operator is Exists or DoesNotExist, - the values array must be empty. If the - operator is Gt or Lt, the values array - must have a single element, which will - be interpreted as an integer. This array - is replaced during a strategic merge - patch. + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchFields: description: A list of node selector requirements by node's fields. items: - description: A node selector requirement is - a selector that contains values, a key, - and an operator that relates the key and - values. + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. properties: key: description: The label key that the selector applies to. type: string operator: - description: Represents a key's relationship - to a set of values. Valid operators - are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: - description: An array of string values. - If the operator is In or NotIn, the - values array must be non-empty. If the - operator is Exists or DoesNotExist, - the values array must be empty. If the - operator is Gt or Lt, the values array - must have a single element, which will - be interpreted as an integer. This array - is replaced during a strategic merge - patch. + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic type: object x-kubernetes-map-type: atomic weight: @@ -163,103 +159,96 @@ spec: - weight type: object type: array + x-kubernetes-list-type: atomic requiredDuringSchedulingIgnoredDuringExecution: - description: If the affinity requirements specified by - this field are not met at scheduling time, the pod will - not be scheduled onto the node. If the affinity requirements - specified by this field cease to be met at some point - during pod execution (e.g. due to an update), the system - may or may not try to eventually evict the pod from - its node. + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from its node. properties: nodeSelectorTerms: description: Required. A list of node selector terms. The terms are ORed. items: - description: A null or empty node selector term - matches no objects. The requirements of them are - ANDed. The TopologySelectorTerm type implements - a subset of the NodeSelectorTerm. + description: |- + A null or empty node selector term matches no objects. The requirements of + them are ANDed. + The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. properties: matchExpressions: description: A list of node selector requirements by node's labels. items: - description: A node selector requirement is - a selector that contains values, a key, - and an operator that relates the key and - values. + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. properties: key: description: The label key that the selector applies to. type: string operator: - description: Represents a key's relationship - to a set of values. Valid operators - are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: - description: An array of string values. - If the operator is In or NotIn, the - values array must be non-empty. If the - operator is Exists or DoesNotExist, - the values array must be empty. If the - operator is Gt or Lt, the values array - must have a single element, which will - be interpreted as an integer. This array - is replaced during a strategic merge - patch. + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchFields: description: A list of node selector requirements by node's fields. items: - description: A node selector requirement is - a selector that contains values, a key, - and an operator that relates the key and - values. + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. properties: key: description: The label key that the selector applies to. type: string operator: - description: Represents a key's relationship - to a set of values. Valid operators - are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: - description: An array of string values. - If the operator is In or NotIn, the - values array must be non-empty. If the - operator is Exists or DoesNotExist, - the values array must be empty. If the - operator is Gt or Lt, the values array - must have a single element, which will - be interpreted as an integer. This array - is replaced during a strategic merge - patch. + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic type: object x-kubernetes-map-type: atomic type: array + x-kubernetes-list-type: atomic required: - nodeSelectorTerms type: object @@ -271,18 +260,16 @@ spec: other pod(s)). properties: preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule pods - to nodes that satisfy the affinity expressions specified - by this field, but it may choose a node that violates - one or more of the expressions. The node that is most - preferred is the one with the greatest sum of weights, - i.e. for each node that meets all of the scheduling - requirements (resource request, requiredDuringScheduling - affinity expressions, etc.), compute a sum by iterating - through the elements of this field and adding "weight" - to the sum if the node has pods which matches the corresponding - podAffinityTerm; the node(s) with the highest sum are - the most preferred. + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. items: description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred @@ -293,143 +280,161 @@ spec: with the corresponding weight. properties: labelSelector: - description: A label query over a set of resources, - in this case pods. + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied - to the union of the namespaces selected by - this field and the ones listed in the namespaces - field. null selector and null or empty namespaces - list means "this pod's namespace". An empty - selector ({}) matches all namespaces. + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: - description: namespaces specifies a static list - of namespace names that the term applies to. - The term is applied to the union of the namespaces - listed in this field and the ones selected - by namespaceSelector. null or empty namespaces - list and null namespaceSelector means "this - pod's namespace". + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array + x-kubernetes-list-type: atomic topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the - pods matching the labelSelector in the specified - namespaces, where co-located is defined as - running on a node whose value of the label - with key topologyKey matches that of any node - on which any of the selected pods is running. + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. Empty topologyKey is not allowed. type: string required: - topologyKey type: object weight: - description: weight associated with matching the - corresponding podAffinityTerm, in the range 1-100. + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. format: int32 type: integer required: @@ -437,157 +442,179 @@ spec: - weight type: object type: array + x-kubernetes-list-type: atomic requiredDuringSchedulingIgnoredDuringExecution: - description: If the affinity requirements specified by - this field are not met at scheduling time, the pod will - not be scheduled onto the node. If the affinity requirements - specified by this field cease to be met at some point - during pod execution (e.g. due to a pod label update), - the system may or may not try to eventually evict the - pod from its node. When there are multiple elements, - the lists of nodes corresponding to each podAffinityTerm - are intersected, i.e. all terms must be satisfied. + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. items: - description: Defines a set of pods (namely those matching - the labelSelector relative to the given namespace(s)) - that this pod should be co-located (affinity) or not - co-located (anti-affinity) with, where co-located - is defined as running on a node whose value of the - label with key matches that of any node - on which a pod of the set of pods is running + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running properties: labelSelector: - description: A label query over a set of resources, - in this case pods. + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, a key, - and an operator that relates the key and - values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a key's - relationship to a set of values. Valid - operators are In, NotIn, Exists and - DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. This - array is replaced during a strategic + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is - "In", and the values array contains only "value". - The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied - to the union of the namespaces selected by this - field and the ones listed in the namespaces field. - null selector and null or empty namespaces list - means "this pod's namespace". An empty selector - ({}) matches all namespaces. + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, a key, - and an operator that relates the key and - values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a key's - relationship to a set of values. Valid - operators are In, NotIn, Exists and - DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. This - array is replaced during a strategic + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is - "In", and the values array contains only "value". - The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: - description: namespaces specifies a static list - of namespace names that the term applies to. The - term is applied to the union of the namespaces - listed in this field and the ones selected by - namespaceSelector. null or empty namespaces list - and null namespaceSelector means "this pod's namespace". + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array + x-kubernetes-list-type: atomic topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the pods - matching the labelSelector in the specified namespaces, - where co-located is defined as running on a node - whose value of the label with key topologyKey - matches that of any node on which any of the selected - pods is running. Empty topologyKey is not allowed. + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. type: string required: - topologyKey type: object type: array + x-kubernetes-list-type: atomic type: object podAntiAffinity: description: Describes pod anti-affinity scheduling rules @@ -595,18 +622,16 @@ spec: as some other pod(s)). properties: preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule pods - to nodes that satisfy the anti-affinity expressions - specified by this field, but it may choose a node that - violates one or more of the expressions. The node that - is most preferred is the one with the greatest sum of - weights, i.e. for each node that meets all of the scheduling - requirements (resource request, requiredDuringScheduling - anti-affinity expressions, etc.), compute a sum by iterating - through the elements of this field and adding "weight" - to the sum if the node has pods which matches the corresponding - podAffinityTerm; the node(s) with the highest sum are - the most preferred. + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the anti-affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. items: description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred @@ -617,143 +642,161 @@ spec: with the corresponding weight. properties: labelSelector: - description: A label query over a set of resources, - in this case pods. + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied - to the union of the namespaces selected by - this field and the ones listed in the namespaces - field. null selector and null or empty namespaces - list means "this pod's namespace". An empty - selector ({}) matches all namespaces. + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: - description: namespaces specifies a static list - of namespace names that the term applies to. - The term is applied to the union of the namespaces - listed in this field and the ones selected - by namespaceSelector. null or empty namespaces - list and null namespaceSelector means "this - pod's namespace". + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array + x-kubernetes-list-type: atomic topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the - pods matching the labelSelector in the specified - namespaces, where co-located is defined as - running on a node whose value of the label - with key topologyKey matches that of any node - on which any of the selected pods is running. + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. Empty topologyKey is not allowed. type: string required: - topologyKey type: object weight: - description: weight associated with matching the - corresponding podAffinityTerm, in the range 1-100. + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. format: int32 type: integer required: @@ -761,157 +804,179 @@ spec: - weight type: object type: array + x-kubernetes-list-type: atomic requiredDuringSchedulingIgnoredDuringExecution: - description: If the anti-affinity requirements specified - by this field are not met at scheduling time, the pod - will not be scheduled onto the node. If the anti-affinity - requirements specified by this field cease to be met - at some point during pod execution (e.g. due to a pod - label update), the system may or may not try to eventually - evict the pod from its node. When there are multiple - elements, the lists of nodes corresponding to each podAffinityTerm - are intersected, i.e. all terms must be satisfied. + description: |- + If the anti-affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the anti-affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. items: - description: Defines a set of pods (namely those matching - the labelSelector relative to the given namespace(s)) - that this pod should be co-located (affinity) or not - co-located (anti-affinity) with, where co-located - is defined as running on a node whose value of the - label with key matches that of any node - on which a pod of the set of pods is running + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running properties: labelSelector: - description: A label query over a set of resources, - in this case pods. + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, a key, - and an operator that relates the key and - values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a key's - relationship to a set of values. Valid - operators are In, NotIn, Exists and - DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. This - array is replaced during a strategic + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is - "In", and the values array contains only "value". - The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied - to the union of the namespaces selected by this - field and the ones listed in the namespaces field. - null selector and null or empty namespaces list - means "this pod's namespace". An empty selector - ({}) matches all namespaces. + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, a key, - and an operator that relates the key and - values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a key's - relationship to a set of values. Valid - operators are In, NotIn, Exists and - DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. This - array is replaced during a strategic + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is - "In", and the values array contains only "value". - The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: - description: namespaces specifies a static list - of namespace names that the term applies to. The - term is applied to the union of the namespaces - listed in this field and the ones selected by - namespaceSelector. null or empty namespaces list - and null namespaceSelector means "this pod's namespace". + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array + x-kubernetes-list-type: atomic topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the pods - matching the labelSelector in the specified namespaces, - where co-located is defined as running on a node - whose value of the label with key topologyKey - matches that of any node on which any of the selected - pods is running. Empty topologyKey is not allowed. + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. type: string required: - topologyKey type: object type: array + x-kubernetes-list-type: atomic type: object type: object annotations: @@ -922,15 +987,16 @@ spec: retrieve arbitrary metadata. type: object api: - description: Vector API params. Allows to interact with a running - Vector instance. https://vector.dev/docs/reference/api/ + description: |- + Vector API params. Allows to interact with a running Vector instance. + https://vector.dev/docs/reference/api/ properties: enabled: type: boolean healthcheck: - description: Enable ReadinessProbe and LivenessProbe via api - /health endpoint. If probe enabled via VectorAgent, this - setting will be ignored for that probe. + description: |- + Enable ReadinessProbe and LivenessProbe via api /health endpoint. + If probe enabled via VectorAgent, this setting will be ignored for that probe. type: boolean playground: type: boolean @@ -950,23 +1016,20 @@ spec: for the pod. properties: preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule - pods to nodes that satisfy the affinity expressions - specified by this field, but it may choose a node - that violates one or more of the expressions. The - node that is most preferred is the one with the - greatest sum of weights, i.e. for each node that - meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, - etc.), compute a sum by iterating through the elements - of this field and adding "weight" to the sum if - the node matches the corresponding matchExpressions; - the node(s) with the highest sum are the most preferred. + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. items: - description: An empty preferred scheduling term - matches all objects with implicit weight 0 (i.e. - it's a no-op). A null preferred scheduling term - matches no objects (i.e. is also a no-op). + description: |- + An empty preferred scheduling term matches all objects with implicit weight 0 + (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). properties: preference: description: A node selector term, associated @@ -976,78 +1039,70 @@ spec: description: A list of node selector requirements by node's labels. items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. properties: key: description: The label key that the selector applies to. type: string operator: - description: Represents a key's relationship - to a set of values. Valid operators - are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: - description: An array of string values. - If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. - If the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchFields: description: A list of node selector requirements by node's fields. items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. properties: key: description: The label key that the selector applies to. type: string operator: - description: Represents a key's relationship - to a set of values. Valid operators - are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: - description: An array of string values. - If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. - If the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic type: object x-kubernetes-map-type: atomic weight: @@ -1061,103 +1116,96 @@ spec: - weight type: object type: array + x-kubernetes-list-type: atomic requiredDuringSchedulingIgnoredDuringExecution: - description: If the affinity requirements specified - by this field are not met at scheduling time, the - pod will not be scheduled onto the node. If the - affinity requirements specified by this field cease - to be met at some point during pod execution (e.g. - due to an update), the system may or may not try - to eventually evict the pod from its node. + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from its node. properties: nodeSelectorTerms: description: Required. A list of node selector terms. The terms are ORed. items: - description: A null or empty node selector term - matches no objects. The requirements of them - are ANDed. The TopologySelectorTerm type implements - a subset of the NodeSelectorTerm. + description: |- + A null or empty node selector term matches no objects. The requirements of + them are ANDed. + The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. properties: matchExpressions: description: A list of node selector requirements by node's labels. items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. properties: key: description: The label key that the selector applies to. type: string operator: - description: Represents a key's relationship - to a set of values. Valid operators - are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: - description: An array of string values. - If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. - If the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchFields: description: A list of node selector requirements by node's fields. items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. properties: key: description: The label key that the selector applies to. type: string operator: - description: Represents a key's relationship - to a set of values. Valid operators - are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: - description: An array of string values. - If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. - If the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic type: object x-kubernetes-map-type: atomic type: array + x-kubernetes-list-type: atomic required: - nodeSelectorTerms type: object @@ -1169,19 +1217,16 @@ spec: other pod(s)). properties: preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule - pods to nodes that satisfy the affinity expressions - specified by this field, but it may choose a node - that violates one or more of the expressions. The - node that is most preferred is the one with the - greatest sum of weights, i.e. for each node that - meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, - etc.), compute a sum by iterating through the elements - of this field and adding "weight" to the sum if - the node has pods which matches the corresponding - podAffinityTerm; the node(s) with the highest sum - are the most preferred. + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. items: description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred @@ -1192,18 +1237,18 @@ spec: associated with the corresponding weight. properties: labelSelector: - description: A label query over a set of - resources, in this case pods. + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label @@ -1211,61 +1256,82 @@ spec: to. type: string operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. - This array is replaced during - a strategic merge patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string - description: matchLabels is a map of - {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic namespaceSelector: - description: A label query over the set - of namespaces that the term applies to. - The term is applied to the union of the - namespaces selected by this field and - the ones listed in the namespaces field. - null selector and null or empty namespaces - list means "this pod's namespace". An - empty selector ({}) matches all namespaces. + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label @@ -1273,71 +1339,61 @@ spec: to. type: string operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. - This array is replaced during - a strategic merge patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string - description: matchLabels is a map of - {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: - description: namespaces specifies a static - list of namespace names that the term - applies to. The term is applied to the - union of the namespaces listed in this - field and the ones selected by namespaceSelector. - null or empty namespaces list and null - namespaceSelector means "this pod's namespace". + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array + x-kubernetes-list-type: atomic topologyKey: - description: This pod should be co-located - (affinity) or not co-located (anti-affinity) - with the pods matching the labelSelector - in the specified namespaces, where co-located - is defined as running on a node whose - value of the label with key topologyKey - matches that of any node on which any - of the selected pods is running. Empty - topologyKey is not allowed. + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. type: string required: - topologyKey type: object weight: - description: weight associated with matching - the corresponding podAffinityTerm, in the - range 1-100. + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. format: int32 type: integer required: @@ -1345,163 +1401,179 @@ spec: - weight type: object type: array + x-kubernetes-list-type: atomic requiredDuringSchedulingIgnoredDuringExecution: - description: If the affinity requirements specified - by this field are not met at scheduling time, the - pod will not be scheduled onto the node. If the - affinity requirements specified by this field cease - to be met at some point during pod execution (e.g. - due to a pod label update), the system may or may - not try to eventually evict the pod from its node. - When there are multiple elements, the lists of nodes - corresponding to each podAffinityTerm are intersected, - i.e. all terms must be satisfied. + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. items: - description: Defines a set of pods (namely those - matching the labelSelector relative to the given - namespace(s)) that this pod should be co-located - (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node - whose value of the label with key - matches that of any node on which a pod of the - set of pods is running + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running properties: labelSelector: - description: A label query over a set of resources, - in this case pods. + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied - to the union of the namespaces selected by - this field and the ones listed in the namespaces - field. null selector and null or empty namespaces - list means "this pod's namespace". An empty - selector ({}) matches all namespaces. + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: - description: namespaces specifies a static list - of namespace names that the term applies to. - The term is applied to the union of the namespaces - listed in this field and the ones selected - by namespaceSelector. null or empty namespaces - list and null namespaceSelector means "this - pod's namespace". + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array + x-kubernetes-list-type: atomic topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the - pods matching the labelSelector in the specified - namespaces, where co-located is defined as - running on a node whose value of the label - with key topologyKey matches that of any node - on which any of the selected pods is running. + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. Empty topologyKey is not allowed. type: string required: - topologyKey type: object type: array + x-kubernetes-list-type: atomic type: object podAntiAffinity: description: Describes pod anti-affinity scheduling rules @@ -1509,19 +1581,16 @@ spec: etc. as some other pod(s)). properties: preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule - pods to nodes that satisfy the anti-affinity expressions - specified by this field, but it may choose a node - that violates one or more of the expressions. The - node that is most preferred is the one with the - greatest sum of weights, i.e. for each node that - meets all of the scheduling requirements (resource - request, requiredDuringScheduling anti-affinity - expressions, etc.), compute a sum by iterating through - the elements of this field and adding "weight" to - the sum if the node has pods which matches the corresponding - podAffinityTerm; the node(s) with the highest sum - are the most preferred. + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the anti-affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. items: description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred @@ -1532,18 +1601,18 @@ spec: associated with the corresponding weight. properties: labelSelector: - description: A label query over a set of - resources, in this case pods. + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label @@ -1551,61 +1620,82 @@ spec: to. type: string operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. - This array is replaced during - a strategic merge patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string - description: matchLabels is a map of - {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic namespaceSelector: - description: A label query over the set - of namespaces that the term applies to. - The term is applied to the union of the - namespaces selected by this field and - the ones listed in the namespaces field. - null selector and null or empty namespaces - list means "this pod's namespace". An - empty selector ({}) matches all namespaces. + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label @@ -1613,71 +1703,61 @@ spec: to. type: string operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. - This array is replaced during - a strategic merge patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string - description: matchLabels is a map of - {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: - description: namespaces specifies a static - list of namespace names that the term - applies to. The term is applied to the - union of the namespaces listed in this - field and the ones selected by namespaceSelector. - null or empty namespaces list and null - namespaceSelector means "this pod's namespace". + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array + x-kubernetes-list-type: atomic topologyKey: - description: This pod should be co-located - (affinity) or not co-located (anti-affinity) - with the pods matching the labelSelector - in the specified namespaces, where co-located - is defined as running on a node whose - value of the label with key topologyKey - matches that of any node on which any - of the selected pods is running. Empty - topologyKey is not allowed. + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. type: string required: - topologyKey type: object weight: - description: weight associated with matching - the corresponding podAffinityTerm, in the - range 1-100. + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. format: int32 type: integer required: @@ -1685,163 +1765,179 @@ spec: - weight type: object type: array + x-kubernetes-list-type: atomic requiredDuringSchedulingIgnoredDuringExecution: - description: If the anti-affinity requirements specified - by this field are not met at scheduling time, the - pod will not be scheduled onto the node. If the - anti-affinity requirements specified by this field - cease to be met at some point during pod execution - (e.g. due to a pod label update), the system may - or may not try to eventually evict the pod from - its node. When there are multiple elements, the - lists of nodes corresponding to each podAffinityTerm - are intersected, i.e. all terms must be satisfied. + description: |- + If the anti-affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the anti-affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. items: - description: Defines a set of pods (namely those - matching the labelSelector relative to the given - namespace(s)) that this pod should be co-located - (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node - whose value of the label with key - matches that of any node on which a pod of the - set of pods is running + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running properties: labelSelector: - description: A label query over a set of resources, - in this case pods. + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied - to the union of the namespaces selected by - this field and the ones listed in the namespaces - field. null selector and null or empty namespaces - list means "this pod's namespace". An empty - selector ({}) matches all namespaces. + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: - description: namespaces specifies a static list - of namespace names that the term applies to. - The term is applied to the union of the namespaces - listed in this field and the ones selected - by namespaceSelector. null or empty namespaces - list and null namespaceSelector means "this - pod's namespace". + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array + x-kubernetes-list-type: atomic topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the - pods matching the labelSelector in the specified - namespaces, where co-located is defined as - running on a node whose value of the label - with key topologyKey matches that of any node - on which any of the selected pods is running. + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. Empty topologyKey is not allowed. type: string required: - topologyKey type: object type: array + x-kubernetes-list-type: atomic type: object type: object annotations: @@ -1854,14 +1950,46 @@ spec: disabled: type: boolean image: - description: Image - docker image settings for Vector Agent + description: |- + Image - docker image settings for Vector Agent if no specified operator uses default config version type: string resources: - description: Resources container resource request and limits, - https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + description: |- + Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ if not specified - default setting will be used properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map limits: additionalProperties: anyOf: @@ -1869,8 +1997,9 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute - resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object requests: additionalProperties: @@ -1879,52 +2008,49 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of - compute resources required. If Requests is omitted for - a container, it defaults to Limits if that is explicitly - specified, otherwise to an implementation-defined value. - More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object type: object tolerations: description: Tolerations If specified, the pod's tolerations. items: - description: The pod this Toleration is attached to tolerates - any taint that matches the triple using - the matching operator . + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . properties: effect: - description: Effect indicates the taint effect to match. - Empty means match all taint effects. When specified, - allowed values are NoSchedule, PreferNoSchedule and - NoExecute. + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. type: string key: - description: Key is the taint key that the toleration - applies to. Empty means match all taint keys. If the - key is empty, operator must be Exists; this combination - means to match all values and all keys. + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. type: string operator: - description: Operator represents a key's relationship - to the value. Valid operators are Exists and Equal. - Defaults to Equal. Exists is equivalent to wildcard - for value, so that a pod can tolerate all taints of - a particular category. + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. type: string tolerationSeconds: - description: TolerationSeconds represents the period - of time the toleration (which must be of effect NoExecute, - otherwise this field is ignored) tolerates the taint. - By default, it is not set, which means tolerate the - taint forever (do not evict). Zero and negative values - will be treated as 0 (evict immediately) by the system. + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. format: int64 type: integer value: - description: Value is the taint value the toleration - matches to. If the operator is Exists, the value should - be empty, otherwise just a regular string. + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. type: string type: object type: array @@ -1935,6 +2061,37 @@ spec: description: ResourceRequirements describes the compute resource requirements. properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map limits: additionalProperties: anyOf: @@ -1942,8 +2099,9 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute - resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object requests: additionalProperties: @@ -1952,32 +2110,58 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, otherwise - to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object type: object containerSecurityContext: - description: SecurityContext holds security configuration that - will be applied to a container. Some fields are present in both - SecurityContext and PodSecurityContext. When both are set, the - values in SecurityContext take precedence. + description: |- + SecurityContext holds security configuration that will be applied to a container. + Some fields are present in both SecurityContext and PodSecurityContext. + When both are set, the values in SecurityContext take precedence. properties: allowPrivilegeEscalation: - description: 'AllowPrivilegeEscalation controls whether a - process can gain more privileges than its parent process. - This bool directly controls if the no_new_privs flag will - be set on the container process. AllowPrivilegeEscalation - is true always when the container is: 1) run as Privileged - 2) has CAP_SYS_ADMIN Note that this field cannot be set - when spec.os.name is windows.' + description: |- + AllowPrivilegeEscalation controls whether a process can gain more + privileges than its parent process. This bool directly controls if + the no_new_privs flag will be set on the container process. + AllowPrivilegeEscalation is true always when the container is: + 1) run as Privileged + 2) has CAP_SYS_ADMIN + Note that this field cannot be set when spec.os.name is windows. type: boolean + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by this container. If set, this profile + overrides the pod's appArmorProfile. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object capabilities: - description: The capabilities to add/drop when running containers. - Defaults to the default set of capabilities granted by the - container runtime. Note that this field cannot be set when - spec.os.name is windows. + description: |- + The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container runtime. + Note that this field cannot be set when spec.os.name is windows. properties: add: description: Added capabilities @@ -1986,6 +2170,7 @@ spec: type type: string type: array + x-kubernetes-list-type: atomic drop: description: Removed capabilities items: @@ -1993,61 +2178,63 @@ spec: type type: string type: array + x-kubernetes-list-type: atomic type: object privileged: - description: Run container in privileged mode. Processes in - privileged containers are essentially equivalent to root - on the host. Defaults to false. Note that this field cannot - be set when spec.os.name is windows. + description: |- + Run container in privileged mode. + Processes in privileged containers are essentially equivalent to root on the host. + Defaults to false. + Note that this field cannot be set when spec.os.name is windows. type: boolean procMount: - description: procMount denotes the type of proc mount to use - for the containers. The default is DefaultProcMount which - uses the container runtime defaults for readonly paths and - masked paths. This requires the ProcMountType feature flag - to be enabled. Note that this field cannot be set when spec.os.name - is windows. + description: |- + procMount denotes the type of proc mount to use for the containers. + The default value is Default which uses the container runtime defaults for + readonly paths and masked paths. + This requires the ProcMountType feature flag to be enabled. + Note that this field cannot be set when spec.os.name is windows. type: string readOnlyRootFilesystem: - description: Whether this container has a read-only root filesystem. - Default is false. Note that this field cannot be set when - spec.os.name is windows. + description: |- + Whether this container has a read-only root filesystem. + Default is false. + Note that this field cannot be set when spec.os.name is windows. type: boolean runAsGroup: - description: The GID to run the entrypoint of the container - process. Uses runtime default if unset. May also be set - in PodSecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext - takes precedence. Note that this field cannot be set when - spec.os.name is windows. + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. format: int64 type: integer runAsNonRoot: - description: Indicates that the container must run as a non-root - user. If true, the Kubelet will validate the image at runtime - to ensure that it does not run as UID 0 (root) and fail - to start the container if it does. If unset or false, no - such validation will be performed. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, the - value specified in SecurityContext takes precedence. + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. type: boolean runAsUser: - description: The UID to run the entrypoint of the container - process. Defaults to user specified in image metadata if - unspecified. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, the - value specified in SecurityContext takes precedence. Note - that this field cannot be set when spec.os.name is windows. + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. format: int64 type: integer seLinuxOptions: - description: The SELinux context to be applied to the container. - If unspecified, the container runtime will allocate a random - SELinux context for each container. May also be set in - PodSecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext - takes precedence. Note that this field cannot be set when - spec.os.name is windows. + description: |- + The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. properties: level: description: Level is SELinux level label that applies @@ -2067,72 +2254,67 @@ spec: type: string type: object seccompProfile: - description: The seccomp options to use by this container. - If seccomp options are provided at both the pod & container - level, the container options override the pod options. Note - that this field cannot be set when spec.os.name is windows. + description: |- + The seccomp options to use by this container. If seccomp options are + provided at both the pod & container level, the container options + override the pod options. + Note that this field cannot be set when spec.os.name is windows. properties: localhostProfile: - description: localhostProfile indicates a profile defined - in a file on the node should be used. The profile must - be preconfigured on the node to work. Must be a descending - path, relative to the kubelet's configured seccomp profile - location. Must only be set if type is "Localhost". + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. type: string type: - description: "type indicates which kind of seccomp profile - will be applied. Valid options are: \n Localhost - a - profile defined in a file on the node should be used. - RuntimeDefault - the container runtime default profile - should be used. Unconfined - no profile should be applied." + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. type: string required: - type type: object windowsOptions: - description: The Windows specific settings applied to all - containers. If unspecified, the options from the PodSecurityContext - will be used. If set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence. - Note that this field cannot be set when spec.os.name is - linux. + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options from the PodSecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. properties: gmsaCredentialSpec: - description: GMSACredentialSpec is where the GMSA admission - webhook (https://github.com/kubernetes-sigs/windows-gmsa) - inlines the contents of the GMSA credential spec named - by the GMSACredentialSpecName field. + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. type: string gmsaCredentialSpecName: description: GMSACredentialSpecName is the name of the GMSA credential spec to use. type: string hostProcess: - description: HostProcess determines if a container should - be run as a 'Host Process' container. This field is - alpha-level and will only be honored by components that - enable the WindowsHostProcessContainers feature flag. - Setting this field without the feature flag will result - in errors when validating the Pod. All of a Pod's containers - must have the same effective HostProcess value (it is - not allowed to have a mix of HostProcess containers - and non-HostProcess containers). In addition, if HostProcess - is true then HostNetwork must also be set to true. + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. type: boolean runAsUserName: - description: The UserName in Windows to run the entrypoint - of the container process. Defaults to the user specified - in image metadata if unspecified. May also be set in - PodSecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext - takes precedence. + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. type: string type: object type: object dataDir: - description: The directory used for persisting Vector state, such - as on-disk buffers, file checkpoints, and more. Please make - sure the Vector project has write permissions to this directory. + description: |- + The directory used for persisting Vector state, such as on-disk buffers, file checkpoints, and more. Please make sure the Vector project has write permissions to this directory. https://vector.dev/docs/reference/configuration/global-options/#data_dir type: string env: @@ -2146,15 +2328,16 @@ spec: C_IDENTIFIER. type: string value: - description: 'Variable references $(VAR_NAME) are expanded - using the previously defined environment variables in - the container and any service environment variables. If - a variable cannot be resolved, the reference in the input - string will be unchanged. Double $$ are reduced to a single - $, which allows for escaping the $(VAR_NAME) syntax: i.e. + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". - Escaped references will never be expanded, regardless - of whether the variable exists or not. Defaults to "".' + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". type: string valueFrom: description: Source for the environment variable's value. @@ -2167,9 +2350,13 @@ spec: description: The key to select. type: string name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string optional: description: Specify whether the ConfigMap or its @@ -2180,11 +2367,9 @@ spec: type: object x-kubernetes-map-type: atomic fieldRef: - description: 'Selects a field of the pod: supports metadata.name, - metadata.namespace, `metadata.labels['''']`, - `metadata.annotations['''']`, spec.nodeName, - spec.serviceAccountName, status.hostIP, status.podIP, - status.podIPs.' + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. properties: apiVersion: description: Version of the schema the FieldPath @@ -2199,10 +2384,9 @@ spec: type: object x-kubernetes-map-type: atomic resourceFieldRef: - description: 'Selects a resource of the container: only - resources limits and requests (limits.cpu, limits.memory, - limits.ephemeral-storage, requests.cpu, requests.memory - and requests.ephemeral-storage) are currently supported.' + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. properties: containerName: description: 'Container name: required for volumes, @@ -2232,9 +2416,13 @@ spec: be a valid secret key. type: string name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string optional: description: Specify whether the Secret or its key @@ -2250,20 +2438,26 @@ spec: type: object type: array host_aliases: - description: HostAliases provides mapping between ip and hostnames, - that would be propagated to pod, cannot be used with HostNetwork. + description: |- + HostAliases provides mapping between ip and hostnames, + that would be propagated to pod, + cannot be used with HostNetwork. items: - description: HostAlias holds the mapping between IP and hostnames - that will be injected as an entry in the pod's hosts file. + description: |- + HostAlias holds the mapping between IP and hostnames that will be injected as an entry in the + pod's hosts file. properties: hostnames: description: Hostnames for the above IP address. items: type: string type: array + x-kubernetes-list-type: atomic ip: description: IP address of the host file entry. type: string + required: + - ip type: object type: array hostNetwork: @@ -2271,23 +2465,31 @@ spec: node network namespace type: boolean image: - description: Image - docker image settings for Vector Agent if - no specified operator uses default config version + description: |- + Image - docker image settings for Vector Agent + if no specified operator uses default config version type: string imagePullPolicy: description: ImagePullPolicy of pods type: string imagePullSecrets: - description: ImagePullSecrets An optional list of references to - secrets in the same namespace to use for pulling images from - registries see http://kubernetes.io/docs/user-guide/images#specifying-imagepullsecrets-on-a-pod + description: |- + ImagePullSecrets An optional list of references to secrets in the same namespace + to use for pulling images from registries + see http://kubernetes.io/docs/user-guide/images#specifying-imagepullsecrets-on-a-pod items: - description: LocalObjectReference contains enough information - to let you locate the referenced object inside the same namespace. + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. properties: name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string type: object x-kubernetes-map-type: atomic @@ -2303,28 +2505,25 @@ spec: description: Exec specifies the action to take. properties: command: - description: Command is the command line to execute inside - the container, the working directory for the command is - root ('/') in the container's filesystem. The command - is simply exec'd, it is not run inside a shell, so traditional - shell instructions ('|', etc) won't work. To use a shell, - you need to explicitly call out to that shell. Exit - status of 0 is treated as live/healthy and non-zero - is unhealthy. + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. items: type: string type: array + x-kubernetes-list-type: atomic type: object failureThreshold: - description: Minimum consecutive failures for the probe to - be considered failed after having succeeded. Defaults to - 3. Minimum value is 1. + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. format: int32 type: integer grpc: description: GRPC specifies an action involving a GRPC port. - This is a beta field and requires enabling GRPCContainerProbe - feature gate. properties: port: description: Port number of the gRPC service. Number must @@ -2332,10 +2531,12 @@ spec: format: int32 type: integer service: - description: "Service is the name of the service to place - in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). - \n If this is not specified, the default behavior is - defined by gRPC." + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. type: string required: - port @@ -2344,9 +2545,9 @@ spec: description: HTTPGet specifies the http request to perform. properties: host: - description: Host name to connect to, defaults to the - pod IP. You probably want to set "Host" in httpHeaders - instead. + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. type: string httpHeaders: description: Custom headers to set in the request. HTTP @@ -2356,7 +2557,9 @@ spec: be used in HTTP probes properties: name: - description: The header field name + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. type: string value: description: The header field value @@ -2366,6 +2569,7 @@ spec: - value type: object type: array + x-kubernetes-list-type: atomic path: description: Path to access on the HTTP server. type: string @@ -2373,32 +2577,35 @@ spec: anyOf: - type: integer - type: string - description: Name or number of the port to access on the - container. Number must be in the range 1 to 65535. Name - must be an IANA_SVC_NAME. + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true scheme: - description: Scheme to use for connecting to the host. + description: |- + Scheme to use for connecting to the host. Defaults to HTTP. type: string required: - port type: object initialDelaySeconds: - description: 'Number of seconds after the container has started - before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes format: int32 type: integer periodSeconds: - description: How often (in seconds) to perform the probe. + description: |- + How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1. format: int32 type: integer successThreshold: - description: Minimum consecutive successes for the probe to - be considered successful after having failed. Defaults to - 1. Must be 1 for liveness and startup. Minimum value is - 1. + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. format: int32 type: integer tcpSocket: @@ -2413,100 +2620,125 @@ spec: anyOf: - type: integer - type: string - description: Number or name of the port to access on the - container. Number must be in the range 1 to 65535. Name - must be an IANA_SVC_NAME. + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true required: - port type: object terminationGracePeriodSeconds: - description: Optional duration in seconds the pod needs to - terminate gracefully upon probe failure. The grace period - is the duration in seconds after the processes running in - the pod are sent a termination signal and the time when - the processes are forcibly halted with a kill signal. Set - this value longer than the expected cleanup time for your - process. If this value is nil, the pod's terminationGracePeriodSeconds - will be used. Otherwise, this value overrides the value - provided by the pod spec. Value must be non-negative integer. - The value zero indicates stop immediately via the kill signal - (no opportunity to shut down). This is a beta field and - requires enabling ProbeTerminationGracePeriod feature gate. - Minimum value is 1. spec.terminationGracePeriodSeconds is - used if unset. + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. format: int64 type: integer timeoutSeconds: - description: 'Number of seconds after which the probe times - out. Defaults to 1 second. Minimum value is 1. More info: - https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes format: int32 type: integer type: object podSecurityContext: - description: SecurityContext holds pod-level security attributes - and common container settings. This defaults to the default - PodSecurityContext. + description: |- + SecurityContext holds pod-level security attributes and common container settings. + This defaults to the default PodSecurityContext. properties: + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by the containers in this pod. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object fsGroup: - description: "A special supplemental group that applies to - all containers in a pod. Some volume types allow the Kubelet - to change the ownership of that volume to be owned by the - pod: \n 1. The owning GID will be the FSGroup 2. The setgid - bit is set (new files created in the volume will be owned - by FSGroup) 3. The permission bits are OR'd with rw-rw---- - \n If unset, the Kubelet will not modify the ownership and - permissions of any volume. Note that this field cannot be - set when spec.os.name is windows." + description: |- + A special supplemental group that applies to all containers in a pod. + Some volume types allow the Kubelet to change the ownership of that volume + to be owned by the pod: + + 1. The owning GID will be the FSGroup + 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) + 3. The permission bits are OR'd with rw-rw---- + + If unset, the Kubelet will not modify the ownership and permissions of any volume. + Note that this field cannot be set when spec.os.name is windows. format: int64 type: integer fsGroupChangePolicy: - description: 'fsGroupChangePolicy defines behavior of changing - ownership and permission of the volume before being exposed - inside Pod. This field will only apply to volume types which - support fsGroup based ownership(and permissions). It will - have no effect on ephemeral volume types such as: secret, - configmaps and emptydir. Valid values are "OnRootMismatch" - and "Always". If not specified, "Always" is used. Note that - this field cannot be set when spec.os.name is windows.' + description: |- + fsGroupChangePolicy defines behavior of changing ownership and permission of the volume + before being exposed inside Pod. This field will only apply to + volume types which support fsGroup based ownership(and permissions). + It will have no effect on ephemeral volume types such as: secret, configmaps + and emptydir. + Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used. + Note that this field cannot be set when spec.os.name is windows. type: string runAsGroup: - description: The GID to run the entrypoint of the container - process. Uses runtime default if unset. May also be set - in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext - takes precedence for that container. Note that this field - cannot be set when spec.os.name is windows. + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. format: int64 type: integer runAsNonRoot: - description: Indicates that the container must run as a non-root - user. If true, the Kubelet will validate the image at runtime - to ensure that it does not run as UID 0 (root) and fail - to start the container if it does. If unset or false, no - such validation will be performed. May also be set in SecurityContext. If - set in both SecurityContext and PodSecurityContext, the - value specified in SecurityContext takes precedence. + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. type: boolean runAsUser: - description: The UID to run the entrypoint of the container - process. Defaults to user specified in image metadata if - unspecified. May also be set in SecurityContext. If set - in both SecurityContext and PodSecurityContext, the value - specified in SecurityContext takes precedence for that container. - Note that this field cannot be set when spec.os.name is - windows. + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. format: int64 type: integer seLinuxOptions: - description: The SELinux context to be applied to all containers. - If unspecified, the container runtime will allocate a random - SELinux context for each container. May also be set in - SecurityContext. If set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence - for that container. Note that this field cannot be set when - spec.os.name is windows. + description: |- + The SELinux context to be applied to all containers. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in SecurityContext. If set in + both SecurityContext and PodSecurityContext, the value specified in SecurityContext + takes precedence for that container. + Note that this field cannot be set when spec.os.name is windows. properties: level: description: Level is SELinux level label that applies @@ -2526,42 +2758,58 @@ spec: type: string type: object seccompProfile: - description: The seccomp options to use by the containers - in this pod. Note that this field cannot be set when spec.os.name - is windows. + description: |- + The seccomp options to use by the containers in this pod. + Note that this field cannot be set when spec.os.name is windows. properties: localhostProfile: - description: localhostProfile indicates a profile defined - in a file on the node should be used. The profile must - be preconfigured on the node to work. Must be a descending - path, relative to the kubelet's configured seccomp profile - location. Must only be set if type is "Localhost". + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. type: string type: - description: "type indicates which kind of seccomp profile - will be applied. Valid options are: \n Localhost - a - profile defined in a file on the node should be used. - RuntimeDefault - the container runtime default profile - should be used. Unconfined - no profile should be applied." + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. type: string required: - type type: object supplementalGroups: - description: A list of groups applied to the first process - run in each container, in addition to the container's primary - GID. If unspecified, no groups will be added to any container. - Note that this field cannot be set when spec.os.name is - windows. + description: |- + A list of groups applied to the first process run in each container, in + addition to the container's primary GID and fsGroup (if specified). If + the SupplementalGroupsPolicy feature is enabled, the + supplementalGroupsPolicy field determines whether these are in addition + to or instead of any group memberships defined in the container image. + If unspecified, no additional groups are added, though group memberships + defined in the container image may still be used, depending on the + supplementalGroupsPolicy field. + Note that this field cannot be set when spec.os.name is windows. items: format: int64 type: integer type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + description: |- + Defines how supplemental groups of the first container processes are calculated. + Valid values are "Merge" and "Strict". If not specified, "Merge" is used. + (Alpha) Using the field requires the SupplementalGroupsPolicy feature gate to be enabled + and the container runtime must implement support for this feature. + Note that this field cannot be set when spec.os.name is windows. + type: string sysctls: - description: Sysctls hold a list of namespaced sysctls used - for the pod. Pods with unsupported sysctls (by the container - runtime) might fail to launch. Note that this field cannot - be set when spec.os.name is windows. + description: |- + Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported + sysctls (by the container runtime) might fail to launch. + Note that this field cannot be set when spec.os.name is windows. items: description: Sysctl defines a kernel parameter to be set properties: @@ -2576,48 +2824,43 @@ spec: - value type: object type: array + x-kubernetes-list-type: atomic windowsOptions: - description: The Windows specific settings applied to all - containers. If unspecified, the options within a container's - SecurityContext will be used. If set in both SecurityContext - and PodSecurityContext, the value specified in SecurityContext - takes precedence. Note that this field cannot be set when - spec.os.name is linux. + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options within a container's SecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. properties: gmsaCredentialSpec: - description: GMSACredentialSpec is where the GMSA admission - webhook (https://github.com/kubernetes-sigs/windows-gmsa) - inlines the contents of the GMSA credential spec named - by the GMSACredentialSpecName field. + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. type: string gmsaCredentialSpecName: description: GMSACredentialSpecName is the name of the GMSA credential spec to use. type: string hostProcess: - description: HostProcess determines if a container should - be run as a 'Host Process' container. This field is - alpha-level and will only be honored by components that - enable the WindowsHostProcessContainers feature flag. - Setting this field without the feature flag will result - in errors when validating the Pod. All of a Pod's containers - must have the same effective HostProcess value (it is - not allowed to have a mix of HostProcess containers - and non-HostProcess containers). In addition, if HostProcess - is true then HostNetwork must also be set to true. + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. type: boolean runAsUserName: - description: The UserName in Windows to run the entrypoint - of the container process. Defaults to the user specified - in image metadata if unspecified. May also be set in - PodSecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext - takes precedence. + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. type: string type: object type: object podSecurityPolicyName: - description: PodSecurityPolicyName - defines name for podSecurityPolicy + description: |- + PodSecurityPolicyName - defines name for podSecurityPolicy in case of empty value, prefixedName will be used. type: string priorityClassName: @@ -2631,28 +2874,25 @@ spec: description: Exec specifies the action to take. properties: command: - description: Command is the command line to execute inside - the container, the working directory for the command is - root ('/') in the container's filesystem. The command - is simply exec'd, it is not run inside a shell, so traditional - shell instructions ('|', etc) won't work. To use a shell, - you need to explicitly call out to that shell. Exit - status of 0 is treated as live/healthy and non-zero - is unhealthy. + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. items: type: string type: array + x-kubernetes-list-type: atomic type: object failureThreshold: - description: Minimum consecutive failures for the probe to - be considered failed after having succeeded. Defaults to - 3. Minimum value is 1. + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. format: int32 type: integer grpc: description: GRPC specifies an action involving a GRPC port. - This is a beta field and requires enabling GRPCContainerProbe - feature gate. properties: port: description: Port number of the gRPC service. Number must @@ -2660,10 +2900,12 @@ spec: format: int32 type: integer service: - description: "Service is the name of the service to place - in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). - \n If this is not specified, the default behavior is - defined by gRPC." + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. type: string required: - port @@ -2672,9 +2914,9 @@ spec: description: HTTPGet specifies the http request to perform. properties: host: - description: Host name to connect to, defaults to the - pod IP. You probably want to set "Host" in httpHeaders - instead. + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. type: string httpHeaders: description: Custom headers to set in the request. HTTP @@ -2684,7 +2926,9 @@ spec: be used in HTTP probes properties: name: - description: The header field name + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. type: string value: description: The header field value @@ -2694,6 +2938,7 @@ spec: - value type: object type: array + x-kubernetes-list-type: atomic path: description: Path to access on the HTTP server. type: string @@ -2701,32 +2946,35 @@ spec: anyOf: - type: integer - type: string - description: Name or number of the port to access on the - container. Number must be in the range 1 to 65535. Name - must be an IANA_SVC_NAME. + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true scheme: - description: Scheme to use for connecting to the host. + description: |- + Scheme to use for connecting to the host. Defaults to HTTP. type: string required: - port type: object initialDelaySeconds: - description: 'Number of seconds after the container has started - before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes format: int32 type: integer periodSeconds: - description: How often (in seconds) to perform the probe. + description: |- + How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1. format: int32 type: integer successThreshold: - description: Minimum consecutive successes for the probe to - be considered successful after having failed. Defaults to - 1. Must be 1 for liveness and startup. Minimum value is - 1. + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. format: int32 type: integer tcpSocket: @@ -2741,42 +2989,72 @@ spec: anyOf: - type: integer - type: string - description: Number or name of the port to access on the - container. Number must be in the range 1 to 65535. Name - must be an IANA_SVC_NAME. + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true required: - port type: object terminationGracePeriodSeconds: - description: Optional duration in seconds the pod needs to - terminate gracefully upon probe failure. The grace period - is the duration in seconds after the processes running in - the pod are sent a termination signal and the time when - the processes are forcibly halted with a kill signal. Set - this value longer than the expected cleanup time for your - process. If this value is nil, the pod's terminationGracePeriodSeconds - will be used. Otherwise, this value overrides the value - provided by the pod spec. Value must be non-negative integer. - The value zero indicates stop immediately via the kill signal - (no opportunity to shut down). This is a beta field and - requires enabling ProbeTerminationGracePeriod feature gate. - Minimum value is 1. spec.terminationGracePeriodSeconds is - used if unset. + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. format: int64 type: integer timeoutSeconds: - description: 'Number of seconds after which the probe times - out. Defaults to 1 second. Minimum value is 1. More info: - https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes format: int32 type: integer type: object resources: - description: Resources container resource request and limits, - https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + description: |- + Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ if not specified - default setting will be used properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map limits: additionalProperties: anyOf: @@ -2784,8 +3062,9 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute - resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object requests: additionalProperties: @@ -2794,15 +3073,17 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, otherwise - to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object type: object runtimeClassName: - description: RuntimeClassName - defines runtime class for kubernetes - pod. https://kubernetes.io/docs/concepts/containers/runtime-class/ + description: |- + RuntimeClassName - defines runtime class for kubernetes pod. + https://kubernetes.io/docs/concepts/containers/runtime-class/ type: string schedulerName: description: SchedulerName - defines kubernetes scheduler name @@ -2810,41 +3091,39 @@ spec: tolerations: description: Tolerations If specified, the pod's tolerations. items: - description: The pod this Toleration is attached to tolerates - any taint that matches the triple using - the matching operator . + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . properties: effect: - description: Effect indicates the taint effect to match. - Empty means match all taint effects. When specified, allowed - values are NoSchedule, PreferNoSchedule and NoExecute. + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. type: string key: - description: Key is the taint key that the toleration applies - to. Empty means match all taint keys. If the key is empty, - operator must be Exists; this combination means to match - all values and all keys. + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. type: string operator: - description: Operator represents a key's relationship to - the value. Valid operators are Exists and Equal. Defaults - to Equal. Exists is equivalent to wildcard for value, - so that a pod can tolerate all taints of a particular - category. + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. type: string tolerationSeconds: - description: TolerationSeconds represents the period of - time the toleration (which must be of effect NoExecute, - otherwise this field is ignored) tolerates the taint. - By default, it is not set, which means tolerate the taint - forever (do not evict). Zero and negative values will - be treated as 0 (evict immediately) by the system. + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. format: int64 type: integer value: - description: Value is the taint value the toleration matches - to. If the operator is Exists, the value should be empty, - otherwise just a regular string. + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. type: string type: object type: array @@ -2855,33 +3134,57 @@ spec: a container. properties: mountPath: - description: Path within the container at which the volume - should be mounted. Must not contain ':'. + description: |- + Path within the container at which the volume should be mounted. Must + not contain ':'. type: string mountPropagation: - description: mountPropagation determines how mounts are - propagated from the host to container and the other way - around. When not set, MountPropagationNone is used. This - field is beta in 1.10. + description: |- + mountPropagation determines how mounts are propagated from the host + to container and the other way around. + When not set, MountPropagationNone is used. + This field is beta in 1.10. + When RecursiveReadOnly is set to IfPossible or to Enabled, MountPropagation must be None or unspecified + (which defaults to None). type: string name: description: This must match the Name of a Volume. type: string readOnly: - description: Mounted read-only if true, read-write otherwise - (false or unspecified). Defaults to false. + description: |- + Mounted read-only if true, read-write otherwise (false or unspecified). + Defaults to false. type: boolean + recursiveReadOnly: + description: |- + RecursiveReadOnly specifies whether read-only mounts should be handled + recursively. + + If ReadOnly is false, this field has no meaning and must be unspecified. + + If ReadOnly is true, and this field is set to Disabled, the mount is not made + recursively read-only. If this field is set to IfPossible, the mount is made + recursively read-only, if it is supported by the container runtime. If this + field is set to Enabled, the mount is made recursively read-only if it is + supported by the container runtime, otherwise the pod will not be started and + an error will be generated to indicate the reason. + + If this field is set to IfPossible or Enabled, MountPropagation must be set to + None (or be unspecified, which defaults to None). + + If this field is not specified, it is treated as an equivalent of Disabled. + type: string subPath: - description: Path within the volume from which the container's - volume should be mounted. Defaults to "" (volume's root). + description: |- + Path within the volume from which the container's volume should be mounted. + Defaults to "" (volume's root). type: string subPathExpr: - description: Expanded path within the volume from which - the container's volume should be mounted. Behaves similarly - to SubPath but environment variable references $(VAR_NAME) - are expanded using the container's environment. Defaults - to "" (volume's root). SubPathExpr and SubPath are mutually - exclusive. + description: |- + Expanded path within the volume from which the container's volume should be mounted. + Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. + Defaults to "" (volume's root). + SubPathExpr and SubPath are mutually exclusive. type: string required: - mountPath @@ -2896,36 +3199,35 @@ spec: may be accessed by any container in the pod. properties: awsElasticBlockStore: - description: 'awsElasticBlockStore represents an AWS Disk - resource that is attached to a kubelet''s host machine - and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + description: |- + awsElasticBlockStore represents an AWS Disk resource that is attached to a + kubelet's host machine and then exposed to the pod. + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore properties: fsType: - description: 'fsType is the filesystem type of the volume - that you want to mount. Tip: Ensure that the filesystem - type is supported by the host operating system. Examples: - "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" - if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore - TODO: how do we prevent errors in the filesystem from - compromising the machine' + description: |- + fsType is the filesystem type of the volume that you want to mount. + Tip: Ensure that the filesystem type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore type: string partition: - description: 'partition is the partition in the volume - that you want to mount. If omitted, the default is - to mount by volume name. Examples: For volume /dev/sda1, - you specify the partition as "1". Similarly, the volume - partition for /dev/sda is "0" (or you can leave the - property empty).' + description: |- + partition is the partition in the volume that you want to mount. + If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition as "1". + Similarly, the volume partition for /dev/sda is "0" (or you can leave the property empty). format: int32 type: integer readOnly: - description: 'readOnly value true will force the readOnly - setting in VolumeMounts. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + description: |- + readOnly value true will force the readOnly setting in VolumeMounts. + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore type: boolean volumeID: - description: 'volumeID is unique ID of the persistent - disk resource in AWS (Amazon EBS volume). More info: - https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + description: |- + volumeID is unique ID of the persistent disk resource in AWS (Amazon EBS volume). + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore type: string required: - volumeID @@ -2947,10 +3249,11 @@ spec: blob storage type: string fsType: - description: fsType is Filesystem type to mount. Must - be a filesystem type supported by the host operating - system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred - to be "ext4" if unspecified. + default: ext4 + description: |- + fsType is Filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. type: string kind: description: 'kind expected values are Shared: multiple @@ -2960,8 +3263,10 @@ spec: to shared' type: string readOnly: - description: readOnly Defaults to false (read/write). - ReadOnly here will force the ReadOnly setting in VolumeMounts. + default: false + description: |- + readOnly Defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. type: boolean required: - diskName @@ -2972,8 +3277,9 @@ spec: mount on the host and bind mount to the pod. properties: readOnly: - description: readOnly defaults to false (read/write). - ReadOnly here will force the ReadOnly setting in VolumeMounts. + description: |- + readOnly defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. type: boolean secretName: description: secretName is the name of secret that @@ -2991,73 +3297,90 @@ spec: that shares a pod's lifetime properties: monitors: - description: 'monitors is Required: Monitors is a collection - of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + description: |- + monitors is Required: Monitors is a collection of Ceph monitors + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it items: type: string type: array + x-kubernetes-list-type: atomic path: description: 'path is Optional: Used as the mounted root, rather than the full Ceph tree, default is /' type: string readOnly: - description: 'readOnly is Optional: Defaults to false - (read/write). ReadOnly here will force the ReadOnly - setting in VolumeMounts. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + description: |- + readOnly is Optional: Defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it type: boolean secretFile: - description: 'secretFile is Optional: SecretFile is - the path to key ring for User, default is /etc/ceph/user.secret - More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + description: |- + secretFile is Optional: SecretFile is the path to key ring for User, default is /etc/ceph/user.secret + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it type: string secretRef: - description: 'secretRef is Optional: SecretRef is reference - to the authentication secret for User, default is - empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + description: |- + secretRef is Optional: SecretRef is reference to the authentication secret for User, default is empty. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it properties: name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string type: object x-kubernetes-map-type: atomic user: - description: 'user is optional: User is the rados user - name, default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + description: |- + user is optional: User is the rados user name, default is admin + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it type: string required: - monitors type: object cinder: - description: 'cinder represents a cinder volume attached - and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + description: |- + cinder represents a cinder volume attached and mounted on kubelets host machine. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md properties: fsType: - description: 'fsType is the filesystem type to mount. - Must be a filesystem type supported by the host operating - system. Examples: "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md type: string readOnly: - description: 'readOnly defaults to false (read/write). - ReadOnly here will force the ReadOnly setting in VolumeMounts. - More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + description: |- + readOnly defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md type: boolean secretRef: - description: 'secretRef is optional: points to a secret - object containing parameters used to connect to OpenStack.' + description: |- + secretRef is optional: points to a secret object containing parameters used to connect + to OpenStack. properties: name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string type: object x-kubernetes-map-type: atomic volumeID: - description: 'volumeID used to identify the volume in - cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + description: |- + volumeID used to identify the volume in cinder. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md type: string required: - volumeID @@ -3067,29 +3390,25 @@ spec: populate this volume properties: defaultMode: - description: 'defaultMode is optional: mode bits used - to set permissions on created files by default. Must - be an octal value between 0000 and 0777 or a decimal - value between 0 and 511. YAML accepts both octal and - decimal values, JSON requires decimal values for mode - bits. Defaults to 0644. Directories within the path - are not affected by this setting. This might be in - conflict with other options that affect the file mode, - like fsGroup, and the result can be other mode bits - set.' + description: |- + defaultMode is optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. format: int32 type: integer items: - description: items if unspecified, each key-value pair - in the Data field of the referenced ConfigMap will - be projected into the volume as a file whose name - is the key and content is the value. If specified, - the listed keys will be projected into the specified - paths, and unlisted keys will not be present. If a - key is specified which is not present in the ConfigMap, - the volume setup will error unless it is marked optional. - Paths must be relative and may not contain the '..' - path or start with '..'. + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + ConfigMap will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. items: description: Maps a string key to a path within a volume. @@ -3098,22 +3417,20 @@ spec: description: key is the key to project. type: string mode: - description: 'mode is Optional: mode bits used - to set permissions on this file. Must be an - octal value between 0000 and 0777 or a decimal - value between 0 and 511. YAML accepts both octal - and decimal values, JSON requires decimal values - for mode bits. If not specified, the volume - defaultMode will be used. This might be in conflict - with other options that affect the file mode, - like fsGroup, and the result can be other mode - bits set.' + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. format: int32 type: integer path: - description: path is the relative path of the - file to map the key to. May not be an absolute - path. May not contain the path element '..'. + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. May not start with the string '..'. type: string required: @@ -3121,9 +3438,15 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string optional: description: optional specify whether the ConfigMap @@ -3137,42 +3460,46 @@ spec: CSI drivers (Beta feature). properties: driver: - description: driver is the name of the CSI driver that - handles this volume. Consult with your admin for the - correct name as registered in the cluster. + description: |- + driver is the name of the CSI driver that handles this volume. + Consult with your admin for the correct name as registered in the cluster. type: string fsType: - description: fsType to mount. Ex. "ext4", "xfs", "ntfs". - If not provided, the empty value is passed to the - associated CSI driver which will determine the default - filesystem to apply. + description: |- + fsType to mount. Ex. "ext4", "xfs", "ntfs". + If not provided, the empty value is passed to the associated CSI driver + which will determine the default filesystem to apply. type: string nodePublishSecretRef: - description: nodePublishSecretRef is a reference to - the secret object containing sensitive information - to pass to the CSI driver to complete the CSI NodePublishVolume - and NodeUnpublishVolume calls. This field is optional, - and may be empty if no secret is required. If the - secret object contains more than one secret, all secret - references are passed. + description: |- + nodePublishSecretRef is a reference to the secret object containing + sensitive information to pass to the CSI driver to complete the CSI + NodePublishVolume and NodeUnpublishVolume calls. + This field is optional, and may be empty if no secret is required. If the + secret object contains more than one secret, all secret references are passed. properties: name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string type: object x-kubernetes-map-type: atomic readOnly: - description: readOnly specifies a read-only configuration - for the volume. Defaults to false (read/write). + description: |- + readOnly specifies a read-only configuration for the volume. + Defaults to false (read/write). type: boolean volumeAttributes: additionalProperties: type: string - description: volumeAttributes stores driver-specific - properties that are passed to the CSI driver. Consult - your driver's documentation for supported values. + description: |- + volumeAttributes stores driver-specific properties that are passed to the CSI + driver. Consult your driver's documentation for supported values. type: object required: - driver @@ -3182,17 +3509,15 @@ spec: pod that should populate this volume properties: defaultMode: - description: 'Optional: mode bits to use on created - files by default. Must be a Optional: mode bits used - to set permissions on created files by default. Must - be an octal value between 0000 and 0777 or a decimal - value between 0 and 511. YAML accepts both octal and - decimal values, JSON requires decimal values for mode - bits. Defaults to 0644. Directories within the path - are not affected by this setting. This might be in - conflict with other options that affect the file mode, - like fsGroup, and the result can be other mode bits - set.' + description: |- + Optional: mode bits to use on created files by default. Must be a + Optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. format: int32 type: integer items: @@ -3204,8 +3529,8 @@ spec: properties: fieldRef: description: 'Required: Selects a field of the - pod: only annotations, labels, name and namespace - are supported.' + pod: only annotations, labels, name, namespace + and uid are supported.' properties: apiVersion: description: Version of the schema the FieldPath @@ -3220,16 +3545,13 @@ spec: type: object x-kubernetes-map-type: atomic mode: - description: 'Optional: mode bits used to set - permissions on this file, must be an octal value - between 0000 and 0777 or a decimal value between - 0 and 511. YAML accepts both octal and decimal - values, JSON requires decimal values for mode - bits. If not specified, the volume defaultMode - will be used. This might be in conflict with - other options that affect the file mode, like - fsGroup, and the result can be other mode bits - set.' + description: |- + Optional: mode bits used to set permissions on this file, must be an octal value + between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. format: int32 type: integer path: @@ -3240,10 +3562,9 @@ spec: path must not start with ''..''' type: string resourceFieldRef: - description: 'Selects a resource of the container: - only resources limits and requests (limits.cpu, - limits.memory, requests.cpu and requests.memory) - are currently supported.' + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. properties: containerName: description: 'Container name: required for @@ -3268,115 +3589,122 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic type: object emptyDir: - description: 'emptyDir represents a temporary directory - that shares a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + description: |- + emptyDir represents a temporary directory that shares a pod's lifetime. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir properties: medium: - description: 'medium represents what type of storage - medium should back this directory. The default is - "" which means to use the node''s default medium. - Must be an empty string (default) or Memory. More - info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + description: |- + medium represents what type of storage medium should back this directory. + The default is "" which means to use the node's default medium. + Must be an empty string (default) or Memory. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir type: string sizeLimit: anyOf: - type: integer - type: string - description: 'sizeLimit is the total amount of local - storage required for this EmptyDir volume. The size - limit is also applicable for memory medium. The maximum - usage on memory medium EmptyDir would be the minimum - value between the SizeLimit specified here and the - sum of memory limits of all containers in a pod. The - default is nil which means that the limit is undefined. - More info: http://kubernetes.io/docs/user-guide/volumes#emptydir' + description: |- + sizeLimit is the total amount of local storage required for this EmptyDir volume. + The size limit is also applicable for memory medium. + The maximum usage on memory medium EmptyDir would be the minimum value between + the SizeLimit specified here and the sum of memory limits of all containers in a pod. + The default is nil which means that the limit is undefined. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true type: object ephemeral: - description: "ephemeral represents a volume that is handled - by a cluster storage driver. The volume's lifecycle is - tied to the pod that defines it - it will be created before - the pod starts, and deleted when the pod is removed. \n - Use this if: a) the volume is only needed while the pod - runs, b) features of normal volumes like restoring from - snapshot or capacity tracking are needed, c) the storage - driver is specified through a storage class, and d) the - storage driver supports dynamic volume provisioning through - a PersistentVolumeClaim (see EphemeralVolumeSource for - more information on the connection between this volume - type and PersistentVolumeClaim). \n Use PersistentVolumeClaim - or one of the vendor-specific APIs for volumes that persist - for longer than the lifecycle of an individual pod. \n - Use CSI for light-weight local ephemeral volumes if the - CSI driver is meant to be used that way - see the documentation - of the driver for more information. \n A pod can use both - types of ephemeral volumes and persistent volumes at the - same time." + description: |- + ephemeral represents a volume that is handled by a cluster storage driver. + The volume's lifecycle is tied to the pod that defines it - it will be created before the pod starts, + and deleted when the pod is removed. + + Use this if: + a) the volume is only needed while the pod runs, + b) features of normal volumes like restoring from snapshot or capacity + tracking are needed, + c) the storage driver is specified through a storage class, and + d) the storage driver supports dynamic volume provisioning through + a PersistentVolumeClaim (see EphemeralVolumeSource for more + information on the connection between this volume type + and PersistentVolumeClaim). + + Use PersistentVolumeClaim or one of the vendor-specific + APIs for volumes that persist for longer than the lifecycle + of an individual pod. + + Use CSI for light-weight local ephemeral volumes if the CSI driver is meant to + be used that way - see the documentation of the driver for + more information. + + A pod can use both types of ephemeral volumes and + persistent volumes at the same time. properties: volumeClaimTemplate: - description: "Will be used to create a stand-alone PVC - to provision the volume. The pod in which this EphemeralVolumeSource - is embedded will be the owner of the PVC, i.e. the - PVC will be deleted together with the pod. The name - of the PVC will be `-` where - `` is the name from the `PodSpec.Volumes` - array entry. Pod validation will reject the pod if - the concatenated name is not valid for a PVC (for - example, too long). \n An existing PVC with that name - that is not owned by the pod will *not* be used for - the pod to avoid using an unrelated volume by mistake. - Starting the pod is then blocked until the unrelated - PVC is removed. If such a pre-created PVC is meant - to be used by the pod, the PVC has to updated with - an owner reference to the pod once the pod exists. - Normally this should not be necessary, but it may - be useful when manually reconstructing a broken cluster. - \n This field is read-only and no changes will be - made by Kubernetes to the PVC after it has been created. - \n Required, must not be nil." + description: |- + Will be used to create a stand-alone PVC to provision the volume. + The pod in which this EphemeralVolumeSource is embedded will be the + owner of the PVC, i.e. the PVC will be deleted together with the + pod. The name of the PVC will be `-` where + `` is the name from the `PodSpec.Volumes` array + entry. Pod validation will reject the pod if the concatenated name + is not valid for a PVC (for example, too long). + + An existing PVC with that name that is not owned by the pod + will *not* be used for the pod to avoid using an unrelated + volume by mistake. Starting the pod is then blocked until + the unrelated PVC is removed. If such a pre-created PVC is + meant to be used by the pod, the PVC has to updated with an + owner reference to the pod once the pod exists. Normally + this should not be necessary, but it may be useful when + manually reconstructing a broken cluster. + + This field is read-only and no changes will be made by Kubernetes + to the PVC after it has been created. + + Required, must not be nil. properties: metadata: - description: May contain labels and annotations - that will be copied into the PVC when creating - it. No other fields are allowed and will be rejected - during validation. + description: |- + May contain labels and annotations that will be copied into the PVC + when creating it. No other fields are allowed and will be rejected during + validation. type: object spec: - description: The specification for the PersistentVolumeClaim. - The entire content is copied unchanged into the - PVC that gets created from this template. The - same fields as in a PersistentVolumeClaim are - also valid here. + description: |- + The specification for the PersistentVolumeClaim. The entire content is + copied unchanged into the PVC that gets created from this + template. The same fields as in a PersistentVolumeClaim + are also valid here. properties: accessModes: - description: 'accessModes contains the desired - access modes the volume should have. More - info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' + description: |- + accessModes contains the desired access modes the volume should have. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1 items: type: string type: array + x-kubernetes-list-type: atomic dataSource: - description: 'dataSource field can be used to - specify either: * An existing VolumeSnapshot - object (snapshot.storage.k8s.io/VolumeSnapshot) + description: |- + dataSource field can be used to specify either: + * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) * An existing PVC (PersistentVolumeClaim) - If the provisioner or an external controller - can support the specified data source, it - will create a new volume based on the contents - of the specified data source. If the AnyVolumeDataSource - feature gate is enabled, this field will always - have the same contents as the DataSourceRef - field.' + If the provisioner or an external controller can support the specified data source, + it will create a new volume based on the contents of the specified data source. + When the AnyVolumeDataSource feature gate is enabled, dataSource contents will be copied to dataSourceRef, + and dataSourceRef contents will be copied to dataSource when dataSourceRef.namespace is not specified. + If the namespace is specified, then dataSourceRef will not be copied to dataSource. properties: apiGroup: - description: APIGroup is the group for the - resource being referenced. If APIGroup - is not specified, the specified Kind must - be in the core API group. For any other - third-party types, APIGroup is required. + description: |- + APIGroup is the group for the resource being referenced. + If APIGroup is not specified, the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. type: string kind: description: Kind is the type of resource @@ -3392,39 +3720,36 @@ spec: type: object x-kubernetes-map-type: atomic dataSourceRef: - description: 'dataSourceRef specifies the object - from which to populate the volume with data, - if a non-empty volume is desired. This may - be any local object from a non-empty API group - (non core object) or a PersistentVolumeClaim - object. When this field is specified, volume - binding will only succeed if the type of the - specified object matches some installed volume - populator or dynamic provisioner. This field - will replace the functionality of the DataSource - field and as such if both fields are non-empty, - they must have the same value. For backwards - compatibility, both fields (DataSource and - DataSourceRef) will be set to the same value - automatically if one of them is empty and - the other is non-empty. There are two important - differences between DataSource and DataSourceRef: - * While DataSource only allows two specific - types of objects, DataSourceRef allows any - non-core object, as well as PersistentVolumeClaim - objects. * While DataSource ignores disallowed - values (dropping them), DataSourceRef preserves - all values, and generates an error if a disallowed - value is specified. (Beta) Using this field - requires the AnyVolumeDataSource feature gate - to be enabled.' + description: |- + dataSourceRef specifies the object from which to populate the volume with data, if a non-empty + volume is desired. This may be any object from a non-empty API group (non + core object) or a PersistentVolumeClaim object. + When this field is specified, volume binding will only succeed if the type of + the specified object matches some installed volume populator or dynamic + provisioner. + This field will replace the functionality of the dataSource field and as such + if both fields are non-empty, they must have the same value. For backwards + compatibility, when namespace isn't specified in dataSourceRef, + both fields (dataSource and dataSourceRef) will be set to the same + value automatically if one of them is empty and the other is non-empty. + When namespace is specified in dataSourceRef, + dataSource isn't set to the same value and must be empty. + There are three important differences between dataSource and dataSourceRef: + * While dataSource only allows two specific types of objects, dataSourceRef + allows any non-core object, as well as PersistentVolumeClaim objects. + * While dataSource ignores disallowed values (dropping them), dataSourceRef + preserves all values, and generates an error if a disallowed value is + specified. + * While dataSource only allows local objects, dataSourceRef allows objects + in any namespaces. + (Beta) Using this field requires the AnyVolumeDataSource feature gate to be enabled. + (Alpha) Using the namespace field of dataSourceRef requires the CrossNamespaceVolumeDataSource feature gate to be enabled. properties: apiGroup: - description: APIGroup is the group for the - resource being referenced. If APIGroup - is not specified, the specified Kind must - be in the core API group. For any other - third-party types, APIGroup is required. + description: |- + APIGroup is the group for the resource being referenced. + If APIGroup is not specified, the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. type: string kind: description: Kind is the type of resource @@ -3434,19 +3759,23 @@ spec: description: Name is the name of resource being referenced type: string + namespace: + description: |- + Namespace is the namespace of resource being referenced + Note that when a namespace is specified, a gateway.networking.k8s.io/ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. + (Alpha) This field requires the CrossNamespaceVolumeDataSource feature gate to be enabled. + type: string required: - kind - name type: object - x-kubernetes-map-type: atomic resources: - description: 'resources represents the minimum - resources the volume should have. If RecoverVolumeExpansionFailure - feature is enabled users are allowed to specify - resource requirements that are lower than - previous value but must still be higher than - capacity recorded in the status field of the - claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' + description: |- + resources represents the minimum resources the volume should have. + If RecoverVolumeExpansionFailure feature is enabled users are allowed to specify resource requirements + that are lower than previous value but must still be higher than capacity recorded in the + status field of the claim. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources properties: limits: additionalProperties: @@ -3455,9 +3784,9 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Limits describes the maximum - amount of compute resources allowed. More - info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object requests: additionalProperties: @@ -3466,12 +3795,11 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Requests describes the minimum - amount of compute resources required. - If Requests is omitted for a container, - it defaults to Limits if that is explicitly - specified, otherwise to an implementation-defined - value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object type: object selector: @@ -3483,60 +3811,69 @@ spec: of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic storageClassName: - description: 'storageClassName is the name of - the StorageClass required by the claim. More - info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' + description: |- + storageClassName is the name of the StorageClass required by the claim. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1 + type: string + volumeAttributesClassName: + description: |- + volumeAttributesClassName may be used to set the VolumeAttributesClass used by this claim. + If specified, the CSI driver will create or update the volume with the attributes defined + in the corresponding VolumeAttributesClass. This has a different purpose than storageClassName, + it can be changed after the claim is created. An empty string value means that no VolumeAttributesClass + will be applied to the claim but it's not allowed to reset this field to empty string once it is set. + If unspecified and the PersistentVolumeClaim is unbound, the default VolumeAttributesClass + will be set by the persistentvolume controller if it exists. + If the resource referred to by volumeAttributesClass does not exist, this PersistentVolumeClaim will be + set to a Pending state, as reflected by the modifyVolumeStatus field, until such as a resource + exists. + More info: https://kubernetes.io/docs/concepts/storage/volume-attributes-classes/ + (Beta) Using this field requires the VolumeAttributesClass feature gate to be enabled (off by default). type: string volumeMode: - description: volumeMode defines what type of - volume is required by the claim. Value of - Filesystem is implied when not included in - claim spec. + description: |- + volumeMode defines what type of volume is required by the claim. + Value of Filesystem is implied when not included in claim spec. type: string volumeName: description: volumeName is the binding reference @@ -3553,20 +3890,19 @@ spec: to the pod. properties: fsType: - description: 'fsType is the filesystem type to mount. - Must be a filesystem type supported by the host operating - system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred - to be "ext4" if unspecified. TODO: how do we prevent - errors in the filesystem from compromising the machine' + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. type: string lun: description: 'lun is Optional: FC target lun number' format: int32 type: integer readOnly: - description: 'readOnly is Optional: Defaults to false - (read/write). ReadOnly here will force the ReadOnly - setting in VolumeMounts.' + description: |- + readOnly is Optional: Defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. type: boolean targetWWNs: description: 'targetWWNs is Optional: FC target worldwide @@ -3574,27 +3910,30 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic wwids: - description: 'wwids Optional: FC volume world wide identifiers - (wwids) Either wwids or combination of targetWWNs - and lun must be set, but not both simultaneously.' + description: |- + wwids Optional: FC volume world wide identifiers (wwids) + Either wwids or combination of targetWWNs and lun must be set, but not both simultaneously. items: type: string type: array + x-kubernetes-list-type: atomic type: object flexVolume: - description: flexVolume represents a generic volume resource - that is provisioned/attached using an exec based plugin. + description: |- + flexVolume represents a generic volume resource that is + provisioned/attached using an exec based plugin. properties: driver: description: driver is the name of the driver to use for this volume. type: string fsType: - description: fsType is the filesystem type to mount. - Must be a filesystem type supported by the host operating - system. Ex. "ext4", "xfs", "ntfs". The default filesystem - depends on FlexVolume script. + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". The default filesystem depends on FlexVolume script. type: string options: additionalProperties: @@ -3603,22 +3942,26 @@ spec: extra command options if any.' type: object readOnly: - description: 'readOnly is Optional: defaults to false - (read/write). ReadOnly here will force the ReadOnly - setting in VolumeMounts.' + description: |- + readOnly is Optional: defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. type: boolean secretRef: - description: 'secretRef is Optional: secretRef is reference - to the secret object containing sensitive information - to pass to the plugin scripts. This may be empty if - no secret object is specified. If the secret object - contains more than one secret, all secrets are passed - to the plugin scripts.' + description: |- + secretRef is Optional: secretRef is reference to the secret object containing + sensitive information to pass to the plugin scripts. This may be + empty if no secret object is specified. If the secret object + contains more than one secret, all secrets are passed to the plugin + scripts. properties: name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string type: object x-kubernetes-map-type: atomic @@ -3631,9 +3974,9 @@ spec: control service being running properties: datasetName: - description: datasetName is Name of the dataset stored - as metadata -> name on the dataset for Flocker should - be considered as deprecated + description: |- + datasetName is Name of the dataset stored as metadata -> name on the dataset for Flocker + should be considered as deprecated type: string datasetUUID: description: datasetUUID is the UUID of the dataset. @@ -3641,54 +3984,54 @@ spec: type: string type: object gcePersistentDisk: - description: 'gcePersistentDisk represents a GCE Disk resource - that is attached to a kubelet''s host machine and then - exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + description: |- + gcePersistentDisk represents a GCE Disk resource that is attached to a + kubelet's host machine and then exposed to the pod. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk properties: fsType: - description: 'fsType is filesystem type of the volume - that you want to mount. Tip: Ensure that the filesystem - type is supported by the host operating system. Examples: - "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" - if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk - TODO: how do we prevent errors in the filesystem from - compromising the machine' + description: |- + fsType is filesystem type of the volume that you want to mount. + Tip: Ensure that the filesystem type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk type: string partition: - description: 'partition is the partition in the volume - that you want to mount. If omitted, the default is - to mount by volume name. Examples: For volume /dev/sda1, - you specify the partition as "1". Similarly, the volume - partition for /dev/sda is "0" (or you can leave the - property empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + description: |- + partition is the partition in the volume that you want to mount. + If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition as "1". + Similarly, the volume partition for /dev/sda is "0" (or you can leave the property empty). + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk format: int32 type: integer pdName: - description: 'pdName is unique name of the PD resource - in GCE. Used to identify the disk in GCE. More info: - https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + description: |- + pdName is unique name of the PD resource in GCE. Used to identify the disk in GCE. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk type: string readOnly: - description: 'readOnly here will force the ReadOnly - setting in VolumeMounts. Defaults to false. More info: - https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + description: |- + readOnly here will force the ReadOnly setting in VolumeMounts. + Defaults to false. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk type: boolean required: - pdName type: object gitRepo: - description: 'gitRepo represents a git repository at a particular - revision. DEPRECATED: GitRepo is deprecated. To provision - a container with a git repo, mount an EmptyDir into an - InitContainer that clones the repo using git, then mount - the EmptyDir into the Pod''s container.' + description: |- + gitRepo represents a git repository at a particular revision. + DEPRECATED: GitRepo is deprecated. To provision a container with a git repo, mount an + EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir + into the Pod's container. properties: directory: - description: directory is the target directory name. - Must not contain or start with '..'. If '.' is supplied, - the volume directory will be the git repository. Otherwise, - if specified, the volume will contain the git repository - in the subdirectory with the given name. + description: |- + directory is the target directory name. + Must not contain or start with '..'. If '.' is supplied, the volume directory will be the + git repository. Otherwise, if specified, the volume will contain the git repository in + the subdirectory with the given name. type: string repository: description: repository is the URL @@ -3701,53 +4044,93 @@ spec: - repository type: object glusterfs: - description: 'glusterfs represents a Glusterfs mount on - the host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md' + description: |- + glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. + More info: https://examples.k8s.io/volumes/glusterfs/README.md properties: endpoints: - description: 'endpoints is the endpoint name that details - Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + description: |- + endpoints is the endpoint name that details Glusterfs topology. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod type: string path: - description: 'path is the Glusterfs volume path. More - info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + description: |- + path is the Glusterfs volume path. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod type: string readOnly: - description: 'readOnly here will force the Glusterfs - volume to be mounted with read-only permissions. Defaults - to false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + description: |- + readOnly here will force the Glusterfs volume to be mounted with read-only permissions. + Defaults to false. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod type: boolean required: - endpoints - path type: object hostPath: - description: 'hostPath represents a pre-existing file or - directory on the host machine that is directly exposed - to the container. This is generally used for system agents - or other privileged things that are allowed to see the - host machine. Most containers will NOT need this. More - info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath - --- TODO(jonesdl) We need to restrict who can use host - directory mounts and who can/can not mount host directories - as read/write.' + description: |- + hostPath represents a pre-existing file or directory on the host + machine that is directly exposed to the container. This is generally + used for system agents or other privileged things that are allowed + to see the host machine. Most containers will NOT need this. + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath properties: path: - description: 'path of the directory on the host. If - the path is a symlink, it will follow the link to - the real path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + description: |- + path of the directory on the host. + If the path is a symlink, it will follow the link to the real path. + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath type: string type: - description: 'type for HostPath Volume Defaults to "" - More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + description: |- + type for HostPath Volume + Defaults to "" + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath type: string required: - path type: object + image: + description: |- + image represents an OCI object (a container image or artifact) pulled and mounted on the kubelet's host machine. + The volume is resolved at pod startup depending on which PullPolicy value is provided: + + - Always: the kubelet always attempts to pull the reference. Container creation will fail If the pull fails. + - Never: the kubelet never pulls the reference and only uses a local image or artifact. Container creation will fail if the reference isn't present. + - IfNotPresent: the kubelet pulls if the reference isn't already present on disk. Container creation will fail if the reference isn't present and the pull fails. + + The volume gets re-resolved if the pod gets deleted and recreated, which means that new remote content will become available on pod recreation. + A failure to resolve or pull the image during pod startup will block containers from starting and may add significant latency. Failures will be retried using normal volume backoff and will be reported on the pod reason and message. + The types of objects that may be mounted by this volume are defined by the container runtime implementation on a host machine and at minimum must include all valid types supported by the container image field. + The OCI object gets mounted in a single directory (spec.containers[*].volumeMounts.mountPath) by merging the manifest layers in the same way as for container images. + The volume will be mounted read-only (ro) and non-executable files (noexec). + Sub path mounts for containers are not supported (spec.containers[*].volumeMounts.subpath). + The field spec.securityContext.fsGroupChangePolicy has no effect on this volume type. + properties: + pullPolicy: + description: |- + Policy for pulling OCI objects. Possible values are: + Always: the kubelet always attempts to pull the reference. Container creation will fail If the pull fails. + Never: the kubelet never pulls the reference and only uses a local image or artifact. Container creation will fail if the reference isn't present. + IfNotPresent: the kubelet pulls if the reference isn't already present on disk. Container creation will fail if the reference isn't present and the pull fails. + Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. + type: string + reference: + description: |- + Required: Image or artifact reference to be used. + Behaves in the same way as pod.spec.containers[*].image. + Pull secrets will be assembled in the same way as for the container image by looking up node credentials, SA image pull secrets, and pod spec image pull secrets. + More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config management to default or override + container images in workload controllers like Deployments and StatefulSets. + type: string + type: object iscsi: - description: 'iscsi represents an ISCSI Disk resource that - is attached to a kubelet''s host machine and then exposed - to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + description: |- + iscsi represents an ISCSI Disk resource that is attached to a + kubelet's host machine and then exposed to the pod. + More info: https://examples.k8s.io/volumes/iscsi/README.md properties: chapAuthDiscovery: description: chapAuthDiscovery defines whether support @@ -3758,59 +4141,63 @@ spec: iSCSI Session CHAP authentication type: boolean fsType: - description: 'fsType is the filesystem type of the volume - that you want to mount. Tip: Ensure that the filesystem - type is supported by the host operating system. Examples: - "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" - if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi - TODO: how do we prevent errors in the filesystem from - compromising the machine' + description: |- + fsType is the filesystem type of the volume that you want to mount. + Tip: Ensure that the filesystem type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi type: string initiatorName: - description: initiatorName is the custom iSCSI Initiator - Name. If initiatorName is specified with iscsiInterface - simultaneously, new iSCSI interface : will be created for the connection. + description: |- + initiatorName is the custom iSCSI Initiator Name. + If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface + : will be created for the connection. type: string iqn: description: iqn is the target iSCSI Qualified Name. type: string iscsiInterface: - description: iscsiInterface is the interface Name that - uses an iSCSI transport. Defaults to 'default' (tcp). + default: default + description: |- + iscsiInterface is the interface Name that uses an iSCSI transport. + Defaults to 'default' (tcp). type: string lun: description: lun represents iSCSI Target Lun number. format: int32 type: integer portals: - description: portals is the iSCSI Target Portal List. - The portal is either an IP or ip_addr:port if the - port is other than default (typically TCP ports 860 - and 3260). + description: |- + portals is the iSCSI Target Portal List. The portal is either an IP or ip_addr:port if the port + is other than default (typically TCP ports 860 and 3260). items: type: string type: array + x-kubernetes-list-type: atomic readOnly: - description: readOnly here will force the ReadOnly setting - in VolumeMounts. Defaults to false. + description: |- + readOnly here will force the ReadOnly setting in VolumeMounts. + Defaults to false. type: boolean secretRef: description: secretRef is the CHAP Secret for iSCSI target and initiator authentication properties: name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string type: object x-kubernetes-map-type: atomic targetPortal: - description: targetPortal is iSCSI Target Portal. The - Portal is either an IP or ip_addr:port if the port - is other than default (typically TCP ports 860 and - 3260). + description: |- + targetPortal is iSCSI Target Portal. The Portal is either an IP or ip_addr:port if the port + is other than default (typically TCP ports 860 and 3260). type: string required: - iqn @@ -3818,43 +4205,51 @@ spec: - targetPortal type: object name: - description: 'name of the volume. Must be a DNS_LABEL and - unique within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + description: |- + name of the volume. + Must be a DNS_LABEL and unique within the pod. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string nfs: - description: 'nfs represents an NFS mount on the host that - shares a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + description: |- + nfs represents an NFS mount on the host that shares a pod's lifetime + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs properties: path: - description: 'path that is exported by the NFS server. - More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + description: |- + path that is exported by the NFS server. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs type: string readOnly: - description: 'readOnly here will force the NFS export - to be mounted with read-only permissions. Defaults - to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + description: |- + readOnly here will force the NFS export to be mounted with read-only permissions. + Defaults to false. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs type: boolean server: - description: 'server is the hostname or IP address of - the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + description: |- + server is the hostname or IP address of the NFS server. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs type: string required: - path - server type: object persistentVolumeClaim: - description: 'persistentVolumeClaimVolumeSource represents - a reference to a PersistentVolumeClaim in the same namespace. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + description: |- + persistentVolumeClaimVolumeSource represents a reference to a + PersistentVolumeClaim in the same namespace. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims properties: claimName: - description: 'claimName is the name of a PersistentVolumeClaim - in the same namespace as the pod using this volume. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + description: |- + claimName is the name of a PersistentVolumeClaim in the same namespace as the pod using this volume. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims type: string readOnly: - description: readOnly Will force the ReadOnly setting - in VolumeMounts. Default false. + description: |- + readOnly Will force the ReadOnly setting in VolumeMounts. + Default false. type: boolean required: - claimName @@ -3865,10 +4260,10 @@ spec: machine properties: fsType: - description: fsType is the filesystem type to mount. - Must be a filesystem type supported by the host operating - system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred - to be "ext4" if unspecified. + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. type: string pdID: description: pdID is the ID that identifies Photon Controller @@ -3882,14 +4277,15 @@ spec: attached and mounted on kubelets host machine properties: fsType: - description: fSType represents the filesystem type to - mount Must be a filesystem type supported by the host - operating system. Ex. "ext4", "xfs". Implicitly inferred - to be "ext4" if unspecified. + description: |- + fSType represents the filesystem type to mount + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs". Implicitly inferred to be "ext4" if unspecified. type: string readOnly: - description: readOnly defaults to false (read/write). - ReadOnly here will force the ReadOnly setting in VolumeMounts. + description: |- + readOnly defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. type: boolean volumeID: description: volumeID uniquely identifies a Portworx @@ -3903,40 +4299,130 @@ spec: configmaps, and downward API properties: defaultMode: - description: defaultMode are the mode bits used to set - permissions on created files by default. Must be an - octal value between 0000 and 0777 or a decimal value - between 0 and 511. YAML accepts both octal and decimal - values, JSON requires decimal values for mode bits. - Directories within the path are not affected by this - setting. This might be in conflict with other options - that affect the file mode, like fsGroup, and the result - can be other mode bits set. + description: |- + defaultMode are the mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. format: int32 type: integer sources: - description: sources is the list of volume projections + description: |- + sources is the list of volume projections. Each entry in this list + handles one source. items: - description: Projection that may be projected along - with other supported volume types + description: |- + Projection that may be projected along with other supported volume types. + Exactly one of these fields must be set. properties: + clusterTrustBundle: + description: |- + ClusterTrustBundle allows a pod to access the `.spec.trustBundle` field + of ClusterTrustBundle objects in an auto-updating file. + + Alpha, gated by the ClusterTrustBundleProjection feature gate. + + ClusterTrustBundle objects can either be selected by name, or by the + combination of signer name and a label selector. + + Kubelet performs aggressive normalization of the PEM contents written + into the pod filesystem. Esoteric PEM features such as inter-block + comments and block headers are stripped. Certificates are deduplicated. + The ordering of certificates within the file is arbitrary, and Kubelet + may change the order over time. + properties: + labelSelector: + description: |- + Select all ClusterTrustBundles that match this label selector. Only has + effect if signerName is set. Mutually-exclusive with name. If unset, + interpreted as "match nothing". If set but empty, interpreted as "match + everything". + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The + requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + name: + description: |- + Select a single ClusterTrustBundle by object name. Mutually-exclusive + with signerName and labelSelector. + type: string + optional: + description: |- + If true, don't block pod startup if the referenced ClusterTrustBundle(s) + aren't available. If using name, then the named ClusterTrustBundle is + allowed not to exist. If using signerName, then the combination of + signerName and labelSelector is allowed to match zero + ClusterTrustBundles. + type: boolean + path: + description: Relative path from the volume + root to write the bundle. + type: string + signerName: + description: |- + Select all ClusterTrustBundles that match this signer name. + Mutually-exclusive with name. The contents of all selected + ClusterTrustBundles will be unified and deduplicated. + type: string + required: + - path + type: object configMap: description: configMap information about the configMap data to project properties: items: - description: items if unspecified, each key-value - pair in the Data field of the referenced - ConfigMap will be projected into the volume - as a file whose name is the key and content - is the value. If specified, the listed keys - will be projected into the specified paths, - and unlisted keys will not be present. If - a key is specified which is not present - in the ConfigMap, the volume setup will - error unless it is marked optional. Paths - must be relative and may not contain the - '..' path or start with '..'. + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + ConfigMap will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. items: description: Maps a string key to a path within a volume. @@ -3945,37 +4431,36 @@ spec: description: key is the key to project. type: string mode: - description: 'mode is Optional: mode - bits used to set permissions on this - file. Must be an octal value between - 0000 and 0777 or a decimal value between - 0 and 511. YAML accepts both octal - and decimal values, JSON requires - decimal values for mode bits. If not - specified, the volume defaultMode - will be used. This might be in conflict - with other options that affect the - file mode, like fsGroup, and the result - can be other mode bits set.' + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. format: int32 type: integer path: - description: path is the relative path - of the file to map the key to. May - not be an absolute path. May not contain - the path element '..'. May not start - with the string '..'. + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. type: string required: - key - path type: object type: array + x-kubernetes-list-type: atomic name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string optional: description: optional specify whether the @@ -3998,7 +4483,7 @@ spec: fieldRef: description: 'Required: Selects a field of the pod: only annotations, labels, - name and namespace are supported.' + name, namespace and uid are supported.' properties: apiVersion: description: Version of the schema @@ -4014,18 +4499,13 @@ spec: type: object x-kubernetes-map-type: atomic mode: - description: 'Optional: mode bits used - to set permissions on this file, must - be an octal value between 0000 and - 0777 or a decimal value between 0 - and 511. YAML accepts both octal and - decimal values, JSON requires decimal - values for mode bits. If not specified, - the volume defaultMode will be used. - This might be in conflict with other - options that affect the file mode, - like fsGroup, and the result can be - other mode bits set.' + description: |- + Optional: mode bits used to set permissions on this file, must be an octal value + between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. format: int32 type: integer path: @@ -4037,11 +4517,9 @@ spec: path must not start with ''..''' type: string resourceFieldRef: - description: 'Selects a resource of - the container: only resources limits - and requests (limits.cpu, limits.memory, - requests.cpu and requests.memory) - are currently supported.' + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. properties: containerName: description: 'Container name: required @@ -4069,24 +4547,21 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic type: object secret: description: secret information about the secret data to project properties: items: - description: items if unspecified, each key-value - pair in the Data field of the referenced - Secret will be projected into the volume - as a file whose name is the key and content - is the value. If specified, the listed keys - will be projected into the specified paths, - and unlisted keys will not be present. If - a key is specified which is not present - in the Secret, the volume setup will error - unless it is marked optional. Paths must - be relative and may not contain the '..' - path or start with '..'. + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + Secret will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. items: description: Maps a string key to a path within a volume. @@ -4095,37 +4570,36 @@ spec: description: key is the key to project. type: string mode: - description: 'mode is Optional: mode - bits used to set permissions on this - file. Must be an octal value between - 0000 and 0777 or a decimal value between - 0 and 511. YAML accepts both octal - and decimal values, JSON requires - decimal values for mode bits. If not - specified, the volume defaultMode - will be used. This might be in conflict - with other options that affect the - file mode, like fsGroup, and the result - can be other mode bits set.' + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. format: int32 type: integer path: - description: path is the relative path - of the file to map the key to. May - not be an absolute path. May not contain - the path element '..'. May not start - with the string '..'. + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. type: string required: - key - path type: object type: array + x-kubernetes-list-type: atomic name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string optional: description: optional field specify whether @@ -4138,29 +4612,25 @@ spec: about the serviceAccountToken data to project properties: audience: - description: audience is the intended audience - of the token. A recipient of a token must - identify itself with an identifier specified - in the audience of the token, and otherwise - should reject the token. The audience defaults - to the identifier of the apiserver. + description: |- + audience is the intended audience of the token. A recipient of a token + must identify itself with an identifier specified in the audience of the + token, and otherwise should reject the token. The audience defaults to the + identifier of the apiserver. type: string expirationSeconds: - description: expirationSeconds is the requested - duration of validity of the service account - token. As the token approaches expiration, - the kubelet volume plugin will proactively - rotate the service account token. The kubelet - will start trying to rotate the token if - the token is older than 80 percent of its - time to live or if the token is older than - 24 hours.Defaults to 1 hour and must be - at least 10 minutes. + description: |- + expirationSeconds is the requested duration of validity of the service + account token. As the token approaches expiration, the kubelet volume + plugin will proactively rotate the service account token. The kubelet will + start trying to rotate the token if the token is older than 80 percent of + its time to live or if the token is older than 24 hours.Defaults to 1 hour + and must be at least 10 minutes. format: int64 type: integer path: - description: path is the path relative to - the mount point of the file to project the + description: |- + path is the path relative to the mount point of the file to project the token into. type: string required: @@ -4168,34 +4638,37 @@ spec: type: object type: object type: array + x-kubernetes-list-type: atomic type: object quobyte: description: quobyte represents a Quobyte mount on the host that shares a pod's lifetime properties: group: - description: group to map volume access to Default is - no group + description: |- + group to map volume access to + Default is no group type: string readOnly: - description: readOnly here will force the Quobyte volume - to be mounted with read-only permissions. Defaults - to false. + description: |- + readOnly here will force the Quobyte volume to be mounted with read-only permissions. + Defaults to false. type: boolean registry: - description: registry represents a single or multiple - Quobyte Registry services specified as a string as - host:port pair (multiple entries are separated with - commas) which acts as the central registry for volumes + description: |- + registry represents a single or multiple Quobyte Registry services + specified as a string as host:port pair (multiple entries are separated with commas) + which acts as the central registry for volumes type: string tenant: - description: tenant owning the given Quobyte volume - in the Backend Used with dynamically provisioned Quobyte - volumes, value is set by the plugin + description: |- + tenant owning the given Quobyte volume in the Backend + Used with dynamically provisioned Quobyte volumes, value is set by the plugin type: string user: - description: user to map volume access to Defaults to - serivceaccount user + description: |- + user to map volume access to + Defaults to serivceaccount user type: string volume: description: volume is a string that references an already @@ -4206,57 +4679,74 @@ spec: - volume type: object rbd: - description: 'rbd represents a Rados Block Device mount - on the host that shares a pod''s lifetime. More info: - https://examples.k8s.io/volumes/rbd/README.md' + description: |- + rbd represents a Rados Block Device mount on the host that shares a pod's lifetime. + More info: https://examples.k8s.io/volumes/rbd/README.md properties: fsType: - description: 'fsType is the filesystem type of the volume - that you want to mount. Tip: Ensure that the filesystem - type is supported by the host operating system. Examples: - "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" - if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd - TODO: how do we prevent errors in the filesystem from - compromising the machine' + description: |- + fsType is the filesystem type of the volume that you want to mount. + Tip: Ensure that the filesystem type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd type: string image: - description: 'image is the rados image name. More info: - https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + description: |- + image is the rados image name. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it type: string keyring: - description: 'keyring is the path to key ring for RBDUser. - Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + default: /etc/ceph/keyring + description: |- + keyring is the path to key ring for RBDUser. + Default is /etc/ceph/keyring. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it type: string monitors: - description: 'monitors is a collection of Ceph monitors. - More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + description: |- + monitors is a collection of Ceph monitors. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it items: type: string type: array + x-kubernetes-list-type: atomic pool: - description: 'pool is the rados pool name. Default is - rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + default: rbd + description: |- + pool is the rados pool name. + Default is rbd. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it type: string readOnly: - description: 'readOnly here will force the ReadOnly - setting in VolumeMounts. Defaults to false. More info: - https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + description: |- + readOnly here will force the ReadOnly setting in VolumeMounts. + Defaults to false. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it type: boolean secretRef: - description: 'secretRef is name of the authentication - secret for RBDUser. If provided overrides keyring. - Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + description: |- + secretRef is name of the authentication secret for RBDUser. If provided + overrides keyring. + Default is nil. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it properties: name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string type: object x-kubernetes-map-type: atomic user: - description: 'user is the rados user name. Default is - admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + default: admin + description: |- + user is the rados user name. + Default is admin. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it type: string required: - image @@ -4267,9 +4757,12 @@ spec: attached and mounted on Kubernetes nodes. properties: fsType: - description: fsType is the filesystem type to mount. - Must be a filesystem type supported by the host operating - system. Ex. "ext4", "xfs", "ntfs". Default is "xfs". + default: xfs + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". + Default is "xfs". type: string gateway: description: gateway is the host address of the ScaleIO @@ -4280,18 +4773,23 @@ spec: Protection Domain for the configured storage. type: string readOnly: - description: readOnly Defaults to false (read/write). - ReadOnly here will force the ReadOnly setting in VolumeMounts. + description: |- + readOnly Defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. type: boolean secretRef: - description: secretRef references to the secret for - ScaleIO user and other sensitive information. If this - is not provided, Login operation will fail. + description: |- + secretRef references to the secret for ScaleIO user and other + sensitive information. If this is not provided, Login operation will fail. properties: name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string type: object x-kubernetes-map-type: atomic @@ -4300,8 +4798,9 @@ spec: with Gateway, default false type: boolean storageMode: - description: storageMode indicates whether the storage - for a volume should be ThickProvisioned or ThinProvisioned. + default: ThinProvisioned + description: |- + storageMode indicates whether the storage for a volume should be ThickProvisioned or ThinProvisioned. Default is ThinProvisioned. type: string storagePool: @@ -4313,9 +4812,9 @@ spec: as configured in ScaleIO. type: string volumeName: - description: volumeName is the name of a volume already - created in the ScaleIO system that is associated with - this volume source. + description: |- + volumeName is the name of a volume already created in the ScaleIO system + that is associated with this volume source. type: string required: - gateway @@ -4323,33 +4822,30 @@ spec: - system type: object secret: - description: 'secret represents a secret that should populate - this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + description: |- + secret represents a secret that should populate this volume. + More info: https://kubernetes.io/docs/concepts/storage/volumes#secret properties: defaultMode: - description: 'defaultMode is Optional: mode bits used - to set permissions on created files by default. Must - be an octal value between 0000 and 0777 or a decimal - value between 0 and 511. YAML accepts both octal and - decimal values, JSON requires decimal values for mode - bits. Defaults to 0644. Directories within the path - are not affected by this setting. This might be in - conflict with other options that affect the file mode, - like fsGroup, and the result can be other mode bits - set.' + description: |- + defaultMode is Optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values + for mode bits. Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. format: int32 type: integer items: - description: items If unspecified, each key-value pair - in the Data field of the referenced Secret will be - projected into the volume as a file whose name is - the key and content is the value. If specified, the - listed keys will be projected into the specified paths, - and unlisted keys will not be present. If a key is - specified which is not present in the Secret, the - volume setup will error unless it is marked optional. - Paths must be relative and may not contain the '..' - path or start with '..'. + description: |- + items If unspecified, each key-value pair in the Data field of the referenced + Secret will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. items: description: Maps a string key to a path within a volume. @@ -4358,22 +4854,20 @@ spec: description: key is the key to project. type: string mode: - description: 'mode is Optional: mode bits used - to set permissions on this file. Must be an - octal value between 0000 and 0777 or a decimal - value between 0 and 511. YAML accepts both octal - and decimal values, JSON requires decimal values - for mode bits. If not specified, the volume - defaultMode will be used. This might be in conflict - with other options that affect the file mode, - like fsGroup, and the result can be other mode - bits set.' + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. format: int32 type: integer path: - description: path is the relative path of the - file to map the key to. May not be an absolute - path. May not contain the path element '..'. + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. May not start with the string '..'. type: string required: @@ -4381,13 +4875,15 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic optional: description: optional field specify whether the Secret or its keys must be defined type: boolean secretName: - description: 'secretName is the name of the secret in - the pod''s namespace to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + description: |- + secretName is the name of the secret in the pod's namespace to use. + More info: https://kubernetes.io/docs/concepts/storage/volumes#secret type: string type: object storageos: @@ -4395,42 +4891,45 @@ spec: and mounted on Kubernetes nodes. properties: fsType: - description: fsType is the filesystem type to mount. - Must be a filesystem type supported by the host operating - system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred - to be "ext4" if unspecified. + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. type: string readOnly: - description: readOnly defaults to false (read/write). - ReadOnly here will force the ReadOnly setting in VolumeMounts. + description: |- + readOnly defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. type: boolean secretRef: - description: secretRef specifies the secret to use for - obtaining the StorageOS API credentials. If not specified, - default values will be attempted. + description: |- + secretRef specifies the secret to use for obtaining the StorageOS API + credentials. If not specified, default values will be attempted. properties: name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string type: object x-kubernetes-map-type: atomic volumeName: - description: volumeName is the human-readable name of - the StorageOS volume. Volume names are only unique - within a namespace. + description: |- + volumeName is the human-readable name of the StorageOS volume. Volume + names are only unique within a namespace. type: string volumeNamespace: - description: volumeNamespace specifies the scope of - the volume within StorageOS. If no namespace is specified - then the Pod's namespace will be used. This allows - the Kubernetes name scoping to be mirrored within - StorageOS for tighter integration. Set VolumeName - to any name to override the default behaviour. Set - to "default" if you are not using namespaces within - StorageOS. Namespaces that do not pre-exist within - StorageOS will be created. + description: |- + volumeNamespace specifies the scope of the volume within StorageOS. If no + namespace is specified then the Pod's namespace will be used. This allows the + Kubernetes name scoping to be mirrored within StorageOS for tighter integration. + Set VolumeName to any name to override the default behaviour. + Set to "default" if you are not using namespaces within StorageOS. + Namespaces that do not pre-exist within StorageOS will be created. type: string type: object vsphereVolume: @@ -4438,10 +4937,10 @@ spec: and mounted on kubelets host machine properties: fsType: - description: fsType is filesystem type to mount. Must - be a filesystem type supported by the host operating - system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred - to be "ext4" if unspecified. + description: |- + fsType is filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. type: string storagePolicyID: description: storagePolicyID is the storage Policy Based diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index d86cff3a..55fa95fa 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -5,23 +5,22 @@ resources: - bases/observability.kaasops.io_vectors.yaml - bases/observability.kaasops.io_vectorpipelines.yaml - bases/observability.kaasops.io_clustervectorpipelines.yaml -#+kubebuilder:scaffold:crdkustomizeresource +# +kubebuilder:scaffold:crdkustomizeresource -patchesStrategicMerge: +patches: # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. # patches here are for enabling the conversion webhook for each CRD -#- patches/webhook_in_vectors.yaml -#- patches/webhook_in_vectorpipelines.yaml -#- patches/webhook_in_clustervectorpipelines.yaml -#+kubebuilder:scaffold:crdkustomizewebhookpatch +# +kubebuilder:scaffold:crdkustomizewebhookpatch # [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. # patches here are for enabling the CA injection for each CRD -#- patches/cainjection_in_vectors.yaml -#- patches/cainjection_in_vectorpipelines.yaml -#- patches/cainjection_in_clustervectorpipelines.yaml -#+kubebuilder:scaffold:crdkustomizecainjectionpatch +#- path: patches/cainjection_in_vectors.yaml +#- path: patches/cainjection_in_vectorpipelines.yaml +#- path: patches/cainjection_in_clustervectorpipelines.yaml +# +kubebuilder:scaffold:crdkustomizecainjectionpatch +# [WEBHOOK] To enable webhook, uncomment the following section # the following config is for teaching kustomize how to do kustomization for CRDs. + configurations: - kustomizeconfig.yaml diff --git a/config/crd/patches/cainjection_in_clustervectorpipelines.yaml b/config/crd/patches/cainjection_in_clustervectorpipelines.yaml deleted file mode 100644 index 7b4b6265..00000000 --- a/config/crd/patches/cainjection_in_clustervectorpipelines.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# The following patch adds a directive for certmanager to inject CA into the CRD -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) - name: clustervectorpipelines.observability.kaasops.io diff --git a/config/crd/patches/cainjection_in_vectorpipelines.yaml b/config/crd/patches/cainjection_in_vectorpipelines.yaml deleted file mode 100644 index a6890ec8..00000000 --- a/config/crd/patches/cainjection_in_vectorpipelines.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# The following patch adds a directive for certmanager to inject CA into the CRD -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) - name: vectorpipelines.observability.kaasops.io diff --git a/config/crd/patches/cainjection_in_vectors.yaml b/config/crd/patches/cainjection_in_vectors.yaml deleted file mode 100644 index 91fe99f6..00000000 --- a/config/crd/patches/cainjection_in_vectors.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# The following patch adds a directive for certmanager to inject CA into the CRD -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) - name: vectors.observability.kaasops.io diff --git a/config/crd/patches/webhook_in_clustervectorpipelines.yaml b/config/crd/patches/webhook_in_clustervectorpipelines.yaml deleted file mode 100644 index 634883c6..00000000 --- a/config/crd/patches/webhook_in_clustervectorpipelines.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# The following patch enables a conversion webhook for the CRD -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: clustervectorpipelines.observability.kaasops.io -spec: - conversion: - strategy: Webhook - webhook: - clientConfig: - service: - namespace: system - name: webhook-service - path: /convert - conversionReviewVersions: - - v1 diff --git a/config/crd/patches/webhook_in_vectorpipelines.yaml b/config/crd/patches/webhook_in_vectorpipelines.yaml deleted file mode 100644 index b34cf2f8..00000000 --- a/config/crd/patches/webhook_in_vectorpipelines.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# The following patch enables a conversion webhook for the CRD -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: vectorpipelines.observability.kaasops.io -spec: - conversion: - strategy: Webhook - webhook: - clientConfig: - service: - namespace: system - name: webhook-service - path: /convert - conversionReviewVersions: - - v1 diff --git a/config/crd/patches/webhook_in_vectors.yaml b/config/crd/patches/webhook_in_vectors.yaml deleted file mode 100644 index 53a2f8b5..00000000 --- a/config/crd/patches/webhook_in_vectors.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# The following patch enables a conversion webhook for the CRD -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: vectors.observability.kaasops.io -spec: - conversion: - strategy: Webhook - webhook: - clientConfig: - service: - namespace: system - name: webhook-service - path: /convert - conversionReviewVersions: - - v1 diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml index 8730941f..f888f0e7 100644 --- a/config/default/kustomization.yaml +++ b/config/default/kustomization.yaml @@ -9,10 +9,12 @@ namespace: vector-operator-system namePrefix: vector-operator- # Labels to add to all resources and selectors. -#commonLabels: -# someName: someValue +#labels: +#- includeSelectors: true +# pairs: +# someName: someValue -bases: +resources: - ../crd - ../rbac - ../manager @@ -23,52 +25,127 @@ bases: #- ../certmanager # [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. #- ../prometheus +# [METRICS] Expose the controller manager metrics service. +- metrics_service.yaml +# [NETWORK POLICY] Protect the /metrics endpoint and Webhook Server with NetworkPolicy. +# Only Pod(s) running a namespace labeled with 'metrics: enabled' will be able to gather the metrics. +# Only CR(s) which requires webhooks and are applied on namespaces labeled with 'webhooks: enabled' will +# be able to communicate with the Webhook Server. +#- ../network-policy -patchesStrategicMerge: -# Protect the /metrics endpoint by putting it behind auth. -# If you want your controller-manager to expose the /metrics -# endpoint w/o any authn/z, please comment the following line. -- manager_auth_proxy_patch.yaml - -# Mount the controller config file for loading manager configurations -# through a ComponentConfig type -#- manager_config_patch.yaml +# Uncomment the patches line if you enable Metrics, and/or are using webhooks and cert-manager +patches: +# [METRICS] The following patch will enable the metrics endpoint using HTTPS and the port :8443. +# More info: https://book.kubebuilder.io/reference/metrics +- path: manager_metrics_patch.yaml + target: + kind: Deployment # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in # crd/kustomization.yaml -#- manager_webhook_patch.yaml +#- path: manager_webhook_patch.yaml # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. # Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks. # 'CERTMANAGER' needs to be enabled to use ca injection -#- webhookcainjection_patch.yaml +#- path: webhookcainjection_patch.yaml -# the following config is for teaching kustomize how to do var substitution -vars: # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. -#- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR -# objref: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert # this name should match the one in certificate.yaml -# fieldref: -# fieldpath: metadata.namespace -#- name: CERTIFICATE_NAME -# objref: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert # this name should match the one in certificate.yaml -#- name: SERVICE_NAMESPACE # namespace of the service -# objref: -# kind: Service -# version: v1 -# name: webhook-service -# fieldref: -# fieldpath: metadata.namespace -#- name: SERVICE_NAME -# objref: -# kind: Service -# version: v1 -# name: webhook-service +# Uncomment the following replacements to add the cert-manager CA injection annotations +#replacements: +# - source: # Add cert-manager annotation to ValidatingWebhookConfiguration, MutatingWebhookConfiguration and CRDs +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert # this name should match the one in certificate.yaml +# fieldPath: .metadata.namespace # namespace of the certificate CR +# targets: +# - select: +# kind: ValidatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 0 +# create: true +# - select: +# kind: MutatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 0 +# create: true +# - select: +# kind: CustomResourceDefinition +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 0 +# create: true +# - source: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert # this name should match the one in certificate.yaml +# fieldPath: .metadata.name +# targets: +# - select: +# kind: ValidatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 1 +# create: true +# - select: +# kind: MutatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 1 +# create: true +# - select: +# kind: CustomResourceDefinition +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 1 +# create: true +# - source: # Add cert-manager annotation to the webhook Service +# kind: Service +# version: v1 +# name: webhook-service +# fieldPath: .metadata.name # namespace of the service +# targets: +# - select: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# fieldPaths: +# - .spec.dnsNames.0 +# - .spec.dnsNames.1 +# options: +# delimiter: '.' +# index: 0 +# create: true +# - source: +# kind: Service +# version: v1 +# name: webhook-service +# fieldPath: .metadata.namespace # namespace of the service +# targets: +# - select: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# fieldPaths: +# - .spec.dnsNames.0 +# - .spec.dnsNames.1 +# options: +# delimiter: '.' +# index: 1 +# create: true diff --git a/config/default/manager_auth_proxy_patch.yaml b/config/default/manager_auth_proxy_patch.yaml deleted file mode 100644 index cec149a0..00000000 --- a/config/default/manager_auth_proxy_patch.yaml +++ /dev/null @@ -1,39 +0,0 @@ -# This patch inject a sidecar container which is a HTTP proxy for the -# controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews. -apiVersion: apps/v1 -kind: Deployment -metadata: - name: controller-manager - namespace: system -spec: - template: - spec: - containers: - - name: kube-rbac-proxy - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - "ALL" - image: gcr.io/kubebuilder/kube-rbac-proxy:v0.13.0 - args: - - "--secure-listen-address=0.0.0.0:8443" - - "--upstream=http://127.0.0.1:8080/" - - "--logtostderr=true" - - "--v=0" - ports: - - containerPort: 8443 - protocol: TCP - name: https - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 5m - memory: 64Mi - - name: manager - args: - - "--health-probe-bind-address=:8081" - - "--metrics-bind-address=127.0.0.1:8080" - - "--leader-elect" diff --git a/config/default/manager_config_patch.yaml b/config/default/manager_config_patch.yaml deleted file mode 100644 index 6c400155..00000000 --- a/config/default/manager_config_patch.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: controller-manager - namespace: system -spec: - template: - spec: - containers: - - name: manager - args: - - "--config=controller_manager_config.yaml" - volumeMounts: - - name: manager-config - mountPath: /controller_manager_config.yaml - subPath: controller_manager_config.yaml - volumes: - - name: manager-config - configMap: - name: manager-config diff --git a/config/default/manager_metrics_patch.yaml b/config/default/manager_metrics_patch.yaml new file mode 100644 index 00000000..2aaef653 --- /dev/null +++ b/config/default/manager_metrics_patch.yaml @@ -0,0 +1,4 @@ +# This patch adds the args to allow exposing the metrics endpoint using HTTPS +- op: add + path: /spec/template/spec/containers/0/args/0 + value: --metrics-bind-address=:8443 diff --git a/config/rbac/auth_proxy_service.yaml b/config/default/metrics_service.yaml similarity index 70% rename from config/rbac/auth_proxy_service.yaml rename to config/default/metrics_service.yaml index 71f17972..302a659d 100644 --- a/config/rbac/auth_proxy_service.yaml +++ b/config/default/metrics_service.yaml @@ -3,6 +3,8 @@ kind: Service metadata: labels: control-plane: controller-manager + app.kubernetes.io/name: vector-operator + app.kubernetes.io/managed-by: kustomize name: controller-manager-metrics-service namespace: system spec: @@ -10,6 +12,6 @@ spec: - name: https port: 8443 protocol: TCP - targetPort: https + targetPort: 8443 selector: control-plane: controller-manager diff --git a/config/manager/controller_manager_config.yaml b/config/manager/controller_manager_config.yaml deleted file mode 100644 index b62ddb08..00000000 --- a/config/manager/controller_manager_config.yaml +++ /dev/null @@ -1,21 +0,0 @@ -apiVersion: controller-runtime.sigs.k8s.io/v1alpha1 -kind: ControllerManagerConfig -health: - healthProbeBindAddress: :8081 -metrics: - bindAddress: 127.0.0.1:8080 -webhook: - port: 9443 -leaderElection: - leaderElect: true - resourceName: 79cbe7f3.kaasops.io -# leaderElectionReleaseOnCancel defines if the leader should step down volume -# when the Manager ends. This requires the binary to immediately end when the -# Manager is stopped, otherwise, this setting is unsafe. Setting this significantly -# speeds up voluntary leader transitions as the new leader don't have to wait -# LeaseDuration time first. -# In the default scaffold provided, the program ends immediately after -# the manager stops, so would be fine to enable this option. However, -# if you are doing or is intended to do any operation such as perform cleanups -# after the manager stops then its usage might be unsafe. -# leaderElectionReleaseOnCancel: true diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 5e793dd1..cd2708b7 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -1,16 +1,8 @@ resources: - manager.yaml - -generatorOptions: - disableNameSuffixHash: true - -configMapGenerator: -- files: - - controller_manager_config.yaml - name: manager-config apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: controller - newTag: latest + newName: example.com/vector-operator + newTag: v0.0.1 diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index 878ad486..6a4ffd28 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -3,6 +3,8 @@ kind: Namespace metadata: labels: control-plane: controller-manager + app.kubernetes.io/name: vector-operator + app.kubernetes.io/managed-by: kustomize name: system --- apiVersion: apps/v1 @@ -12,6 +14,8 @@ metadata: namespace: system labels: control-plane: controller-manager + app.kubernetes.io/name: vector-operator + app.kubernetes.io/managed-by: kustomize spec: selector: matchLabels: @@ -24,6 +28,26 @@ spec: labels: control-plane: controller-manager spec: + # TODO(user): Uncomment the following code to configure the nodeAffinity expression + # according to the platforms which are supported by your solution. + # It is considered best practice to support multiple architectures. You can + # build your manager image using the makefile target docker-buildx. + # affinity: + # nodeAffinity: + # requiredDuringSchedulingIgnoredDuringExecution: + # nodeSelectorTerms: + # - matchExpressions: + # - key: kubernetes.io/arch + # operator: In + # values: + # - amd64 + # - arm64 + # - ppc64le + # - s390x + # - key: kubernetes.io/os + # operator: In + # values: + # - linux securityContext: runAsNonRoot: true # TODO(user): For common cases that do not require escalating privileges @@ -37,14 +61,15 @@ spec: - command: - /manager args: - - --leader-elect + - --leader-elect + - --health-probe-bind-address=:8081 image: controller:latest name: manager securityContext: allowPrivilegeEscalation: false capabilities: drop: - - "ALL" + - "ALL" livenessProbe: httpGet: path: /healthz diff --git a/config/manifests/bases/vector-operator.clusterserviceversion.yaml b/config/manifests/bases/vector-operator.clusterserviceversion.yaml deleted file mode 100644 index 16fb45f6..00000000 --- a/config/manifests/bases/vector-operator.clusterserviceversion.yaml +++ /dev/null @@ -1,75 +0,0 @@ -apiVersion: operators.coreos.com/v1alpha1 -kind: ClusterServiceVersion -metadata: - annotations: - alm-examples: '[]' - capabilities: Basic Install - name: vector-operator.v0.0.0 - namespace: placeholder -spec: - apiservicedefinitions: {} - customresourcedefinitions: - owned: - - description: ClusterVectorPipeline is the Schema for the clustervectorpipelines - API - displayName: Cluster Vector Pipeline - kind: ClusterVectorPipeline - name: clustervectorpipelines.observability.kaasops.io - version: v1alpha1 - - description: VectorPipeline is the Schema for the vectorpipelines API - displayName: Vector Pipeline - kind: VectorPipeline - name: vectorpipelines.observability.kaasops.io - version: v1alpha1 - - description: Vector is the Schema for the vectors API - displayName: Vector - kind: Vector - name: vectors.observability.kaasops.io - specDescriptors: - - description: Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - if not specified - default setting will be used - displayName: Resources - path: agent.configCheck.resources - x-descriptors: - - urn:alm:descriptor:com.tectonic.ui:resourceRequirements - - description: Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - if not specified - default setting will be used - displayName: Resources - path: agent.resources - x-descriptors: - - urn:alm:descriptor:com.tectonic.ui:resourceRequirements - version: v1alpha1 - description: The operator deploys and configures a vector agent daemonset on every - node to collect container and application logs from the node file system - displayName: vector-operator - icon: - - base64data: "" - mediatype: "" - install: - spec: - deployments: null - strategy: "" - installModes: - - supported: false - type: OwnNamespace - - supported: false - type: SingleNamespace - - supported: false - type: MultiNamespace - - supported: true - type: AllNamespaces - keywords: - - vector - - observability - - logging - links: - - name: Vector Operator - url: https://vector-operator.domain - maintainers: - - email: info@kaasops.io - name: kaasops - maturity: alpha - provider: - name: kaasops.io - url: https://kaasops.io/ - version: 0.0.0 diff --git a/config/manifests/kustomization.yaml b/config/manifests/kustomization.yaml deleted file mode 100644 index f87296ce..00000000 --- a/config/manifests/kustomization.yaml +++ /dev/null @@ -1,27 +0,0 @@ -# These resources constitute the fully configured set of manifests -# used to generate the 'manifests/' directory in a bundle. -resources: -- bases/vector-operator.clusterserviceversion.yaml -- ../default -- ../samples -- ../scorecard - -# [WEBHOOK] To enable webhooks, uncomment all the sections with [WEBHOOK] prefix. -# Do NOT uncomment sections with prefix [CERTMANAGER], as OLM does not support cert-manager. -# These patches remove the unnecessary "cert" volume and its manager container volumeMount. -#patchesJson6902: -#- target: -# group: apps -# version: v1 -# kind: Deployment -# name: controller-manager -# namespace: system -# patch: |- -# # Remove the manager container's "cert" volumeMount, since OLM will create and mount a set of certs. -# # Update the indices in this path if adding or removing containers/volumeMounts in the manager's Deployment. -# - op: remove -# path: /spec/template/spec/containers/1/volumeMounts/0 -# # Remove the "cert" volume, since OLM will create and mount a set of certs. -# # Update the indices in this path if adding or removing volumes in the manager's Deployment. -# - op: remove -# path: /spec/template/spec/volumes/0 diff --git a/config/network-policy/allow-metrics-traffic.yaml b/config/network-policy/allow-metrics-traffic.yaml new file mode 100644 index 00000000..10c43a01 --- /dev/null +++ b/config/network-policy/allow-metrics-traffic.yaml @@ -0,0 +1,26 @@ +# This NetworkPolicy allows ingress traffic +# with Pods running on namespaces labeled with 'metrics: enabled'. Only Pods on those +# namespaces are able to gathering data from the metrics endpoint. +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + labels: + app.kubernetes.io/name: vector-operator + app.kubernetes.io/managed-by: kustomize + name: allow-metrics-traffic + namespace: system +spec: + podSelector: + matchLabels: + control-plane: controller-manager + policyTypes: + - Ingress + ingress: + # This allows ingress traffic from any namespace with the label metrics: enabled + - from: + - namespaceSelector: + matchLabels: + metrics: enabled # Only from namespaces with this label + ports: + - port: 8443 + protocol: TCP diff --git a/config/network-policy/kustomization.yaml b/config/network-policy/kustomization.yaml new file mode 100644 index 00000000..ec0fb5e5 --- /dev/null +++ b/config/network-policy/kustomization.yaml @@ -0,0 +1,2 @@ +resources: +- allow-metrics-traffic.yaml diff --git a/config/prometheus/monitor.yaml b/config/prometheus/monitor.yaml index d19136ae..97e9f7e6 100644 --- a/config/prometheus/monitor.yaml +++ b/config/prometheus/monitor.yaml @@ -1,19 +1,29 @@ - # Prometheus Monitor Service (Metrics) apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: labels: control-plane: controller-manager + app.kubernetes.io/name: vector-operator + app.kubernetes.io/managed-by: kustomize name: controller-manager-metrics-monitor namespace: system spec: endpoints: - path: /metrics - port: https + port: https # Ensure this is the name of the port that exposes HTTPS metrics scheme: https bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token tlsConfig: + # TODO(user): The option insecureSkipVerify: true is not recommended for production since it disables + # certificate verification. This poses a significant security risk by making the system vulnerable to + # man-in-the-middle attacks, where an attacker could intercept and manipulate the communication between + # Prometheus and the monitored services. This could lead to unauthorized access to sensitive metrics data, + # compromising the integrity and confidentiality of the information. + # Please use the following options for secure configurations: + # caFile: /etc/metrics-certs/ca.crt + # certFile: /etc/metrics-certs/tls.crt + # keyFile: /etc/metrics-certs/tls.key insecureSkipVerify: true selector: matchLabels: diff --git a/config/rbac/clustervectorpipeline_editor_role.yaml b/config/rbac/clustervectorpipeline_editor_role.yaml index 6f7b4e4b..9f6d8d40 100644 --- a/config/rbac/clustervectorpipeline_editor_role.yaml +++ b/config/rbac/clustervectorpipeline_editor_role.yaml @@ -2,6 +2,9 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: + labels: + app.kubernetes.io/name: vector-operator + app.kubernetes.io/managed-by: kustomize name: clustervectorpipeline-editor-role rules: - apiGroups: diff --git a/config/rbac/clustervectorpipeline_viewer_role.yaml b/config/rbac/clustervectorpipeline_viewer_role.yaml index 95ab58ce..27446a12 100644 --- a/config/rbac/clustervectorpipeline_viewer_role.yaml +++ b/config/rbac/clustervectorpipeline_viewer_role.yaml @@ -2,6 +2,9 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: + labels: + app.kubernetes.io/name: vector-operator + app.kubernetes.io/managed-by: kustomize name: clustervectorpipeline-viewer-role rules: - apiGroups: diff --git a/config/rbac/kustomization.yaml b/config/rbac/kustomization.yaml index 731832a6..02562986 100644 --- a/config/rbac/kustomization.yaml +++ b/config/rbac/kustomization.yaml @@ -9,10 +9,23 @@ resources: - role_binding.yaml - leader_election_role.yaml - leader_election_role_binding.yaml -# Comment the following 4 lines if you want to disable -# the auth proxy (https://github.com/brancz/kube-rbac-proxy) -# which protects your /metrics endpoint. -- auth_proxy_service.yaml -- auth_proxy_role.yaml -- auth_proxy_role_binding.yaml -- auth_proxy_client_clusterrole.yaml +# The following RBAC configurations are used to protect +# the metrics endpoint with authn/authz. These configurations +# ensure that only authorized users and service accounts +# can access the metrics endpoint. Comment the following +# permissions if you want to disable this protection. +# More info: https://book.kubebuilder.io/reference/metrics.html +- metrics_auth_role.yaml +- metrics_auth_role_binding.yaml +- metrics_reader_role.yaml +# For each CRD, "Editor" and "Viewer" roles are scaffolded by +# default, aiding admins in cluster management. Those roles are +# not used by the Project itself. You can comment the following lines +# if you do not want those helpers be installed with your Project. +- clustervectorpipeline_editor_role.yaml +- clustervectorpipeline_viewer_role.yaml +- vectorpipeline_editor_role.yaml +- vectorpipeline_viewer_role.yaml +- vector_editor_role.yaml +- vector_viewer_role.yaml + diff --git a/config/rbac/leader_election_role.yaml b/config/rbac/leader_election_role.yaml index 4190ec80..d57e98be 100644 --- a/config/rbac/leader_election_role.yaml +++ b/config/rbac/leader_election_role.yaml @@ -2,6 +2,9 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: + labels: + app.kubernetes.io/name: vector-operator + app.kubernetes.io/managed-by: kustomize name: leader-election-role rules: - apiGroups: diff --git a/config/rbac/leader_election_role_binding.yaml b/config/rbac/leader_election_role_binding.yaml index 1d1321ed..954f9b21 100644 --- a/config/rbac/leader_election_role_binding.yaml +++ b/config/rbac/leader_election_role_binding.yaml @@ -1,6 +1,9 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: + labels: + app.kubernetes.io/name: vector-operator + app.kubernetes.io/managed-by: kustomize name: leader-election-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io diff --git a/config/rbac/auth_proxy_role.yaml b/config/rbac/metrics_auth_role.yaml similarity index 90% rename from config/rbac/auth_proxy_role.yaml rename to config/rbac/metrics_auth_role.yaml index 80e1857c..32d2e4ec 100644 --- a/config/rbac/auth_proxy_role.yaml +++ b/config/rbac/metrics_auth_role.yaml @@ -1,7 +1,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - name: proxy-role + name: metrics-auth-role rules: - apiGroups: - authentication.k8s.io diff --git a/config/rbac/auth_proxy_role_binding.yaml b/config/rbac/metrics_auth_role_binding.yaml similarity index 79% rename from config/rbac/auth_proxy_role_binding.yaml rename to config/rbac/metrics_auth_role_binding.yaml index ec7acc0a..e775d67f 100644 --- a/config/rbac/auth_proxy_role_binding.yaml +++ b/config/rbac/metrics_auth_role_binding.yaml @@ -1,11 +1,11 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: - name: proxy-rolebinding + name: metrics-auth-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: proxy-role + name: metrics-auth-role subjects: - kind: ServiceAccount name: controller-manager diff --git a/config/rbac/auth_proxy_client_clusterrole.yaml b/config/rbac/metrics_reader_role.yaml similarity index 100% rename from config/rbac/auth_proxy_client_clusterrole.yaml rename to config/rbac/metrics_reader_role.yaml diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index f716aac6..867b6c0d 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -2,25 +2,24 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - creationTimestamp: null name: manager-role rules: - apiGroups: - "" resources: - - sercrets + - events + - namespaces + - nodes verbs: - - create - - delete - - get - list - - patch - - update - watch - apiGroups: - "" resources: + - pods + - secrets - serviceaccounts + - services verbs: - create - delete @@ -32,15 +31,10 @@ rules: - apiGroups: - "" resources: - - services + - pods/log verbs: - - create - - delete - get - list - - patch - - update - - watch - apiGroups: - apps resources: @@ -58,6 +52,7 @@ rules: resources: - clustervectorpipelines - vectorpipelines + - vectors verbs: - create - delete @@ -71,6 +66,7 @@ rules: resources: - clustervectorpipelines/finalizers - vectorpipelines/finalizers + - vectors/finalizers verbs: - update - apiGroups: @@ -78,31 +74,6 @@ rules: resources: - clustervectorpipelines/status - vectorpipelines/status - verbs: - - get - - patch - - update -- apiGroups: - - observability.kaasops.io - resources: - - vectors - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - observability.kaasops.io - resources: - - vectors/finalizers - verbs: - - update -- apiGroups: - - observability.kaasops.io - resources: - vectors/status verbs: - get @@ -112,17 +83,6 @@ rules: - rbac.authorization.k8s.io resources: - clusterrolebindings - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - rbac.authorization.k8s.io - resources: - clusterroles verbs: - create diff --git a/config/rbac/role_binding.yaml b/config/rbac/role_binding.yaml index 2070ede4..cbc5e9ee 100644 --- a/config/rbac/role_binding.yaml +++ b/config/rbac/role_binding.yaml @@ -1,6 +1,9 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: + labels: + app.kubernetes.io/name: vector-operator + app.kubernetes.io/managed-by: kustomize name: manager-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io diff --git a/config/rbac/service_account.yaml b/config/rbac/service_account.yaml index 7cd6025b..0b0bcd69 100644 --- a/config/rbac/service_account.yaml +++ b/config/rbac/service_account.yaml @@ -1,5 +1,8 @@ apiVersion: v1 kind: ServiceAccount metadata: + labels: + app.kubernetes.io/name: vector-operator + app.kubernetes.io/managed-by: kustomize name: controller-manager namespace: system diff --git a/config/rbac/vector_editor_role.yaml b/config/rbac/vector_editor_role.yaml index 59d2906a..4c79b70b 100644 --- a/config/rbac/vector_editor_role.yaml +++ b/config/rbac/vector_editor_role.yaml @@ -2,6 +2,9 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: + labels: + app.kubernetes.io/name: vector-operator + app.kubernetes.io/managed-by: kustomize name: vector-editor-role rules: - apiGroups: diff --git a/config/rbac/vector_viewer_role.yaml b/config/rbac/vector_viewer_role.yaml index 3e38e8c8..ca275902 100644 --- a/config/rbac/vector_viewer_role.yaml +++ b/config/rbac/vector_viewer_role.yaml @@ -2,6 +2,9 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: + labels: + app.kubernetes.io/name: vector-operator + app.kubernetes.io/managed-by: kustomize name: vector-viewer-role rules: - apiGroups: diff --git a/config/rbac/vectorpipeline_editor_role.yaml b/config/rbac/vectorpipeline_editor_role.yaml index cdf5850a..4387e716 100644 --- a/config/rbac/vectorpipeline_editor_role.yaml +++ b/config/rbac/vectorpipeline_editor_role.yaml @@ -2,6 +2,9 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: + labels: + app.kubernetes.io/name: vector-operator + app.kubernetes.io/managed-by: kustomize name: vectorpipeline-editor-role rules: - apiGroups: diff --git a/config/rbac/vectorpipeline_viewer_role.yaml b/config/rbac/vectorpipeline_viewer_role.yaml index 485298ca..a56d8b7d 100644 --- a/config/rbac/vectorpipeline_viewer_role.yaml +++ b/config/rbac/vectorpipeline_viewer_role.yaml @@ -2,6 +2,9 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: + labels: + app.kubernetes.io/name: vector-operator + app.kubernetes.io/managed-by: kustomize name: vectorpipeline-viewer-role rules: - apiGroups: diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index 2dc737f5..1db9c8bd 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -1,6 +1,6 @@ -## Append samples you want in your CSV to this file as resources ## +## Append samples of your project ## resources: - observability_v1alpha1_vector.yaml - observability_v1alpha1_vectorpipeline.yaml - observability_v1alpha1_clustervectorpipeline.yaml -#+kubebuilder:scaffold:manifestskustomizesamples +# +kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/scorecard/bases/config.yaml b/config/scorecard/bases/config.yaml deleted file mode 100644 index c7704784..00000000 --- a/config/scorecard/bases/config.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: scorecard.operatorframework.io/v1alpha3 -kind: Configuration -metadata: - name: config -stages: -- parallel: true - tests: [] diff --git a/config/scorecard/kustomization.yaml b/config/scorecard/kustomization.yaml deleted file mode 100644 index 50cd2d08..00000000 --- a/config/scorecard/kustomization.yaml +++ /dev/null @@ -1,16 +0,0 @@ -resources: -- bases/config.yaml -patchesJson6902: -- path: patches/basic.config.yaml - target: - group: scorecard.operatorframework.io - version: v1alpha3 - kind: Configuration - name: config -- path: patches/olm.config.yaml - target: - group: scorecard.operatorframework.io - version: v1alpha3 - kind: Configuration - name: config -#+kubebuilder:scaffold:patchesJson6902 diff --git a/config/scorecard/patches/basic.config.yaml b/config/scorecard/patches/basic.config.yaml deleted file mode 100644 index 90f7ef77..00000000 --- a/config/scorecard/patches/basic.config.yaml +++ /dev/null @@ -1,10 +0,0 @@ -- op: add - path: /stages/0/tests/- - value: - entrypoint: - - scorecard-test - - basic-check-spec - image: quay.io/operator-framework/scorecard-test:v1.23.0 - labels: - suite: basic - test: basic-check-spec-test diff --git a/config/scorecard/patches/olm.config.yaml b/config/scorecard/patches/olm.config.yaml deleted file mode 100644 index b55840e1..00000000 --- a/config/scorecard/patches/olm.config.yaml +++ /dev/null @@ -1,50 +0,0 @@ -- op: add - path: /stages/0/tests/- - value: - entrypoint: - - scorecard-test - - olm-bundle-validation - image: quay.io/operator-framework/scorecard-test:v1.23.0 - labels: - suite: olm - test: olm-bundle-validation-test -- op: add - path: /stages/0/tests/- - value: - entrypoint: - - scorecard-test - - olm-crds-have-validation - image: quay.io/operator-framework/scorecard-test:v1.23.0 - labels: - suite: olm - test: olm-crds-have-validation-test -- op: add - path: /stages/0/tests/- - value: - entrypoint: - - scorecard-test - - olm-crds-have-resources - image: quay.io/operator-framework/scorecard-test:v1.23.0 - labels: - suite: olm - test: olm-crds-have-resources-test -- op: add - path: /stages/0/tests/- - value: - entrypoint: - - scorecard-test - - olm-spec-descriptors - image: quay.io/operator-framework/scorecard-test:v1.23.0 - labels: - suite: olm - test: olm-spec-descriptors-test -- op: add - path: /stages/0/tests/- - value: - entrypoint: - - scorecard-test - - olm-status-descriptors - image: quay.io/operator-framework/scorecard-test:v1.23.0 - labels: - suite: olm - test: olm-status-descriptors-test diff --git a/go.mod b/go.mod index fca7f8aa..258dc1a5 100644 --- a/go.mod +++ b/go.mod @@ -1,99 +1,126 @@ module github.com/kaasops/vector-operator -go 1.21 +go 1.22.0 require ( - github.com/VictoriaMetrics/operator/api v0.0.0-20231128174956-7965dba77210 - github.com/go-logr/logr v1.2.4 + github.com/VictoriaMetrics/operator/api v0.0.0-20240816142248-64879fb1dd26 + github.com/go-logr/logr v1.4.2 github.com/mitchellh/mapstructure v1.5.0 - github.com/onsi/ginkgo v1.16.5 - github.com/onsi/gomega v1.20.1 + github.com/onsi/ginkgo/v2 v2.19.0 + github.com/onsi/gomega v1.33.1 github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.60.1 - github.com/stretchr/testify v1.8.2 + github.com/stretchr/testify v1.9.0 gopkg.in/yaml.v2 v2.4.0 - k8s.io/api v0.25.6 - k8s.io/apimachinery v0.25.6 - k8s.io/client-go v0.25.6 - k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73 - sigs.k8s.io/controller-runtime v0.12.3 + k8s.io/api v0.31.0 + k8s.io/apimachinery v0.31.0 + k8s.io/client-go v12.0.0+incompatible + k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 + sigs.k8s.io/controller-runtime v0.19.0 sigs.k8s.io/yaml v1.4.0 ) require ( - cloud.google.com/go/compute v1.19.3 // indirect - cloud.google.com/go/compute/metadata v0.2.3 // indirect - github.com/Azure/go-autorest v14.2.0+incompatible // indirect - github.com/Azure/go-autorest/autorest v0.11.27 // indirect - github.com/Azure/go-autorest/autorest/adal v0.9.20 // indirect - github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect - github.com/Azure/go-autorest/logger v0.2.1 // indirect - github.com/Azure/go-autorest/tracing v0.6.0 // indirect - github.com/VictoriaMetrics/VictoriaMetrics v1.91.3 // indirect - github.com/VictoriaMetrics/fasthttp v1.2.0 // indirect - github.com/VictoriaMetrics/metrics v1.24.0 // indirect - github.com/VictoriaMetrics/metricsql v0.56.2 // indirect + github.com/VictoriaMetrics/VictoriaMetrics v1.101.0 // indirect + github.com/VictoriaMetrics/metrics v1.33.1 // indirect + github.com/VictoriaMetrics/metricsql v0.75.1 // indirect + github.com/antlr4-go/antlr/v4 v4.13.0 // indirect + github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect + github.com/aws/aws-sdk-go v1.51.23 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bmatcuk/doublestar/v4 v4.6.0 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/emicklei/go-restful/v3 v3.8.0 // indirect - github.com/evanphx/json-patch v4.12.0+incompatible // indirect - github.com/fsnotify/fsnotify v1.5.1 // indirect - github.com/go-logr/zapr v1.2.3 // indirect - github.com/go-openapi/jsonpointer v0.19.6 // indirect - github.com/go-openapi/jsonreference v0.20.1 // indirect - github.com/go-openapi/swag v0.22.3 // indirect + github.com/blang/semver/v4 v4.0.0 // indirect + github.com/bmatcuk/doublestar/v4 v4.6.1 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/evanphx/json-patch/v5 v5.9.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/go-kit/log v0.2.1 // indirect + github.com/go-logfmt/logfmt v0.6.0 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-logr/zapr v1.3.0 // indirect + github.com/go-openapi/jsonpointer v0.20.2 // indirect + github.com/go-openapi/jsonreference v0.20.4 // indirect + github.com/go-openapi/swag v0.22.9 // indirect + github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang-jwt/jwt/v4 v4.2.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.3 // indirect - github.com/google/gnostic v0.5.7-v3refs // indirect - github.com/google/go-cmp v0.5.9 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/cel-go v0.20.1 // indirect + github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/imdario/mergo v0.3.12 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect + github.com/jpillora/backoff v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.16.5 // indirect + github.com/klauspost/compress v1.17.8 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/nxadm/tail v1.4.8 // indirect + github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.15.1 // indirect - github.com/prometheus/client_model v0.4.0 // indirect - github.com/prometheus/common v0.43.0 // indirect - github.com/prometheus/procfs v0.9.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/alertmanager v0.27.0 // indirect + github.com/prometheus/client_golang v1.19.1 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/common/sigv4 v0.1.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/spf13/cobra v1.8.1 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/stoewer/go-strcase v1.2.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fastjson v1.6.4 // indirect github.com/valyala/fastrand v1.1.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect + github.com/valyala/gozstd v1.20.1 // indirect github.com/valyala/histogram v1.2.0 // indirect github.com/valyala/quicktemplate v1.7.0 // indirect - go.uber.org/atomic v1.11.0 // indirect - go.uber.org/multierr v1.7.0 // indirect - go.uber.org/zap v1.19.1 // indirect - golang.org/x/crypto v0.9.0 // indirect - golang.org/x/net v0.10.0 // indirect - golang.org/x/oauth2 v0.8.0 // indirect - golang.org/x/sys v0.15.0 // indirect - golang.org/x/term v0.8.0 // indirect - golang.org/x/text v0.9.0 // indirect - golang.org/x/time v0.3.0 // indirect - gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.30.0 // indirect + github.com/x448/float16 v0.8.4 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect + go.opentelemetry.io/otel v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 // indirect + go.opentelemetry.io/otel/metric v1.28.0 // indirect + go.opentelemetry.io/otel/sdk v1.28.0 // indirect + go.opentelemetry.io/otel/trace v1.28.0 // indirect + go.opentelemetry.io/proto/otlp v1.3.1 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.26.0 // indirect + golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect + golang.org/x/net v0.26.0 // indirect + golang.org/x/oauth2 v0.21.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/term v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect + golang.org/x/time v0.5.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect + google.golang.org/grpc v1.65.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiextensions-apiserver v0.25.0 // indirect - k8s.io/component-base v0.25.0 // indirect - k8s.io/klog/v2 v2.80.0 // indirect - k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect + k8s.io/apiextensions-apiserver v0.31.0 // indirect + k8s.io/apiserver v0.31.0 // indirect + k8s.io/component-base v0.31.0 // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect + sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect ) + +replace k8s.io/client-go => k8s.io/client-go v0.31.0 diff --git a/go.sum b/go.sum index 2aa08ec9..63bc6271 100644 --- a/go.sum +++ b/go.sum @@ -1,90 +1,153 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go/compute v1.19.3 h1:DcTwsFgGev/wV5+q8o2fzgcHOaac+DKGC91ZlvpsQds= -cloud.google.com/go/compute v1.19.3/go.mod h1:qxvISKp/gYnXkSAD1ppcSOveRAmzxicEv/JlizULFrI= -cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= -github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.27 h1:F3R3q42aWytozkV8ihzcgMO4OA4cuqr3bNlsEuF6//A= -github.com/Azure/go-autorest/autorest v0.11.27/go.mod h1:7l8ybrIdUmGqZMTD0sRtAr8NvbHjfofbf8RSP2q7w7U= -github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= -github.com/Azure/go-autorest/autorest/adal v0.9.20 h1:gJ3E98kMpFB1MFqQCvA1yFab8vthOeD4VlFRQULxahg= -github.com/Azure/go-autorest/autorest/adal v0.9.20/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= -github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= -github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw= -github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= -github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= -github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= -github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/VictoriaMetrics/VictoriaMetrics v1.91.3 h1:mInejMsp7W3z4lrEzO4uQy59WnxIhMqwCeiogAId+jU= -github.com/VictoriaMetrics/VictoriaMetrics v1.91.3/go.mod h1:iDxknwOOdiyR+rBuv20cBVkRLA1jHhQ37eTS29segRw= -github.com/VictoriaMetrics/fasthttp v1.2.0 h1:nd9Wng4DlNtaI27WlYh5mGXCJOmee/2c2blTJwfyU9I= -github.com/VictoriaMetrics/fasthttp v1.2.0/go.mod h1:zv5YSmasAoSyv8sBVexfArzFDIGGTN4TfCKAtAw7IfE= -github.com/VictoriaMetrics/metrics v1.18.1/go.mod h1:ArjwVz7WpgpegX/JpB0zpNF2h2232kErkEnzH1sxMmA= -github.com/VictoriaMetrics/metrics v1.24.0 h1:ILavebReOjYctAGY5QU2F9X0MYvkcrG3aEn2RKa1Zkw= -github.com/VictoriaMetrics/metrics v1.24.0/go.mod h1:eFT25kvsTidQFHb6U0oa0rTrDRdz4xTYjpL8+UPohys= -github.com/VictoriaMetrics/metricsql v0.56.2 h1:quBAbYOlWMhmdgzFSCr1yjtVcdZYZrVQJ7nR9zor7ZM= -github.com/VictoriaMetrics/metricsql v0.56.2/go.mod h1:6pP1ZeLVJHqJrHlF6Ij3gmpQIznSsgktEcZgsAWYel0= -github.com/VictoriaMetrics/operator/api v0.0.0-20231128174956-7965dba77210 h1:lug0mgxNLNEWS5eEVqCBZoTg6ZTRdxdUK4jlV2doKF8= -github.com/VictoriaMetrics/operator/api v0.0.0-20231128174956-7965dba77210/go.mod h1:9xOZrc3kjanpgasau9iMeUM6vYIm37bdTpBRYB0nccY= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/VictoriaMetrics/VictoriaMetrics v1.101.0 h1:9nducJ+trgthdFJ8Fzshj80eotTGlt2DAEhr6pDdiXM= +github.com/VictoriaMetrics/VictoriaMetrics v1.101.0/go.mod h1:4ia9nPE84gL/qd5/YYBkXwca1mXFQg9gRRQ+qwJjvvs= +github.com/VictoriaMetrics/easyproto v0.1.4 h1:r8cNvo8o6sR4QShBXQd1bKw/VVLSQma/V2KhTBPf+Sc= +github.com/VictoriaMetrics/easyproto v0.1.4/go.mod h1:QlGlzaJnDfFd8Lk6Ci/fuLxfTo3/GThPs2KH23mv710= +github.com/VictoriaMetrics/metrics v1.33.1 h1:CNV3tfm2Kpv7Y9W3ohmvqgFWPR55tV2c7M2U6OIo+UM= +github.com/VictoriaMetrics/metrics v1.33.1/go.mod h1:r7hveu6xMdUACXvB8TYdAj8WEsKzWB0EkpJN+RDtOf8= +github.com/VictoriaMetrics/metricsql v0.75.1 h1:cE5Ex6qSdI9vVT2BnsO6GpepB/8LPoSPKQmrM+fuQ84= +github.com/VictoriaMetrics/metricsql v0.75.1/go.mod h1:bEC8gqV+7kjnp97a8Gd6JbV1TraeZhfhvYAuaDuNR/U= +github.com/VictoriaMetrics/operator/api v0.0.0-20240816142248-64879fb1dd26 h1:S9Kya85PQ8ARZ2Y0jQU2DxhvWwhB217iar0OjIMqNaE= +github.com/VictoriaMetrics/operator/api v0.0.0-20240816142248-64879fb1dd26/go.mod h1:Fi3l6VDCoZL4VDCdFnfR6SxNfgW0NGBRdda/MolNWUs= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= -github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= -github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= +github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= +github.com/aws/aws-sdk-go v1.51.23 h1:/3TEdsEE/aHmdKGw2NrOp7Sdea76zfffGkTTSXTsDxY= +github.com/aws/aws-sdk-go v1.51.23/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bmatcuk/doublestar/v4 v4.6.0 h1:HTuxyug8GyFbRkrffIpzNCSK4luc0TY3wzXvzIZhEXc= -github.com/bmatcuk/doublestar/v4 v4.6.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= +github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I= +github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/emicklei/go-restful/v3 v3.8.0 h1:eCZ8ulSerjdAiaNpF7GxXIE7ZCMo1moN1qX+S609eVw= -github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= -github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= +github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= +github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= +github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= -github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4= -github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= -github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= -github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8= -github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= -github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= -github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= +github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= +github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q= +github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs= +github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU= +github.com/go-openapi/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4= +github.com/go-openapi/swag v0.22.9 h1:XX2DssF+mQKM2DHsbgZK74y/zj4mo9I99+89xUmuZCE= +github.com/go-openapi/swag v0.22.9/go.mod h1:3/OXnFfnMAwBD099SwYRk7GD3xOrr1iL7d/XNLXVVwE= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU= -github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= @@ -92,42 +155,83 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= -github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84= +github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM= +github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= -github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= +github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -136,64 +240,84 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.1.6 h1:Fx2POJZfKRQcM1pH49qSZiYeu319wji004qX+GDovrU= -github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q= -github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA= +github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= +github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= +github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.60.1 h1:A46xpyCEQpMFymrNJOaL5aAu3ZWgEKwJUXZrB5D3IUM= github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.60.1/go.mod h1:MNl09GdaKb/vE8QdcCWyICDV7XAbGX6gKKQAS43XW1c= -github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI= -github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= +github.com/prometheus/alertmanager v0.27.0 h1:V6nTa2J5V4s8TG4C4HtrBP/WNSebCCTYGGv4qecA/+I= +github.com/prometheus/alertmanager v0.27.0/go.mod h1:8Ia/R3urPmbzJ8OsdvmZvIprDwvwmYCmUbwBL+jlPOE= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= +github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= -github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= -github.com/prometheus/common v0.43.0 h1:iq+BVjvYLei5f27wiuNiB1DN6DYQkp1c8Bx0Vykh5us= -github.com/prometheus/common v0.43.0/go.mod h1:NCvr5cQIh3Y/gy73/RdVtC9r8xxrxwJnB+2lB3BxrFc= -github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= -github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= +github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/common/sigv4 v0.1.0 h1:qoVebwtwwEhS85Czm2dSROY5fTo2PAPEVdDeppTwGX4= +github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57JrvHu9k5YwTjsNtI= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.30.0/go.mod h1:2rsYD01CKFrjjsvFxx75KlEUNpWNBY9JWD3K/7o2Cus= @@ -203,128 +327,316 @@ github.com/valyala/fastrand v1.1.0 h1:f+5HkLW4rsgzdNoleUOB69hyT9IlD2ZQh9GyDMfb5G github.com/valyala/fastrand v1.1.0/go.mod h1:HWqCzkrkg6QXT8V2EXWvXCoow7vLwOFN002oeRzjapQ= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/valyala/gozstd v1.20.1 h1:xPnnnvjmaDDitMFfDxmQ4vpx0+3CdTg2o3lALvXTU/g= +github.com/valyala/gozstd v1.20.1/go.mod h1:y5Ew47GLlP37EkTB+B4s7r6A5rdaeB7ftbl9zoYiIPQ= github.com/valyala/histogram v1.2.0 h1:wyYGAZZt3CpwUiIb9AU/Zbllg1llXyrtApRS815OLoQ= github.com/valyala/histogram v1.2.0/go.mod h1:Hb4kBwb4UxsaNbbbh+RRz8ZR6pdodR57tzWUS3BUzXY= github.com/valyala/quicktemplate v1.7.0 h1:LUPTJmlVcb46OOUY3IeD9DojFpAVbsG+5WFTcjMJzCM= github.com/valyala/quicktemplate v1.7.0/go.mod h1:sqKJnoaOF88V07vkO+9FL8fb9uZg/VPSJnLYn+LmLk8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= -go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= -go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec= -go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= -go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= -go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI= -go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= +go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= +go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= +go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= +go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= +go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= +go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= +go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= +go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= -golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY= +golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= -golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= +golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= +golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= -gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= +gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= +gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 h1:7whR9kGa5LUwFtpLm2ArCEejtnxlGeLbAyjFY8sGNFw= +google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -334,55 +646,67 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= +gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -k8s.io/api v0.25.6 h1:LwDY2H6kD/3R8TekJYYaJWOdekNdXDO44eVpX6sNtJA= -k8s.io/api v0.25.6/go.mod h1:bVp01KUcl8VUHFBTJMOknWNo7XvR0cMbeTTuFg1zCUs= -k8s.io/apiextensions-apiserver v0.25.0 h1:CJ9zlyXAbq0FIW8CD7HHyozCMBpDSiH7EdrSTCZcZFY= -k8s.io/apiextensions-apiserver v0.25.0/go.mod h1:3pAjZiN4zw7R8aZC5gR0y3/vCkGlAjCazcg1me8iB/E= -k8s.io/apimachinery v0.25.6 h1:r6KIF2AHwLqFfZ0LcOA3I11SF62YZK83dxj1fn14NOQ= -k8s.io/apimachinery v0.25.6/go.mod h1:1S2i1QHkmxc8+EZCIxe/fX5hpldVXk4gvnJInMEb8D4= -k8s.io/client-go v0.25.6 h1:CHxACHi0DijmlYyUR7ooZoXnD5P8jYLgBHcxp775x/U= -k8s.io/client-go v0.25.6/go.mod h1:s9mMAGFYiH3Z66j7BESzu0GEradT9GQ2LjFf/YRrnyc= -k8s.io/component-base v0.25.0 h1:haVKlLkPCFZhkcqB6WCvpVxftrg6+FK5x1ZuaIDaQ5Y= -k8s.io/component-base v0.25.0/go.mod h1:F2Sumv9CnbBlqrpdf7rKZTmmd2meJq0HizeyY/yAFxk= -k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.80.0 h1:lyJt0TWMPaGoODa8B8bUuxgHS3W/m/bNr2cca3brA/g= -k8s.io/klog/v2 v2.80.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 h1:MQ8BAZPZlWk3S9K4a9NCkIFQtZShWqoha7snGixVgEA= -k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU= -k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73 h1:H9TCJUUx+2VA0ZiD9lvtaX8fthFsMoD+Izn93E/hm8U= -k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -sigs.k8s.io/controller-runtime v0.12.3 h1:FCM8xeY/FI8hoAfh/V4XbbYMY20gElh9yh+A98usMio= -sigs.k8s.io/controller-runtime v0.12.3/go.mod h1:qKsk4WE6zW2Hfj0G4v10EnNB2jMG1C+NTb8h+DwCoU0= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/api v0.31.0 h1:b9LiSjR2ym/SzTOlfMHm1tr7/21aD7fSkqgD/CVJBCo= +k8s.io/api v0.31.0/go.mod h1:0YiFF+JfFxMM6+1hQei8FY8M7s1Mth+z/q7eF1aJkTE= +k8s.io/apiextensions-apiserver v0.31.0 h1:fZgCVhGwsclj3qCw1buVXCV6khjRzKC5eCFt24kyLSk= +k8s.io/apiextensions-apiserver v0.31.0/go.mod h1:b9aMDEYaEe5sdK+1T0KU78ApR/5ZVp4i56VacZYEHxk= +k8s.io/apimachinery v0.31.0 h1:m9jOiSr3FoSSL5WO9bjm1n6B9KROYYgNZOb4tyZ1lBc= +k8s.io/apimachinery v0.31.0/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/apiserver v0.31.0 h1:p+2dgJjy+bk+B1Csz+mc2wl5gHwvNkC9QJV+w55LVrY= +k8s.io/apiserver v0.31.0/go.mod h1:KI9ox5Yu902iBnnyMmy7ajonhKnkeZYJhTZ/YI+WEMk= +k8s.io/client-go v0.31.0 h1:QqEJzNjbN2Yv1H79SsS+SWnXkBgVu4Pj3CJQgbx0gI8= +k8s.io/client-go v0.31.0/go.mod h1:Y9wvC76g4fLjmU0BA+rV+h2cncoadjvjjkkIGoTLcGU= +k8s.io/component-base v0.31.0 h1:/KIzGM5EvPNQcYgwq5NwoQBaOlVFrghoVGr8lG6vNRs= +k8s.io/component-base v0.31.0/go.mod h1:TYVuzI1QmN4L5ItVdMSXKvH7/DtvIuas5/mm8YT3rTo= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 h1:2770sDpzrjjsAtVhSeUFseziht227YAWYHLGNM8QPwY= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= +sigs.k8s.io/controller-runtime v0.19.0 h1:nWVM7aq+Il2ABxwiCizrVDSlmDcshi9llbaFbC0ji/Q= +sigs.k8s.io/controller-runtime v0.19.0/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/hack/boilerplate.go.txt b/hack/boilerplate.go.txt index 29c55ecd..ff72ff2a 100644 --- a/hack/boilerplate.go.txt +++ b/hack/boilerplate.go.txt @@ -1,5 +1,5 @@ /* -Copyright 2022. +Copyright 2024. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/hack/simple-test.sh b/hack/simple-test.sh index bdfacf94..96a243d8 100755 --- a/hack/simple-test.sh +++ b/hack/simple-test.sh @@ -145,8 +145,8 @@ spec: - transform-test EOF -echo `date`": INFO: Wait 1m second" -sleep 60 +echo `date`": INFO: Wait 80 seconds" +sleep 80 echo `date`": INFO: Check logs" diff --git a/pkg/config/config.go b/internal/config/config.go similarity index 96% rename from pkg/config/config.go rename to internal/config/config.go index f6c00fa9..25d04aa8 100644 --- a/pkg/config/config.go +++ b/internal/config/config.go @@ -23,9 +23,9 @@ import ( "strconv" vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" - "github.com/kaasops/vector-operator/pkg/pipeline" - "github.com/kaasops/vector-operator/pkg/utils/k8s" - "github.com/kaasops/vector-operator/pkg/vector/vectoragent" + "github.com/kaasops/vector-operator/internal/pipeline" + "github.com/kaasops/vector-operator/internal/utils/k8s" + "github.com/kaasops/vector-operator/internal/vector/vectoragent" "github.com/mitchellh/mapstructure" "gopkg.in/yaml.v2" "k8s.io/apimachinery/pkg/labels" diff --git a/pkg/config/configcheck/configcheck.go b/internal/config/configcheck/configcheck.go similarity index 99% rename from pkg/config/configcheck/configcheck.go rename to internal/config/configcheck/configcheck.go index 412becfc..6b1f746d 100644 --- a/pkg/config/configcheck/configcheck.go +++ b/internal/config/configcheck/configcheck.go @@ -22,7 +22,7 @@ import ( "math/rand" "time" - "github.com/kaasops/vector-operator/pkg/utils/k8s" + "github.com/kaasops/vector-operator/internal/utils/k8s" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" diff --git a/pkg/config/configcheck/configcheck_config.go b/internal/config/configcheck/configcheck_config.go similarity index 95% rename from pkg/config/configcheck/configcheck_config.go rename to internal/config/configcheck/configcheck_config.go index a36f4990..a8f37a5c 100644 --- a/pkg/config/configcheck/configcheck_config.go +++ b/internal/config/configcheck/configcheck_config.go @@ -19,7 +19,7 @@ package configcheck import ( "context" - "github.com/kaasops/vector-operator/pkg/utils/compression" + "github.com/kaasops/vector-operator/internal/utils/compression" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/log" diff --git a/pkg/config/configcheck/configcheck_error.go b/internal/config/configcheck/configcheck_error.go similarity index 100% rename from pkg/config/configcheck/configcheck_error.go rename to internal/config/configcheck/configcheck_error.go diff --git a/pkg/config/configcheck/configcheck_pod.go b/internal/config/configcheck/configcheck_pod.go similarity index 100% rename from pkg/config/configcheck/configcheck_pod.go rename to internal/config/configcheck/configcheck_pod.go diff --git a/pkg/config/configcheck/configcheck_rbac.go b/internal/config/configcheck/configcheck_rbac.go similarity index 100% rename from pkg/config/configcheck/configcheck_rbac.go rename to internal/config/configcheck/configcheck_rbac.go diff --git a/pkg/config/configcheck/const.go b/internal/config/configcheck/const.go similarity index 100% rename from pkg/config/configcheck/const.go rename to internal/config/configcheck/const.go diff --git a/pkg/config/default.go b/internal/config/default.go similarity index 100% rename from pkg/config/default.go rename to internal/config/default.go diff --git a/pkg/config/types.go b/internal/config/types.go similarity index 100% rename from pkg/config/types.go rename to internal/config/types.go diff --git a/pkg/config/utils.go b/internal/config/utils.go similarity index 100% rename from pkg/config/utils.go rename to internal/config/utils.go diff --git a/controllers/suite_test.go b/internal/controller/suite_test.go similarity index 54% rename from controllers/suite_test.go rename to internal/controller/suite_test.go index b11b4437..59cff298 100644 --- a/controllers/suite_test.go +++ b/internal/controller/suite_test.go @@ -1,5 +1,5 @@ /* -Copyright 2022. +Copyright 2024. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,25 +14,31 @@ See the License for the specific language governing permissions and limitations under the License. */ -package controllers +package controller import ( + "context" + "fmt" + "k8s.io/client-go/kubernetes" + "k8s.io/utils/pointer" "path/filepath" + "runtime" + "sync" "testing" + "time" - . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/envtest" - "sigs.k8s.io/controller-runtime/pkg/envtest/printer" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" observabilityv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" - //+kubebuilder:scaffold:imports + // +kubebuilder:scaffold:imports ) // These tests use Ginkgo (BDD-style Go testing framework). Refer to @@ -41,22 +47,38 @@ import ( var cfg *rest.Config var k8sClient client.Client var testEnv *envtest.Environment - -func TestAPIs(t *testing.T) { +var ctx context.Context +var cancel context.CancelFunc +var clientset *kubernetes.Clientset +var wg *sync.WaitGroup +var pipelineCheckTimeout time.Duration +var pipelineDeleteEventTimeout time.Duration +var configCheckTimeout time.Duration + +func TestControllers(t *testing.T) { RegisterFailHandler(Fail) - RunSpecsWithDefaultAndCustomReporters(t, - "Controller Suite", - []Reporter{printer.NewlineReporter{}}) + RunSpecs(t, "Controller Suite") } var _ = BeforeSuite(func() { logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + ctx, cancel = context.WithCancel(context.TODO()) + By("bootstrapping test environment") testEnv = &envtest.Environment{ - CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")}, + UseExistingCluster: pointer.Bool(true), + CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, ErrorIfCRDPathMissing: true, + + // The BinaryAssetsDirectory is only required if you want to run the tests directly + // without call the makefile target test. If not informed it will look for the + // default path defined in controller-runtime which is /usr/local/kubebuilder/. + // Note that you must have the required binaries setup under the bin directory to perform + // the tests directly. When we run make test it will be setup and used automatically. + BinaryAssetsDirectory: filepath.Join("..", "..", "bin", "k8s", + fmt.Sprintf("1.31.0-%s-%s", runtime.GOOS, runtime.GOARCH)), } var err error @@ -68,16 +90,25 @@ var _ = BeforeSuite(func() { err = observabilityv1alpha1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) - //+kubebuilder:scaffold:scheme + // +kubebuilder:scaffold:scheme k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) Expect(err).NotTo(HaveOccurred()) Expect(k8sClient).NotTo(BeNil()) -}, 60) + wg = &sync.WaitGroup{} + pipelineCheckTimeout = time.Second * 15 + configCheckTimeout = time.Second * 60 + pipelineDeleteEventTimeout = time.Second * 3 + + clientset, err = kubernetes.NewForConfig(cfg) + Expect(err).NotTo(HaveOccurred()) + Expect(clientset).NotTo(BeNil()) +}) var _ = AfterSuite(func() { By("tearing down the test environment") + cancel() err := testEnv.Stop() Expect(err).NotTo(HaveOccurred()) }) diff --git a/controllers/vector_controller.go b/internal/controller/vector_controller.go similarity index 88% rename from controllers/vector_controller.go rename to internal/controller/vector_controller.go index 719cf39f..a99af9f3 100644 --- a/controllers/vector_controller.go +++ b/internal/controller/vector_controller.go @@ -1,5 +1,5 @@ /* -Copyright 2022. +Copyright 2024. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package controllers +package controller import ( "context" @@ -22,12 +22,12 @@ import ( "sync" "time" - "github.com/kaasops/vector-operator/pkg/config" - "github.com/kaasops/vector-operator/pkg/config/configcheck" - "github.com/kaasops/vector-operator/pkg/pipeline" - "github.com/kaasops/vector-operator/pkg/utils/hash" - "github.com/kaasops/vector-operator/pkg/utils/k8s" - "github.com/kaasops/vector-operator/pkg/vector/vectoragent" + "github.com/kaasops/vector-operator/internal/config" + "github.com/kaasops/vector-operator/internal/config/configcheck" + "github.com/kaasops/vector-operator/internal/pipeline" + "github.com/kaasops/vector-operator/internal/utils/hash" + "github.com/kaasops/vector-operator/internal/utils/k8s" + "github.com/kaasops/vector-operator/internal/vector/vectoragent" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -68,14 +68,18 @@ type VectorReconciler struct { //+kubebuilder:rbac:groups=observability.kaasops.io,resources=vectors/finalizers,verbs=update // +kubebuilder:rbac:groups="",resources=serviceaccounts,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups="",resources=sercrets,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups="",resources=services,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups="",resources=pods,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups="",resources=pods/log,verbs=get;list +// +kubebuilder:rbac:groups="",resources=namespaces,verbs=list;watch +// +kubebuilder:rbac:groups="",resources=nodes,verbs=list;watch +// +kubebuilder:rbac:groups="",resources=events,verbs=list;watch // +kubebuilder:rbac:groups=apps,resources=daemonsets,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups="rbac.authorization.k8s.io",resources=clusterrolebindings,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups="rbac.authorization.k8s.io",resources=clusterroles,verbs=get;list;watch;create;update;patch;delete func (r *VectorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - _ = log.FromContext(ctx) log := log.FromContext(ctx).WithValues("Vector", req.NamespacedName) log.Info("Waiting pipeline checks") if waitPipelineChecks(r.PipelineCheckWG, r.PipelineCheckTimeout) { @@ -112,7 +116,7 @@ func (r *VectorReconciler) SetupWithManager(mgr ctrl.Manager) error { } builder := ctrl.NewControllerManagedBy(mgr). For(&vectorv1alpha1.Vector{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})). - Watches(&source.Channel{Source: VectorAgentReconciliationSourceChannel}, &handler.EnqueueRequestForObject{}). + WatchesRawSource(source.Channel(VectorAgentReconciliationSourceChannel, &handler.EnqueueRequestForObject{})). Owns(&appsv1.DaemonSet{}). Owns(&corev1.Service{}). Owns(&corev1.Secret{}). @@ -172,7 +176,6 @@ func (r *VectorReconciler) reconcileVectors(ctx context.Context, client client.C } func (r *VectorReconciler) createOrUpdateVector(ctx context.Context, client client.Client, clientset *kubernetes.Clientset, v *vectorv1alpha1.Vector, configOnly bool) (ctrl.Result, error) { - _ = log.FromContext(ctx) log := log.FromContext(ctx).WithValues("Vector", v.Name) // Init Controller for Vector Agent vaCtrl := vectoragent.NewController(v, client, clientset) @@ -231,7 +234,7 @@ func (r *VectorReconciler) createOrUpdateVector(ctx context.Context, client clie return ctrl.Result{}, err } - if err := vaCtrl.SetSucceesStatus(ctx); err != nil { + if err := vaCtrl.SetSuccessStatus(ctx); err != nil { // TODO: Handle err: Operation cannot be fulfilled on vectors.observability.kaasops.io \"vector-sample\": the object has been modified; please apply your changes to the latest version and try again if api_errors.IsConflict(err) { return ctrl.Result{}, err diff --git a/internal/controller/vector_controller_test.go b/internal/controller/vector_controller_test.go new file mode 100644 index 00000000..5102288b --- /dev/null +++ b/internal/controller/vector_controller_test.go @@ -0,0 +1,88 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "context" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + observabilityv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" +) + +var _ = Describe("Vector Controller", func() { + Context("When reconciling a resource", func() { + const resourceName = "test-resource" + + ctx := context.Background() + + typeNamespacedName := types.NamespacedName{ + Name: resourceName, + Namespace: "default", // TODO(user):Modify as needed + } + vector := &observabilityv1alpha1.Vector{} + + BeforeEach(func() { + By("creating the custom resource for the Kind Vector") + err := k8sClient.Get(ctx, typeNamespacedName, vector) + if err != nil && errors.IsNotFound(err) { + resource := &observabilityv1alpha1.Vector{ + ObjectMeta: metav1.ObjectMeta{ + Name: resourceName, + Namespace: "default", + }, + // TODO(user): Specify other spec details if needed. + } + Expect(k8sClient.Create(ctx, resource)).To(Succeed()) + } + }) + + AfterEach(func() { + // TODO(user): Cleanup logic after each test, like removing the resource instance. + resource := &observabilityv1alpha1.Vector{} + err := k8sClient.Get(ctx, typeNamespacedName, resource) + Expect(err).NotTo(HaveOccurred()) + + By("Cleanup the specific resource instance Vector") + Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) + }) + It("should successfully reconcile the resource", func() { + By("Reconciling the created resource") + controllerReconciler := &VectorReconciler{ + Client: k8sClient, + Scheme: k8sClient.Scheme(), + Clientset: clientset, + PipelineCheckWG: wg, + PipelineCheckTimeout: pipelineCheckTimeout, + ConfigCheckTimeout: configCheckTimeout, + DiscoveryClient: clientset.DiscoveryClient, + } + + _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ + NamespacedName: typeNamespacedName, + }) + Expect(err).NotTo(HaveOccurred()) + // TODO(user): Add more specific assertions depending on your controller's reconciliation logic. + // Example: If you expect a certain status condition after reconciliation, verify it here. + }) + }) +}) diff --git a/controllers/pipeline_controller.go b/internal/controller/vectorpipeline_controller.go similarity index 93% rename from controllers/pipeline_controller.go rename to internal/controller/vectorpipeline_controller.go index 93647b4f..8eda3ba8 100644 --- a/controllers/pipeline_controller.go +++ b/internal/controller/vectorpipeline_controller.go @@ -1,5 +1,5 @@ /* -Copyright 2022. +Copyright 2024. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,13 +14,18 @@ See the License for the specific language governing permissions and limitations under the License. */ -package controllers +package controller import ( "context" "sync" "time" + vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" + "github.com/kaasops/vector-operator/internal/config" + "github.com/kaasops/vector-operator/internal/config/configcheck" + "github.com/kaasops/vector-operator/internal/pipeline" + "github.com/kaasops/vector-operator/internal/vector/vectoragent" api_errors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes" @@ -30,13 +35,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/predicate" - "sigs.k8s.io/controller-runtime/pkg/source" - - vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" - "github.com/kaasops/vector-operator/pkg/config" - "github.com/kaasops/vector-operator/pkg/config/configcheck" - "github.com/kaasops/vector-operator/pkg/pipeline" - "github.com/kaasops/vector-operator/pkg/vector/vectoragent" ) type PipelineReconciler struct { @@ -160,7 +158,7 @@ func (r *PipelineReconciler) findPipelineCustomResourceInstance(ctx context.Cont func (r *PipelineReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&vectorv1alpha1.VectorPipeline{}). - Watches(&source.Kind{Type: &vectorv1alpha1.ClusterVectorPipeline{}}, &handler.EnqueueRequestForObject{}). + Watches(&vectorv1alpha1.ClusterVectorPipeline{}, &handler.EnqueueRequestForObject{}). WithEventFilter(predicate.GenerationChangedPredicate{}). Complete(r) } diff --git a/internal/controller/vectorpipeline_controller_test.go b/internal/controller/vectorpipeline_controller_test.go new file mode 100644 index 00000000..8e51ed6f --- /dev/null +++ b/internal/controller/vectorpipeline_controller_test.go @@ -0,0 +1,88 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + observabilityv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" +) + +var _ = Describe("VectorPipeline Controller", func() { + Context("When reconciling a resource", func() { + const resourceName = "test-resource" + + ctx := context.Background() + + typeNamespacedName := types.NamespacedName{ + Name: resourceName, + Namespace: "default", // TODO(user):Modify as needed + } + vectorpipeline := &observabilityv1alpha1.VectorPipeline{} + + BeforeEach(func() { + By("creating the custom resource for the Kind VectorPipeline") + err := k8sClient.Get(ctx, typeNamespacedName, vectorpipeline) + if err != nil && errors.IsNotFound(err) { + resource := &observabilityv1alpha1.VectorPipeline{ + ObjectMeta: metav1.ObjectMeta{ + Name: resourceName, + Namespace: "default", + }, + // TODO(user): Specify other spec details if needed. + } + Expect(k8sClient.Create(ctx, resource)).To(Succeed()) + } + }) + + AfterEach(func() { + // TODO(user): Cleanup logic after each test, like removing the resource instance. + resource := &observabilityv1alpha1.VectorPipeline{} + err := k8sClient.Get(ctx, typeNamespacedName, resource) + Expect(err).NotTo(HaveOccurred()) + + By("Cleanup the specific resource instance VectorPipeline") + Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) + }) + It("should successfully reconcile the resource", func() { + By("Reconciling the created resource") + controllerReconciler := &PipelineReconciler{ + Client: k8sClient, + Scheme: k8sClient.Scheme(), + Clientset: clientset, + PipelineCheckWG: wg, + PipelineDeleteEventTimeout: pipelineDeleteEventTimeout, + ConfigCheckTimeout: configCheckTimeout, + } + + _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ + NamespacedName: typeNamespacedName, + }) + Expect(err).NotTo(HaveOccurred()) + // TODO(user): Add more specific assertions depending on your controller's reconciliation logic. + // Example: If you expect a certain status condition after reconciliation, verify it here. + }) + }) +}) diff --git a/pkg/pipeline/hash.go b/internal/pipeline/hash.go similarity index 95% rename from pkg/pipeline/hash.go rename to internal/pipeline/hash.go index da31df8e..6a4415ef 100644 --- a/pkg/pipeline/hash.go +++ b/internal/pipeline/hash.go @@ -19,7 +19,7 @@ package pipeline import ( "encoding/json" - "github.com/kaasops/vector-operator/pkg/utils/hash" + "github.com/kaasops/vector-operator/internal/utils/hash" ) func GetSpecHash(pipeline Pipeline) (*uint32, error) { diff --git a/pkg/pipeline/pipeline.go b/internal/pipeline/pipeline.go similarity index 100% rename from pkg/pipeline/pipeline.go rename to internal/pipeline/pipeline.go diff --git a/pkg/utils/compression/compression.go b/internal/utils/compression/compression.go similarity index 100% rename from pkg/utils/compression/compression.go rename to internal/utils/compression/compression.go diff --git a/pkg/utils/hash/hash.go b/internal/utils/hash/hash.go similarity index 100% rename from pkg/utils/hash/hash.go rename to internal/utils/hash/hash.go diff --git a/pkg/utils/hash/hash_test.go b/internal/utils/hash/hash_test.go similarity index 95% rename from pkg/utils/hash/hash_test.go rename to internal/utils/hash/hash_test.go index 0300419e..46ce0239 100644 --- a/pkg/utils/hash/hash_test.go +++ b/internal/utils/hash/hash_test.go @@ -19,7 +19,7 @@ package hash_test import ( "testing" - "github.com/kaasops/vector-operator/pkg/utils/hash" + "github.com/kaasops/vector-operator/internal/utils/hash" "github.com/stretchr/testify/require" ) diff --git a/pkg/utils/k8s/k8s.go b/internal/utils/k8s/k8s.go similarity index 99% rename from pkg/utils/k8s/k8s.go rename to internal/utils/k8s/k8s.go index abb68b7f..8eac9893 100644 --- a/pkg/utils/k8s/k8s.go +++ b/internal/utils/k8s/k8s.go @@ -23,7 +23,7 @@ import ( "fmt" "io" - victoriametricsv1beta1 "github.com/VictoriaMetrics/operator/api/v1beta1" + victoriametricsv1beta1 "github.com/VictoriaMetrics/operator/api/operator/v1beta1" monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -145,7 +145,7 @@ func createOrUpdateService(ctx context.Context, desired *corev1.Service, c clien return nil }) if err != nil { - return fmt.Errorf("failed to create or update Deployment: %w", err) + return fmt.Errorf("failed to create or update Service: %w", err) } existing.DeepCopyInto(desired) return nil diff --git a/pkg/utils/k8s/k8s_test.go b/internal/utils/k8s/k8s_test.go similarity index 91% rename from pkg/utils/k8s/k8s_test.go rename to internal/utils/k8s/k8s_test.go index eb67d392..2c5fd442 100644 --- a/pkg/utils/k8s/k8s_test.go +++ b/internal/utils/k8s/k8s_test.go @@ -18,12 +18,13 @@ package k8s_test import ( "context" + "fmt" "testing" // . "github.com/onsi/ginkgo/v2" // . "github.com/onsi/gomega" - "github.com/kaasops/vector-operator/pkg/utils/k8s" + "github.com/kaasops/vector-operator/internal/utils/k8s" "github.com/stretchr/testify/require" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -44,7 +45,7 @@ type objCase struct { want error } -var nameRequeriedError = api_errors.NewInvalid( +var nameRequiredError = api_errors.NewInvalid( schema.GroupKind{}, "", field.ErrorList{ @@ -65,15 +66,22 @@ func getInitObjectMeta() metav1.ObjectMeta { } func TestCreateOrUpdateResource(t *testing.T) { - createOrUpdateResourceCase := func(initObj, obj client.Object, want error) func(t *testing.T) { + createOrUpdateResourceCase := func(initObj, obj client.Object, expected error) func(t *testing.T) { return func(t *testing.T) { t.Helper() t.Parallel() req := require.New(t) cl := fake.NewClientBuilder().WithObjects(initObj).Build() - result := k8s.CreateOrUpdateResource(context.Background(), obj, cl) - req.Equal(result, want) + actualErr := k8s.CreateOrUpdateResource(context.Background(), obj, cl) + switch { + case expected == nil && actualErr != nil: + t.Errorf("unexpected error: '%v'", actualErr) + case expected != nil && actualErr == nil: + t.Errorf("expected error: '%v', but actual nil", actualErr) + case expected != nil && actualErr != nil: + req.EqualError(actualErr, expected.Error()) + } } } @@ -95,7 +103,7 @@ func TestCreateOrUpdateResource(t *testing.T) { want: nil, }, { - name: "Create Alredy exist case", + name: "Create Already exist case", initObj: &appsv1.Deployment{ ObjectMeta: getInitObjectMeta(), }, @@ -125,7 +133,7 @@ func TestCreateOrUpdateResource(t *testing.T) { obj: &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{}, }, - want: nameRequeriedError, + want: fmt.Errorf("failed to create or update Deployment: %w", nameRequiredError), }, { name: "Update exist case", @@ -159,7 +167,7 @@ func TestCreateOrUpdateResource(t *testing.T) { want: nil, }, { - name: "Create Alredy exist case", + name: "Create Already exist case", initObj: &appsv1.StatefulSet{ ObjectMeta: getInitObjectMeta(), }, @@ -189,7 +197,7 @@ func TestCreateOrUpdateResource(t *testing.T) { obj: &appsv1.StatefulSet{ ObjectMeta: metav1.ObjectMeta{}, }, - want: nameRequeriedError, + want: fmt.Errorf("failed to create or update StatefulSet: %w", nameRequiredError), }, { name: "Update exist case", @@ -223,7 +231,7 @@ func TestCreateOrUpdateResource(t *testing.T) { want: nil, }, { - name: "Create Alredy exist case", + name: "Create Already exist case", initObj: &appsv1.DaemonSet{ ObjectMeta: getInitObjectMeta(), }, @@ -253,7 +261,7 @@ func TestCreateOrUpdateResource(t *testing.T) { obj: &appsv1.DaemonSet{ ObjectMeta: metav1.ObjectMeta{}, }, - want: nameRequeriedError, + want: fmt.Errorf("failed to create or update Daemonset: %w", nameRequiredError), }, { name: "Update exist case", @@ -287,7 +295,7 @@ func TestCreateOrUpdateResource(t *testing.T) { want: nil, }, { - name: "Create Alredy exist case", + name: "Create Already exist case", initObj: &corev1.Secret{ ObjectMeta: getInitObjectMeta(), }, @@ -317,7 +325,7 @@ func TestCreateOrUpdateResource(t *testing.T) { obj: &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{}, }, - want: nameRequeriedError, + want: fmt.Errorf("failed to create or update Secret: %w", nameRequiredError), }, { name: "Update exist case", @@ -351,7 +359,7 @@ func TestCreateOrUpdateResource(t *testing.T) { want: nil, }, { - name: "Create Alredy exist case", + name: "Create Already exist case", initObj: &corev1.Service{ ObjectMeta: getInitObjectMeta(), }, @@ -381,7 +389,7 @@ func TestCreateOrUpdateResource(t *testing.T) { obj: &corev1.Service{ ObjectMeta: metav1.ObjectMeta{}, }, - want: nameRequeriedError, + want: fmt.Errorf("failed to create or update Service: %w", nameRequiredError), }, { name: "Update exist case", @@ -415,7 +423,7 @@ func TestCreateOrUpdateResource(t *testing.T) { want: nil, }, { - name: "Create Alredy exist case", + name: "Create Already exist case", initObj: &corev1.ServiceAccount{ ObjectMeta: getInitObjectMeta(), }, @@ -445,7 +453,7 @@ func TestCreateOrUpdateResource(t *testing.T) { obj: &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{}, }, - want: nameRequeriedError, + want: fmt.Errorf("failed to create or update ServiceAccount: %w", nameRequiredError), }, { name: "Update exist case", @@ -482,7 +490,7 @@ func TestCreateOrUpdateResource(t *testing.T) { want: nil, }, { - name: "Create Alredy exist case", + name: "Create Already exist case", initObj: &rbacv1.ClusterRole{ ObjectMeta: getInitObjectMeta(), }, @@ -512,7 +520,7 @@ func TestCreateOrUpdateResource(t *testing.T) { obj: &rbacv1.ClusterRole{ ObjectMeta: metav1.ObjectMeta{}, }, - want: nameRequeriedError, + want: fmt.Errorf("failed to create or update ClusterRole: %w", nameRequiredError), }, { name: "Update exist case", @@ -549,7 +557,7 @@ func TestCreateOrUpdateResource(t *testing.T) { want: nil, }, { - name: "Create Alredy exist case", + name: "Create Already exist case", initObj: &rbacv1.ClusterRoleBinding{ ObjectMeta: getInitObjectMeta(), }, @@ -579,7 +587,7 @@ func TestCreateOrUpdateResource(t *testing.T) { obj: &rbacv1.ClusterRoleBinding{ ObjectMeta: metav1.ObjectMeta{}, }, - want: nameRequeriedError, + want: fmt.Errorf("failed to create or update ClusterRoleBinding: %w", nameRequiredError), }, { name: "Update exist case", @@ -601,7 +609,7 @@ func TestCreateOrUpdateResource(t *testing.T) { cases = append(cases, clusterRoleBindingCases...) // Not supported type case - notSuppurtedcase := []objCase{ + notSupportedCase := []objCase{ { name: "Update exist case", initObj: &rbacv1.RoleBinding{ @@ -628,7 +636,7 @@ func TestCreateOrUpdateResource(t *testing.T) { ), }, } - cases = append(cases, notSuppurtedcase...) + cases = append(cases, notSupportedCase...) for _, tc := range cases { t.Run(tc.name, createOrUpdateResourceCase(tc.initObj, tc.obj, tc.want)) @@ -775,7 +783,7 @@ func TestDeletePod(t *testing.T) { var cases []objCase - // DeletePodcases + // DeletePodCases deletePodCases := []objCase{ { name: "Delete Simple case", @@ -899,7 +907,7 @@ func TestDeleteSecret(t *testing.T) { var cases []objCase - // DeletePodcases + // DeletePodCases deleteSecretCases := []objCase{ { name: "Delete Simple case", @@ -939,15 +947,22 @@ func TestDeleteSecret(t *testing.T) { } func TestUpdateStatus(t *testing.T) { - updateStatusCase := func(objInit, obj client.Object, want error) func(t *testing.T) { + updateStatusCase := func(objInit, obj client.Object, expected error) func(t *testing.T) { return func(t *testing.T) { t.Helper() t.Parallel() req := require.New(t) cl := fake.NewClientBuilder().WithObjects(objInit).Build() - err := k8s.UpdateStatus(context.Background(), obj, cl) - req.Equal(err, want) + actualErr := k8s.UpdateStatus(context.Background(), obj, cl) + switch { + case expected == nil && actualErr != nil: + t.Errorf("unexpected error: '%v'", actualErr) + case expected != nil && actualErr == nil: + t.Errorf("expected error: '%v', but actual nil", actualErr) + case expected != nil && actualErr != nil: + req.EqualError(actualErr, expected.Error()) + } } } @@ -970,7 +985,7 @@ func TestUpdateStatus(t *testing.T) { err: nil, }, { - name: "Update Alredy exist case", + name: "Update Already exist case", initObj: &appsv1.Deployment{ ObjectMeta: getInitObjectMeta(), }, @@ -987,7 +1002,7 @@ func TestUpdateStatus(t *testing.T) { updateObj: &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{}, }, - err: nameRequeriedError, + err: nameRequiredError, }, { name: "Update status case", diff --git a/pkg/utils/k8s/label.go b/internal/utils/k8s/label.go similarity index 100% rename from pkg/utils/k8s/label.go rename to internal/utils/k8s/label.go diff --git a/pkg/vector/vectoragent/vectoragent.go b/internal/vector/vectoragent/vectoragent.go similarity index 93% rename from pkg/vector/vectoragent/vectoragent.go rename to internal/vector/vectoragent/vectoragent.go index 36c57433..4816d880 100644 --- a/pkg/vector/vectoragent/vectoragent.go +++ b/internal/vector/vectoragent/vectoragent.go @@ -20,7 +20,7 @@ import ( "context" vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" - "github.com/kaasops/vector-operator/pkg/utils/k8s" + "github.com/kaasops/vector-operator/internal/utils/k8s" "k8s.io/client-go/kubernetes" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -42,7 +42,7 @@ func NewController(v *vectorv1alpha1.Vector, c client.Client, cs *kubernetes.Cli } } -func (ctrl *Controller) SetSucceesStatus(ctx context.Context) error { +func (ctrl *Controller) SetSuccessStatus(ctx context.Context) error { var status = true ctrl.Vector.Status.ConfigCheckResult = &status ctrl.Vector.Status.Reason = nil diff --git a/pkg/vector/vectoragent/vectoragent_config.go b/internal/vector/vectoragent/vectoragent_config.go similarity index 95% rename from pkg/vector/vectoragent/vectoragent_config.go rename to internal/vector/vectoragent/vectoragent_config.go index faee97b6..66313e83 100644 --- a/pkg/vector/vectoragent/vectoragent_config.go +++ b/internal/vector/vectoragent/vectoragent_config.go @@ -19,7 +19,7 @@ package vectoragent import ( "context" - "github.com/kaasops/vector-operator/pkg/utils/compression" + "github.com/kaasops/vector-operator/internal/utils/compression" corev1 "k8s.io/api/core/v1" "sigs.k8s.io/controller-runtime/pkg/log" ) diff --git a/pkg/vector/vectoragent/vectoragent_controller.go b/internal/vector/vectoragent/vectoragent_controller.go similarity index 97% rename from pkg/vector/vectoragent/vectoragent_controller.go rename to internal/vector/vectoragent/vectoragent_controller.go index ca79e368..6d77623f 100644 --- a/pkg/vector/vectoragent/vectoragent_controller.go +++ b/internal/vector/vectoragent/vectoragent_controller.go @@ -18,11 +18,11 @@ package vectoragent import ( "context" + "k8s.io/utils/ptr" - "github.com/kaasops/vector-operator/pkg/utils/k8s" + "github.com/kaasops/vector-operator/internal/utils/k8s" monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/utils/pointer" "sigs.k8s.io/controller-runtime/pkg/log" ) @@ -176,8 +176,8 @@ func (ctrl *Controller) getControllerReference() []metav1.OwnerReference { Kind: ctrl.Vector.Kind, Name: ctrl.Vector.GetName(), UID: ctrl.Vector.GetUID(), - BlockOwnerDeletion: pointer.BoolPtr(true), - Controller: pointer.BoolPtr(true), + BlockOwnerDeletion: ptr.To(true), + Controller: ptr.To(true), }, } } diff --git a/pkg/vector/vectoragent/vectoragent_daemonset.go b/internal/vector/vectoragent/vectoragent_daemonset.go similarity index 100% rename from pkg/vector/vectoragent/vectoragent_daemonset.go rename to internal/vector/vectoragent/vectoragent_daemonset.go diff --git a/pkg/vector/vectoragent/vectoragent_default.go b/internal/vector/vectoragent/vectoragent_default.go similarity index 100% rename from pkg/vector/vectoragent/vectoragent_default.go rename to internal/vector/vectoragent/vectoragent_default.go diff --git a/pkg/vector/vectoragent/vectoragent_podmonitor.go b/internal/vector/vectoragent/vectoragent_podmonitor.go similarity index 100% rename from pkg/vector/vectoragent/vectoragent_podmonitor.go rename to internal/vector/vectoragent/vectoragent_podmonitor.go diff --git a/pkg/vector/vectoragent/vectoragent_rbac.go b/internal/vector/vectoragent/vectoragent_rbac.go similarity index 100% rename from pkg/vector/vectoragent/vectoragent_rbac.go rename to internal/vector/vectoragent/vectoragent_rbac.go diff --git a/pkg/vector/vectoragent/vectoragent_service.go b/internal/vector/vectoragent/vectoragent_service.go similarity index 100% rename from pkg/vector/vectoragent/vectoragent_service.go rename to internal/vector/vectoragent/vectoragent_service.go diff --git a/main.go b/main.go deleted file mode 100644 index 27f07b7e..00000000 --- a/main.go +++ /dev/null @@ -1,209 +0,0 @@ -/* -Copyright 2022. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "flag" - "os" - "sync" - "time" - - // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) - // to ensure that exec-entrypoint and run can make use of them. - - "k8s.io/client-go/discovery" - "k8s.io/client-go/kubernetes" - _ "k8s.io/client-go/plugin/pkg/client/auth" - - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/runtime" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - clientgoscheme "k8s.io/client-go/kubernetes/scheme" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/cache" - "sigs.k8s.io/controller-runtime/pkg/healthz" - "sigs.k8s.io/controller-runtime/pkg/log/zap" - - observabilityv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" - "github.com/kaasops/vector-operator/controllers" - "github.com/kaasops/vector-operator/pkg/utils/k8s" - monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" - //+kubebuilder:scaffold:imports -) - -var ( - scheme = runtime.NewScheme() - setupLog = ctrl.Log.WithName("setup") -) - -func init() { - utilruntime.Must(clientgoscheme.AddToScheme(scheme)) - - utilruntime.Must(observabilityv1alpha1.AddToScheme(scheme)) - utilruntime.Must(monitorv1.AddToScheme(scheme)) - //+kubebuilder:scaffold:scheme -} - -func main() { - var metricsAddr string - var enableLeaderElection bool - var probeAddr string - var namespace string - var watchLabel string - var pipelineCheckWG sync.WaitGroup - var PipelineCheckTimeout time.Duration - var PipelineDeleteEventTimeout time.Duration - var ConfigCheckTimeout time.Duration - - flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.") - flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") - flag.BoolVar(&enableLeaderElection, "leader-elect", false, - "Enable leader election for controller manager. "+ - "Enabling this will ensure there is only one active controller manager.") - flag.StringVar(&namespace, "watch-namespace", "", "Namespace to filter the list of watched objects") - flag.StringVar(&watchLabel, "watch-name", "", "Filter the list of watched objects by checking the app.kubernetes.io/managed-by label") - flag.DurationVar(&PipelineCheckTimeout, "pipeline-check-timeout", 15*time.Second, "wait pipeline checks before force vector reconcile. Default: 15s") - flag.DurationVar(&PipelineDeleteEventTimeout, "pipeline-delete-timeout", 5*time.Second, "collect delete events timeout") - flag.DurationVar(&ConfigCheckTimeout, "configcheck-timeout", 300*time.Second, "configcheck timeout") - opts := zap.Options{ - Development: true, - } - opts.BindFlags(flag.CommandLine) - flag.Parse() - - ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) - - config := ctrl.GetConfigOrDie() - - clientset, err := kubernetes.NewForConfig(config) - if err != nil { - setupLog.Error(err, "unable to create clientset") - os.Exit(1) - } - - dc, err := discovery.NewDiscoveryClientForConfig(config) - if err != nil { - setupLog.Error(err, "unable to create discovery client") - os.Exit(1) - } - - mgrOptions := ctrl.Options{ - Scheme: scheme, - MetricsBindAddress: metricsAddr, - Port: 9443, - HealthProbeBindAddress: probeAddr, - LeaderElection: enableLeaderElection, - LeaderElectionID: "79cbe7f3.kaasops.io", - } - customMgrOptions, err := setupCustomCache(&mgrOptions, namespace, watchLabel) - - if err != nil { - setupLog.Error(err, "unable to set up custom cache settings") - os.Exit(1) - } - - mgr, err := ctrl.NewManager(config, *customMgrOptions) - if err != nil { - setupLog.Error(err, "unable to start manager") - os.Exit(1) - } - - if err = (&controllers.VectorReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Clientset: clientset, - PipelineCheckWG: &pipelineCheckWG, - PipelineCheckTimeout: PipelineCheckTimeout, - ConfigCheckTimeout: ConfigCheckTimeout, - DiscoveryClient: dc, - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "Vector") - os.Exit(1) - } - if err = (&controllers.PipelineReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Clientset: clientset, - PipelineCheckWG: &pipelineCheckWG, - PipelineDeleteEventTimeout: PipelineDeleteEventTimeout, - ConfigCheckTimeout: ConfigCheckTimeout, - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "VectorPipeline") - os.Exit(1) - } - //+kubebuilder:scaffold:builder - - if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { - setupLog.Error(err, "unable to set up health check") - os.Exit(1) - } - if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil { - setupLog.Error(err, "unable to set up ready check") - os.Exit(1) - } - - setupLog.Info("starting manager") - if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { - setupLog.Error(err, "problem running manager") - os.Exit(1) - } -} - -func setupCustomCache(mgrOptions *ctrl.Options, namespace string, watchLabel string) (*ctrl.Options, error) { - if namespace == "" && watchLabel == "" { - return mgrOptions, nil - } - - var namespaceSelector fields.Selector - var labelSelector labels.Selector - if namespace != "" { - namespaceSelector = fields.Set{"metadata.namespace": namespace}.AsSelector() - } - if watchLabel != "" { - labelSelector = labels.Set{k8s.ManagedByLabelKey: "vector-operator", k8s.NameLabelKey: watchLabel}.AsSelector() - } - - selectorsByObject := cache.SelectorsByObject{ - &corev1.Pod{}: { - Field: namespaceSelector, - Label: labelSelector, - }, - &appsv1.DaemonSet{}: { - Field: namespaceSelector, - Label: labelSelector, - }, - &corev1.Service{}: { - Field: namespaceSelector, - Label: labelSelector, - }, - &corev1.Secret{}: { - Field: namespaceSelector, - Label: labelSelector, - }, - &corev1.ServiceAccount{}: { - Field: namespaceSelector, - Label: labelSelector, - }, - } - - mgrOptions.NewCache = cache.BuilderWithOptions(cache.Options{SelectorsByObject: selectorsByObject}) - - return mgrOptions, nil -} diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go new file mode 100644 index 00000000..ce823b49 --- /dev/null +++ b/test/e2e/e2e_suite_test.go @@ -0,0 +1,32 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package e2e + +import ( + "fmt" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +// Run e2e tests using the Ginkgo runner. +func TestE2E(t *testing.T) { + RegisterFailHandler(Fail) + _, _ = fmt.Fprintf(GinkgoWriter, "Starting vector-operator suite\n") + RunSpecs(t, "e2e suite") +} diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go new file mode 100644 index 00000000..901f0ade --- /dev/null +++ b/test/e2e/e2e_test.go @@ -0,0 +1,122 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package e2e + +import ( + "fmt" + "os/exec" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/kaasops/vector-operator/test/utils" +) + +const namespace = "vector-operator-system" + +var _ = Describe("controller", Ordered, func() { + BeforeAll(func() { + By("installing prometheus operator") + Expect(utils.InstallPrometheusOperator()).To(Succeed()) + + By("installing the cert-manager") + Expect(utils.InstallCertManager()).To(Succeed()) + + By("creating manager namespace") + cmd := exec.Command("kubectl", "create", "ns", namespace) + _, _ = utils.Run(cmd) + }) + + AfterAll(func() { + By("uninstalling the Prometheus manager bundle") + utils.UninstallPrometheusOperator() + + By("uninstalling the cert-manager bundle") + utils.UninstallCertManager() + + By("removing manager namespace") + cmd := exec.Command("kubectl", "delete", "ns", namespace) + _, _ = utils.Run(cmd) + }) + + Context("Operator", func() { + It("should run successfully", func() { + var controllerPodName string + var err error + + // projectimage stores the name of the image used in the example + var projectimage = "example.com/vector-operator:v0.0.1" + + By("building the manager(Operator) image") + cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", projectimage)) + _, err = utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred()) + + By("loading the the manager(Operator) image on Kind") + err = utils.LoadImageToKindClusterWithName(projectimage) + ExpectWithOffset(1, err).NotTo(HaveOccurred()) + + By("installing CRDs") + cmd = exec.Command("make", "install") + _, err = utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred()) + + By("deploying the controller-manager") + cmd = exec.Command("make", "deploy", fmt.Sprintf("IMG=%s", projectimage)) + _, err = utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred()) + + By("validating that the controller-manager pod is running as expected") + verifyControllerUp := func() error { + // Get pod name + + cmd = exec.Command("kubectl", "get", + "pods", "-l", "control-plane=controller-manager", + "-o", "go-template={{ range .items }}"+ + "{{ if not .metadata.deletionTimestamp }}"+ + "{{ .metadata.name }}"+ + "{{ \"\\n\" }}{{ end }}{{ end }}", + "-n", namespace, + ) + + podOutput, err := utils.Run(cmd) + ExpectWithOffset(2, err).NotTo(HaveOccurred()) + podNames := utils.GetNonEmptyLines(string(podOutput)) + if len(podNames) != 1 { + return fmt.Errorf("expect 1 controller pods running, but got %d", len(podNames)) + } + controllerPodName = podNames[0] + ExpectWithOffset(2, controllerPodName).Should(ContainSubstring("controller-manager")) + + // Validate pod status + cmd = exec.Command("kubectl", "get", + "pods", controllerPodName, "-o", "jsonpath={.status.phase}", + "-n", namespace, + ) + status, err := utils.Run(cmd) + ExpectWithOffset(2, err).NotTo(HaveOccurred()) + if string(status) != "Running" { + return fmt.Errorf("controller pod in %s status", status) + } + return nil + } + EventuallyWithOffset(1, verifyControllerUp, time.Minute, time.Second).Should(Succeed()) + + }) + }) +}) diff --git a/test/utils/utils.go b/test/utils/utils.go new file mode 100644 index 00000000..6b96ab5d --- /dev/null +++ b/test/utils/utils.go @@ -0,0 +1,140 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package utils + +import ( + "fmt" + "os" + "os/exec" + "strings" + + . "github.com/onsi/ginkgo/v2" //nolint:golint,revive +) + +const ( + prometheusOperatorVersion = "v0.72.0" + prometheusOperatorURL = "https://github.com/prometheus-operator/prometheus-operator/" + + "releases/download/%s/bundle.yaml" + + certmanagerVersion = "v1.14.4" + certmanagerURLTmpl = "https://github.com/jetstack/cert-manager/releases/download/%s/cert-manager.yaml" +) + +func warnError(err error) { + _, _ = fmt.Fprintf(GinkgoWriter, "warning: %v\n", err) +} + +// InstallPrometheusOperator installs the prometheus Operator to be used to export the enabled metrics. +func InstallPrometheusOperator() error { + url := fmt.Sprintf(prometheusOperatorURL, prometheusOperatorVersion) + cmd := exec.Command("kubectl", "create", "-f", url) + _, err := Run(cmd) + return err +} + +// Run executes the provided command within this context +func Run(cmd *exec.Cmd) ([]byte, error) { + dir, _ := GetProjectDir() + cmd.Dir = dir + + if err := os.Chdir(cmd.Dir); err != nil { + _, _ = fmt.Fprintf(GinkgoWriter, "chdir dir: %s\n", err) + } + + cmd.Env = append(os.Environ(), "GO111MODULE=on") + command := strings.Join(cmd.Args, " ") + _, _ = fmt.Fprintf(GinkgoWriter, "running: %s\n", command) + output, err := cmd.CombinedOutput() + if err != nil { + return output, fmt.Errorf("%s failed with error: (%v) %s", command, err, string(output)) + } + + return output, nil +} + +// UninstallPrometheusOperator uninstalls the prometheus +func UninstallPrometheusOperator() { + url := fmt.Sprintf(prometheusOperatorURL, prometheusOperatorVersion) + cmd := exec.Command("kubectl", "delete", "-f", url) + if _, err := Run(cmd); err != nil { + warnError(err) + } +} + +// UninstallCertManager uninstalls the cert manager +func UninstallCertManager() { + url := fmt.Sprintf(certmanagerURLTmpl, certmanagerVersion) + cmd := exec.Command("kubectl", "delete", "-f", url) + if _, err := Run(cmd); err != nil { + warnError(err) + } +} + +// InstallCertManager installs the cert manager bundle. +func InstallCertManager() error { + url := fmt.Sprintf(certmanagerURLTmpl, certmanagerVersion) + cmd := exec.Command("kubectl", "apply", "-f", url) + if _, err := Run(cmd); err != nil { + return err + } + // Wait for cert-manager-webhook to be ready, which can take time if cert-manager + // was re-installed after uninstalling on a cluster. + cmd = exec.Command("kubectl", "wait", "deployment.apps/cert-manager-webhook", + "--for", "condition=Available", + "--namespace", "cert-manager", + "--timeout", "5m", + ) + + _, err := Run(cmd) + return err +} + +// LoadImageToKindClusterWithName loads a local docker image to the kind cluster +func LoadImageToKindClusterWithName(name string) error { + cluster := "kind" + if v, ok := os.LookupEnv("KIND_CLUSTER"); ok { + cluster = v + } + kindOptions := []string{"load", "docker-image", name, "--name", cluster} + cmd := exec.Command("kind", kindOptions...) + _, err := Run(cmd) + return err +} + +// GetNonEmptyLines converts given command output string into individual objects +// according to line breakers, and ignores the empty elements in it. +func GetNonEmptyLines(output string) []string { + var res []string + elements := strings.Split(output, "\n") + for _, element := range elements { + if element != "" { + res = append(res, element) + } + } + + return res +} + +// GetProjectDir will return the directory where the project is +func GetProjectDir() (string, error) { + wd, err := os.Getwd() + if err != nil { + return wd, err + } + wd = strings.Replace(wd, "/test/e2e", "", -1) + return wd, nil +} From 09f60bb048d170e70ae41638e5d92f387f1ce69d Mon Sep 17 00:00:00 2001 From: aa1ex <115736127+aa1ex@users.noreply.github.com> Date: Thu, 26 Sep 2024 12:00:54 +0300 Subject: [PATCH 269/316] Select pipelines via labels (#148) add match label + small refactoring Signed-off-by: Aleksandr Aleksandrov --- api/v1alpha1/vector_types.go | 11 +- api/v1alpha1/vectorpipeline.go | 4 - api/v1alpha1/zz_generated.deepcopy.go | 23 ++ cmd/main.go | 68 ++++-- .../observability.kaasops.io_vectors.yaml | 15 +- internal/config/configcheck/configcheck.go | 2 + internal/controller/pipeline_controller.go | 178 +++++++++++++++ ...er_test.go => pipeline_controller_test.go} | 20 +- internal/controller/suite_test.go | 11 +- internal/controller/vector_controller.go | 84 +++---- internal/controller/vector_controller_test.go | 22 +- .../controller/vectorpipeline_controller.go | 210 ------------------ internal/pipeline/pipeline.go | 28 ++- internal/pipeline/pipeline_test.go | 59 +++++ internal/vector/vectoragent/vectoragent.go | 14 +- 15 files changed, 410 insertions(+), 339 deletions(-) create mode 100644 internal/controller/pipeline_controller.go rename internal/controller/{vectorpipeline_controller_test.go => pipeline_controller_test.go} (81%) delete mode 100644 internal/controller/vectorpipeline_controller.go create mode 100644 internal/pipeline/pipeline_test.go diff --git a/api/v1alpha1/vector_types.go b/api/v1alpha1/vector_types.go index 3b57f662..080a59d6 100644 --- a/api/v1alpha1/vector_types.go +++ b/api/v1alpha1/vector_types.go @@ -26,14 +26,17 @@ import ( // VectorSpec defines the desired state of Vector type VectorSpec struct { - // DisableAggregation - // DisableAggregation bool `json:"disableAggregation,omitempty"` // Vector Agent Agent *VectorAgent `json:"agent,omitempty"` // Determines if requests to the kube-apiserver can be served by a cache. UseApiServerCache bool `json:"useApiServerCache,omitempty"` - // Vector Aggregator - // Aggregator *VectorAggregator `json:"aggregator,omitempty"` + // Defines a filter for the Vector Pipeline and Cluster Vector Pipeline by labels. + // If not specified, all pipelines will be selected. + Selector VectorSelectorSpec `json:"selector,omitempty"` +} + +type VectorSelectorSpec struct { + MatchLabels map[string]string `json:"matchLabels,omitempty"` } // VectorStatus defines the observed state of Vector diff --git a/api/v1alpha1/vectorpipeline.go b/api/v1alpha1/vectorpipeline.go index b3e545b0..9a30b01b 100644 --- a/api/v1alpha1/vectorpipeline.go +++ b/api/v1alpha1/vectorpipeline.go @@ -7,10 +7,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -var ( - LocalPipelineKind = "VectorPipeline" -) - func (vp *VectorPipeline) GetSpec() VectorPipelineSpec { return vp.Spec } diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 0d828cc8..a57c6cd9 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -436,6 +436,28 @@ func (in *VectorPipelineStatus) DeepCopy() *VectorPipelineStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VectorSelectorSpec) DeepCopyInto(out *VectorSelectorSpec) { + *out = *in + if in.MatchLabels != nil { + in, out := &in.MatchLabels, &out.MatchLabels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VectorSelectorSpec. +func (in *VectorSelectorSpec) DeepCopy() *VectorSelectorSpec { + if in == nil { + return nil + } + out := new(VectorSelectorSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VectorSpec) DeepCopyInto(out *VectorSpec) { *out = *in @@ -444,6 +466,7 @@ func (in *VectorSpec) DeepCopyInto(out *VectorSpec) { *out = new(VectorAgent) (*in).DeepCopyInto(*out) } + in.Selector.DeepCopyInto(&out.Selector) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VectorSpec. diff --git a/cmd/main.go b/cmd/main.go index 69f2c138..1dd3b938 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -17,8 +17,10 @@ limitations under the License. package main import ( + "context" "crypto/tls" "flag" + "fmt" "github.com/kaasops/vector-operator/internal/utils/k8s" monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" appsv1 "k8s.io/api/apps/v1" @@ -29,7 +31,7 @@ import ( "os" "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/client" - "sync" + "sigs.k8s.io/controller-runtime/pkg/event" "time" // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) @@ -73,9 +75,6 @@ func main() { var tlsOpts []func(*tls.Config) var watchNamespace string var watchLabel string - var pipelineCheckWG sync.WaitGroup - var pipelineCheckTimeout time.Duration - var pipelineDeleteEventTimeout time.Duration var configCheckTimeout time.Duration flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+ @@ -90,8 +89,6 @@ func main() { "If set, HTTP/2 will be enabled for the metrics and webhook servers") flag.StringVar(&watchNamespace, "watch-namespace", "", "Namespace to filter the list of watched objects") flag.StringVar(&watchLabel, "watch-name", "", "Filter the list of watched objects by checking the app.kubernetes.io/managed-by label") - flag.DurationVar(&pipelineCheckTimeout, "pipeline-check-timeout", 15*time.Second, "wait pipeline checks before force vector reconcile. Default: 15s") - flag.DurationVar(&pipelineDeleteEventTimeout, "pipeline-delete-timeout", 5*time.Second, "collect delete events timeout") flag.DurationVar(&configCheckTimeout, "configcheck-timeout", 300*time.Second, "configcheck timeout") opts := zap.Options{ Development: true, @@ -189,31 +186,38 @@ func main() { os.Exit(1) } + vectorAgentEventCh := make(chan event.GenericEvent) + defer close(vectorAgentEventCh) + if err = (&controller.VectorReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Clientset: clientset, - PipelineCheckWG: &pipelineCheckWG, - PipelineCheckTimeout: pipelineCheckTimeout, - ConfigCheckTimeout: configCheckTimeout, - DiscoveryClient: dc, + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Clientset: clientset, + ConfigCheckTimeout: configCheckTimeout, + DiscoveryClient: dc, + EventChan: vectorAgentEventCh, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Vector") os.Exit(1) } + + vectorPipelineEventCh := make(chan event.GenericEvent, 10) + defer close(vectorPipelineEventCh) + if err = (&controller.PipelineReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Clientset: clientset, - PipelineCheckWG: &pipelineCheckWG, - PipelineDeleteEventTimeout: pipelineDeleteEventTimeout, - ConfigCheckTimeout: configCheckTimeout, + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Clientset: clientset, + ConfigCheckTimeout: configCheckTimeout, + VectorAgentEventCh: vectorPipelineEventCh, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "VectorPipeline") os.Exit(1) } // +kubebuilder:scaffold:builder + go reconcileWithDelay(context.Background(), vectorPipelineEventCh, vectorAgentEventCh, time.Second*10) + if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { setupLog.Error(err, "unable to set up health check") os.Exit(1) @@ -288,3 +292,29 @@ func setupCustomCache(mgrOptions *ctrl.Options, namespace string, watchLabel str return mgrOptions, nil } + +func reconcileWithDelay(ctx context.Context, in, out chan event.GenericEvent, delay time.Duration) { + ticker := time.NewTicker(delay) + defer ticker.Stop() + + store := make(map[string]event.GenericEvent) + + for { + select { + case <-ctx.Done(): + return + case ev := <-in: + key := fmt.Sprintf("%s/%s", ev.Object.GetNamespace(), ev.Object.GetName()) + if _, ok := store[key]; !ok { + store[key] = ev + } + case <-ticker.C: + if len(store) != 0 { + for nn, ev := range store { + out <- ev + delete(store, nn) + } + } + } + } +} diff --git a/config/crd/bases/observability.kaasops.io_vectors.yaml b/config/crd/bases/observability.kaasops.io_vectors.yaml index 14862094..61494279 100644 --- a/config/crd/bases/observability.kaasops.io_vectors.yaml +++ b/config/crd/bases/observability.kaasops.io_vectors.yaml @@ -47,10 +47,7 @@ spec: description: VectorSpec defines the desired state of Vector properties: agent: - description: |- - DisableAggregation - DisableAggregation bool `json:"disableAggregation,omitempty"` - Vector Agent + description: Vector Agent properties: affinity: description: Affinity If specified, the pod's scheduling constraints. @@ -4962,6 +4959,16 @@ spec: type: object type: array type: object + selector: + description: |- + Defines a filter for the Vector Pipeline and Cluster Vector Pipeline by labels. + If not specified, all pipelines will be selected. + properties: + matchLabels: + additionalProperties: + type: string + type: object + type: object useApiServerCache: description: Determines if requests to the kube-apiserver can be served by a cache. diff --git a/internal/config/configcheck/configcheck.go b/internal/config/configcheck/configcheck.go index 6b1f746d..2cf979dd 100644 --- a/internal/config/configcheck/configcheck.go +++ b/internal/config/configcheck/configcheck.go @@ -68,6 +68,7 @@ func New( cs *kubernetes.Clientset, va *vectorv1alpha1.Vector, timeout time.Duration, + initiator string, ) *ConfigCheck { image := va.Spec.Agent.Image if va.Spec.Agent.ConfigCheck.Image != nil { @@ -105,6 +106,7 @@ func New( ConfigReloaderResources: va.Spec.Agent.ConfigReloaderResources, ConfigCheckTimeout: timeout, Annotations: va.Spec.Agent.ConfigCheck.Annotations, + Initiator: initiator, } } diff --git a/internal/controller/pipeline_controller.go b/internal/controller/pipeline_controller.go new file mode 100644 index 00000000..797e6253 --- /dev/null +++ b/internal/controller/pipeline_controller.go @@ -0,0 +1,178 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "context" + "errors" + "fmt" + "golang.org/x/sync/errgroup" + "sigs.k8s.io/controller-runtime/pkg/controller" + "time" + + "github.com/kaasops/vector-operator/api/v1alpha1" + "github.com/kaasops/vector-operator/internal/config" + "github.com/kaasops/vector-operator/internal/config/configcheck" + "github.com/kaasops/vector-operator/internal/pipeline" + "github.com/kaasops/vector-operator/internal/vector/vectoragent" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/predicate" +) + +type PipelineReconciler struct { + client.Client + Scheme *runtime.Scheme + + // Temp. Wait this issue - https://github.com/kubernetes-sigs/controller-runtime/issues/452 + Clientset *kubernetes.Clientset + ConfigCheckTimeout time.Duration + VectorAgentEventCh chan event.GenericEvent +} + +var ( + ErrBuildConfigFailed = errors.New("failed to build config") +) + +//+kubebuilder:rbac:groups=observability.kaasops.io,resources=vectorpipelines;clustervectorpipelines,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=observability.kaasops.io,resources=vectorpipelines/status;clustervectorpipelines/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=observability.kaasops.io,resources=vectorpipelines/finalizers;clustervectorpipelines/finalizers,verbs=update + +func (r *PipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + log := log.FromContext(ctx).WithValues("Pipeline", req.Name) + + log.Info("start Reconcile Pipeline") + pipelineCR, err := r.getPipeline(ctx, req) + if err != nil { + log.Error(err, "Failed to get Pipeline") + return ctrl.Result{}, err + } + vectorAgents, err := listVectorAgents(ctx, r.Client) + if err != nil { + log.Error(err, "Failed to get Instances") + return ctrl.Result{}, nil + } + + if len(vectorAgents) == 0 { + log.Info("Vectors not found") + return ctrl.Result{}, nil + } + + if pipelineCR == nil { + log.Info("Pipeline CR not found. Ignoring since object must be deleted") + for _, vector := range vectorAgents { + r.VectorAgentEventCh <- event.GenericEvent{Object: vector} + } + return ctrl.Result{}, nil + } + + // Check Pipeline hash + checkResult, err := pipeline.CheckHash(pipelineCR) + if err != nil { + return ctrl.Result{}, err + } + if checkResult { + log.Info("Pipeline has no changes. Finish Reconcile Pipeline") + return ctrl.Result{}, nil + } + + eg := errgroup.Group{} + + for _, vector := range vectorAgents { + eg.Go(func() error { + vaCtrl := vectoragent.NewController(vector, r.Client, r.Clientset) + byteConfig, err := config.BuildByteConfig(vaCtrl, pipelineCR) + if err != nil { + return fmt.Errorf("agent %s/%s build config failed: %w: %w", vector.Namespace, vector.Name, ErrBuildConfigFailed, err) + } + + vaCtrl.Config = byteConfig + configCheck := configcheck.New( + vaCtrl.Config, + vaCtrl.Client, + vaCtrl.ClientSet, + vaCtrl.Vector, + r.ConfigCheckTimeout, + configcheck.ConfigCheckInitiatorPipieline, + ) + + reason, err := configCheck.Run(ctx) + if reason != "" { + return fmt.Errorf("agent %s/%s config check failed: %s", vector.Namespace, vector.Name, reason) + } + return err + }) + } + + if err = eg.Wait(); err != nil { + log.Error(err, "Configcheck error") + if err := pipeline.SetFailedStatus(ctx, r.Client, pipelineCR, err.Error()); err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{}, IgnoreBuildConfigFailed(err) + } + + if err = pipeline.SetSuccessStatus(ctx, r.Client, pipelineCR); err != nil { + return ctrl.Result{}, err + } + + for _, vector := range vectorAgents { + r.VectorAgentEventCh <- event.GenericEvent{Object: vector} + } + + log.Info("finish Reconcile Pipeline") + return ctrl.Result{}, nil +} + +func (r *PipelineReconciler) getPipeline(ctx context.Context, req ctrl.Request) (pipeline pipeline.Pipeline, err error) { + if req.Namespace != "" { + vp := &v1alpha1.VectorPipeline{} + err := r.Get(ctx, req.NamespacedName, vp) + if err != nil { + return nil, client.IgnoreNotFound(err) + } + return vp, nil + } + cvp := &v1alpha1.ClusterVectorPipeline{} + err = r.Get(ctx, req.NamespacedName, cvp) + if err != nil { + return nil, client.IgnoreNotFound(err) + } + return cvp, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *PipelineReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&v1alpha1.VectorPipeline{}). + WithOptions(controller.Options{MaxConcurrentReconciles: 20}). + Watches(&v1alpha1.ClusterVectorPipeline{}, &handler.EnqueueRequestForObject{}). + WithEventFilter(predicate.GenerationChangedPredicate{}). + Complete(r) +} + +func IgnoreBuildConfigFailed(err error) error { + if errors.Is(err, ErrBuildConfigFailed) { + return nil + } + return err +} diff --git a/internal/controller/vectorpipeline_controller_test.go b/internal/controller/pipeline_controller_test.go similarity index 81% rename from internal/controller/vectorpipeline_controller_test.go rename to internal/controller/pipeline_controller_test.go index 8e51ed6f..d0e58d36 100644 --- a/internal/controller/vectorpipeline_controller_test.go +++ b/internal/controller/pipeline_controller_test.go @@ -18,6 +18,7 @@ package controller import ( "context" + "sigs.k8s.io/controller-runtime/pkg/event" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -27,7 +28,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - observabilityv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" + "github.com/kaasops/vector-operator/api/v1alpha1" ) var _ = Describe("VectorPipeline Controller", func() { @@ -40,13 +41,13 @@ var _ = Describe("VectorPipeline Controller", func() { Name: resourceName, Namespace: "default", // TODO(user):Modify as needed } - vectorpipeline := &observabilityv1alpha1.VectorPipeline{} + vectorpipeline := &v1alpha1.VectorPipeline{} BeforeEach(func() { By("creating the custom resource for the Kind VectorPipeline") err := k8sClient.Get(ctx, typeNamespacedName, vectorpipeline) if err != nil && errors.IsNotFound(err) { - resource := &observabilityv1alpha1.VectorPipeline{ + resource := &v1alpha1.VectorPipeline{ ObjectMeta: metav1.ObjectMeta{ Name: resourceName, Namespace: "default", @@ -59,7 +60,7 @@ var _ = Describe("VectorPipeline Controller", func() { AfterEach(func() { // TODO(user): Cleanup logic after each test, like removing the resource instance. - resource := &observabilityv1alpha1.VectorPipeline{} + resource := &v1alpha1.VectorPipeline{} err := k8sClient.Get(ctx, typeNamespacedName, resource) Expect(err).NotTo(HaveOccurred()) @@ -69,12 +70,11 @@ var _ = Describe("VectorPipeline Controller", func() { It("should successfully reconcile the resource", func() { By("Reconciling the created resource") controllerReconciler := &PipelineReconciler{ - Client: k8sClient, - Scheme: k8sClient.Scheme(), - Clientset: clientset, - PipelineCheckWG: wg, - PipelineDeleteEventTimeout: pipelineDeleteEventTimeout, - ConfigCheckTimeout: configCheckTimeout, + Client: k8sClient, + Scheme: k8sClient.Scheme(), + Clientset: clientset, + ConfigCheckTimeout: configCheckTimeout, + VectorAgentEventCh: make(chan event.GenericEvent, 1), } _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ diff --git a/internal/controller/suite_test.go b/internal/controller/suite_test.go index 59cff298..ae92768a 100644 --- a/internal/controller/suite_test.go +++ b/internal/controller/suite_test.go @@ -23,7 +23,6 @@ import ( "k8s.io/utils/pointer" "path/filepath" "runtime" - "sync" "testing" "time" @@ -37,7 +36,7 @@ import ( logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" - observabilityv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" + "github.com/kaasops/vector-operator/api/v1alpha1" // +kubebuilder:scaffold:imports ) @@ -50,9 +49,6 @@ var testEnv *envtest.Environment var ctx context.Context var cancel context.CancelFunc var clientset *kubernetes.Clientset -var wg *sync.WaitGroup -var pipelineCheckTimeout time.Duration -var pipelineDeleteEventTimeout time.Duration var configCheckTimeout time.Duration func TestControllers(t *testing.T) { @@ -87,7 +83,7 @@ var _ = BeforeSuite(func() { Expect(err).NotTo(HaveOccurred()) Expect(cfg).NotTo(BeNil()) - err = observabilityv1alpha1.AddToScheme(scheme.Scheme) + err = v1alpha1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) // +kubebuilder:scaffold:scheme @@ -96,10 +92,7 @@ var _ = BeforeSuite(func() { Expect(err).NotTo(HaveOccurred()) Expect(k8sClient).NotTo(BeNil()) - wg = &sync.WaitGroup{} - pipelineCheckTimeout = time.Second * 15 configCheckTimeout = time.Second * 60 - pipelineDeleteEventTimeout = time.Second * 3 clientset, err = kubernetes.NewForConfig(cfg) Expect(err).NotTo(HaveOccurred()) diff --git a/internal/controller/vector_controller.go b/internal/controller/vector_controller.go index a99af9f3..a166409d 100644 --- a/internal/controller/vector_controller.go +++ b/internal/controller/vector_controller.go @@ -19,7 +19,9 @@ package controller import ( "context" "errors" - "sync" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/source" "time" "github.com/kaasops/vector-operator/internal/config" @@ -34,6 +36,8 @@ import ( rbacv1 "k8s.io/api/rbac/v1" + "github.com/kaasops/vector-operator/api/v1alpha1" + monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" api_errors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/discovery" @@ -41,13 +45,8 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/predicate" - "sigs.k8s.io/controller-runtime/pkg/source" - - vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" - monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" ) // VectorReconciler reconciles a Vector object @@ -56,11 +55,10 @@ type VectorReconciler struct { Scheme *runtime.Scheme // Temp. Wait this issue - https://github.com/kubernetes-sigs/controller-runtime/issues/452 - Clientset *kubernetes.Clientset - PipelineCheckWG *sync.WaitGroup - PipelineCheckTimeout time.Duration - ConfigCheckTimeout time.Duration - DiscoveryClient *discovery.DiscoveryClient + Clientset *kubernetes.Clientset + ConfigCheckTimeout time.Duration + DiscoveryClient *discovery.DiscoveryClient + EventChan chan event.GenericEvent } //+kubebuilder:rbac:groups=observability.kaasops.io,resources=vectors,verbs=get;list;watch;create;update;patch;delete @@ -81,13 +79,9 @@ type VectorReconciler struct { func (r *VectorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log := log.FromContext(ctx).WithValues("Vector", req.NamespacedName) - log.Info("Waiting pipeline checks") - if waitPipelineChecks(r.PipelineCheckWG, r.PipelineCheckTimeout) { - log.Info("Timeout waiting pipeline checks, continue reconcile vector") - } log.Info("Start Reconcile Vector") if req.Namespace == "" { - vectors, err := listVectorCustomResourceInstances(ctx, r.Client) + vectors, err := listVectorAgents(ctx, r.Client) if err != nil { log.Error(err, "Failed to list vector instances") return ctrl.Result{}, err @@ -104,7 +98,6 @@ func (r *VectorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr log.Info("Vector CR not found. Ignoring since object must be deleted") return ctrl.Result{}, nil } - return r.createOrUpdateVector(ctx, r.Client, r.Clientset, vectorCR, false) } @@ -115,8 +108,8 @@ func (r *VectorReconciler) SetupWithManager(mgr ctrl.Manager) error { return err } builder := ctrl.NewControllerManagedBy(mgr). - For(&vectorv1alpha1.Vector{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})). - WatchesRawSource(source.Channel(VectorAgentReconciliationSourceChannel, &handler.EnqueueRequestForObject{})). + For(&v1alpha1.Vector{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})). + WatchesRawSource(source.Channel(r.EventChan, &handler.EnqueueRequestForObject{})). Owns(&appsv1.DaemonSet{}). Owns(&corev1.Service{}). Owns(&corev1.Secret{}). @@ -134,21 +127,24 @@ func (r *VectorReconciler) SetupWithManager(mgr ctrl.Manager) error { return nil } -func listVectorCustomResourceInstances(ctx context.Context, client client.Client) (vectors []*vectorv1alpha1.Vector, err error) { - vectorlist := vectorv1alpha1.VectorList{} - err = client.List(ctx, &vectorlist) +func listVectorAgents(ctx context.Context, client client.Client) (vectors []*v1alpha1.Vector, err error) { + vectorList := v1alpha1.VectorList{} + err = client.List(ctx, &vectorList) if err != nil { return nil, err } - for _, vector := range vectorlist.Items { + for _, vector := range vectorList.Items { + if vector.DeletionTimestamp != nil { + continue + } vectors = append(vectors, &vector) } return vectors, nil } -func (r *VectorReconciler) findVectorCustomResourceInstance(ctx context.Context, req ctrl.Request) (*vectorv1alpha1.Vector, error) { +func (r *VectorReconciler) findVectorCustomResourceInstance(ctx context.Context, req ctrl.Request) (*v1alpha1.Vector, error) { // fetch the master instance - vectorCR := &vectorv1alpha1.Vector{} + vectorCR := &v1alpha1.Vector{} err := r.Get(ctx, req.NamespacedName, vectorCR) if err != nil { if api_errors.IsNotFound(err) { @@ -156,10 +152,11 @@ func (r *VectorReconciler) findVectorCustomResourceInstance(ctx context.Context, } return nil, err } + setTypeMetaIfNeeded(vectorCR) return vectorCR, nil } -func (r *VectorReconciler) reconcileVectors(ctx context.Context, client client.Client, clientset *kubernetes.Clientset, configOnly bool, vectors ...*vectorv1alpha1.Vector) (ctrl.Result, error) { +func (r *VectorReconciler) reconcileVectors(ctx context.Context, client client.Client, clientset *kubernetes.Clientset, configOnly bool, vectors ...*v1alpha1.Vector) (ctrl.Result, error) { if len(vectors) == 0 { return ctrl.Result{}, nil } @@ -168,6 +165,7 @@ func (r *VectorReconciler) reconcileVectors(ctx context.Context, client client.C if vector.DeletionTimestamp != nil { continue } + setTypeMetaIfNeeded(vector) if _, err := r.createOrUpdateVector(ctx, client, clientset, vector, configOnly); err != nil { return ctrl.Result{}, err } @@ -175,15 +173,13 @@ func (r *VectorReconciler) reconcileVectors(ctx context.Context, client client.C return ctrl.Result{}, nil } -func (r *VectorReconciler) createOrUpdateVector(ctx context.Context, client client.Client, clientset *kubernetes.Clientset, v *vectorv1alpha1.Vector, configOnly bool) (ctrl.Result, error) { +func (r *VectorReconciler) createOrUpdateVector(ctx context.Context, client client.Client, clientset *kubernetes.Clientset, v *v1alpha1.Vector, configOnly bool) (ctrl.Result, error) { log := log.FromContext(ctx).WithValues("Vector", v.Name) // Init Controller for Vector Agent vaCtrl := vectoragent.NewController(v, client, clientset) - vaCtrl.SetDefault() - // Get Vector Config file - pipelines, err := pipeline.GetValidPipelines(ctx, vaCtrl.Client) + pipelines, err := pipeline.GetValidPipelines(ctx, vaCtrl.Client, vaCtrl.Vector.Spec.Selector) if err != nil { return ctrl.Result{}, err } @@ -203,8 +199,8 @@ func (r *VectorReconciler) createOrUpdateVector(ctx context.Context, client clie vaCtrl.ClientSet, vaCtrl.Vector, r.ConfigCheckTimeout, + configcheck.ConfigCheckInitiatorVector, ) - configCheck.Initiator = configcheck.ConfigCheckInitiatorVector reason, err := configCheck.Run(ctx) if err != nil { if errors.Is(err, configcheck.ValidationError) { @@ -226,15 +222,7 @@ func (r *VectorReconciler) createOrUpdateVector(ctx context.Context, client clie return ctrl.Result{}, err } - if err := vaCtrl.SetLastAppliedPipelineStatus(ctx, &cfgHash); err != nil { - //TODO: Handle err: Operation cannot be fulfilled on vectors.observability.kaasops.io \"vector-sample\": the object has been modified; please apply your changes to the latest version and try again - if api_errors.IsConflict(err) { - return ctrl.Result{}, err - } - return ctrl.Result{}, err - } - - if err := vaCtrl.SetSuccessStatus(ctx); err != nil { + if err := vaCtrl.SetSuccessStatus(ctx, &cfgHash); err != nil { // TODO: Handle err: Operation cannot be fulfilled on vectors.observability.kaasops.io \"vector-sample\": the object has been modified; please apply your changes to the latest version and try again if api_errors.IsConflict(err) { return ctrl.Result{}, err @@ -245,16 +233,10 @@ func (r *VectorReconciler) createOrUpdateVector(ctx context.Context, client clie return ctrl.Result{}, nil } -func waitPipelineChecks(wg *sync.WaitGroup, timeout time.Duration) bool { - c := make(chan struct{}) - go func() { - defer close(c) - wg.Wait() - }() - select { - case <-c: - return false - case <-time.After(timeout): - return true +func setTypeMetaIfNeeded(cr *v1alpha1.Vector) { + // https://github.com/kubernetes/kubernetes/issues/80609 + if cr.Kind == "" || cr.APIVersion == "" { + cr.Kind = "Vector" + cr.APIVersion = "observability.kaasops.io/v1alpha1" } } diff --git a/internal/controller/vector_controller_test.go b/internal/controller/vector_controller_test.go index 5102288b..5c6a2b62 100644 --- a/internal/controller/vector_controller_test.go +++ b/internal/controller/vector_controller_test.go @@ -22,11 +22,12 @@ import ( . "github.com/onsi/gomega" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/event" "sigs.k8s.io/controller-runtime/pkg/reconcile" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - observabilityv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" + "github.com/kaasops/vector-operator/api/v1alpha1" ) var _ = Describe("Vector Controller", func() { @@ -39,13 +40,13 @@ var _ = Describe("Vector Controller", func() { Name: resourceName, Namespace: "default", // TODO(user):Modify as needed } - vector := &observabilityv1alpha1.Vector{} + vector := &v1alpha1.Vector{} BeforeEach(func() { By("creating the custom resource for the Kind Vector") err := k8sClient.Get(ctx, typeNamespacedName, vector) if err != nil && errors.IsNotFound(err) { - resource := &observabilityv1alpha1.Vector{ + resource := &v1alpha1.Vector{ ObjectMeta: metav1.ObjectMeta{ Name: resourceName, Namespace: "default", @@ -58,7 +59,7 @@ var _ = Describe("Vector Controller", func() { AfterEach(func() { // TODO(user): Cleanup logic after each test, like removing the resource instance. - resource := &observabilityv1alpha1.Vector{} + resource := &v1alpha1.Vector{} err := k8sClient.Get(ctx, typeNamespacedName, resource) Expect(err).NotTo(HaveOccurred()) @@ -68,13 +69,12 @@ var _ = Describe("Vector Controller", func() { It("should successfully reconcile the resource", func() { By("Reconciling the created resource") controllerReconciler := &VectorReconciler{ - Client: k8sClient, - Scheme: k8sClient.Scheme(), - Clientset: clientset, - PipelineCheckWG: wg, - PipelineCheckTimeout: pipelineCheckTimeout, - ConfigCheckTimeout: configCheckTimeout, - DiscoveryClient: clientset.DiscoveryClient, + Client: k8sClient, + Scheme: k8sClient.Scheme(), + Clientset: clientset, + ConfigCheckTimeout: configCheckTimeout, + DiscoveryClient: clientset.DiscoveryClient, + EventChan: make(chan event.GenericEvent, 1), } _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ diff --git a/internal/controller/vectorpipeline_controller.go b/internal/controller/vectorpipeline_controller.go deleted file mode 100644 index 8eda3ba8..00000000 --- a/internal/controller/vectorpipeline_controller.go +++ /dev/null @@ -1,210 +0,0 @@ -/* -Copyright 2024. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package controller - -import ( - "context" - "sync" - "time" - - vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" - "github.com/kaasops/vector-operator/internal/config" - "github.com/kaasops/vector-operator/internal/config/configcheck" - "github.com/kaasops/vector-operator/internal/pipeline" - "github.com/kaasops/vector-operator/internal/vector/vectoragent" - api_errors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/kubernetes" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/event" - "sigs.k8s.io/controller-runtime/pkg/handler" - "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/predicate" -) - -type PipelineReconciler struct { - client.Client - Scheme *runtime.Scheme - - // Temp. Wait this issue - https://github.com/kubernetes-sigs/controller-runtime/issues/452 - Clientset *kubernetes.Clientset - PipelineCheckWG *sync.WaitGroup - PipelineDeleteEventTimeout time.Duration - ConfigCheckTimeout time.Duration -} - -//+kubebuilder:rbac:groups=observability.kaasops.io,resources=vectorpipelines;clustervectorpipelines,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=observability.kaasops.io,resources=vectorpipelines/status;clustervectorpipelines/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=observability.kaasops.io,resources=vectorpipelines/finalizers;clustervectorpipelines/finalizers,verbs=update - -var VectorAgentReconciliationSourceChannel = make(chan event.GenericEvent) - -const PipelineDeleteEventTimeout time.Duration = 3 * time.Second - -func (r *PipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - log := log.FromContext(ctx).WithValues("Pipeline", req.Name) - - log.Info("start Reconcile Pipeline") - pipelineCR, err := r.findPipelineCustomResourceInstance(ctx, req) - if err != nil { - log.Error(err, "Failed to get Pipeline") - return ctrl.Result{}, err - } - vectorInstances, err := listVectorCustomResourceInstances(ctx, r.Client) - - if err != nil { - log.Error(err, "Failed to get Instances") - return ctrl.Result{}, nil - } - - if len(vectorInstances) == 0 { - log.Info("Vertors not found") - return ctrl.Result{}, nil - } - - if pipelineCR == nil { - log.Info("Pipeline CR not found. Ignoring since object must be deleted") - for _, vector := range vectorInstances { - r.PipelineCheckWG.Add(1) - go func() { - VectorAgentReconciliationSourceChannel <- event.GenericEvent{Object: vector} - log.Info("Waiting if other deletions will be done during timeout") - time.Sleep(r.PipelineDeleteEventTimeout) - r.PipelineCheckWG.Done() - }() - return ctrl.Result{}, nil - } - } - - // Check Pipeline hash - checkResult, err := pipeline.CheckHash(pipelineCR) - if err != nil { - return ctrl.Result{}, err - } - if checkResult { - log.Info("Pipeline has no changes. Finish Reconcile Pipeline") - return ctrl.Result{}, nil - } - - for _, vector := range vectorInstances { - if vector.DeletionTimestamp != nil { - continue - } - - // Init Controller for Vector Agent - vaCtrl := vectoragent.NewController(vector, r.Client, r.Clientset) - vaCtrl.SetDefault() - // Get Vector Config file - byteConfig, err := config.BuildByteConfig(vaCtrl, pipelineCR) - if err != nil { - if err := pipeline.SetFailedStatus(ctx, r.Client, pipelineCR, err.Error()); err != nil { - return ctrl.Result{}, err - } - if err = pipeline.SetLastAppliedPipelineStatus(ctx, r.Client, pipelineCR); err != nil { - return ctrl.Result{}, err - } - return ctrl.Result{}, err - } - - vaCtrl.Config = byteConfig - r.PipelineCheckWG.Add(1) - go r.runPipelineCheck(ctx, pipelineCR, vaCtrl) - } - - log.Info("finish Reconcile Pipeline") - return ctrl.Result{}, nil -} - -func (r *PipelineReconciler) findPipelineCustomResourceInstance(ctx context.Context, req ctrl.Request) (pipeline pipeline.Pipeline, err error) { - if req.Namespace != "" { - vp := &vectorv1alpha1.VectorPipeline{} - err := r.Get(ctx, req.NamespacedName, vp) - if err != nil { - if api_errors.IsNotFound(err) { - return nil, nil - } - return nil, err - } - return vp, nil - } else { - cvp := &vectorv1alpha1.ClusterVectorPipeline{} - err := r.Get(ctx, req.NamespacedName, cvp) - if err != nil { - if api_errors.IsNotFound(err) { - return nil, nil - } - return nil, err - } - return cvp, nil - } -} - -// SetupWithManager sets up the controller with the Manager. -func (r *PipelineReconciler) SetupWithManager(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - For(&vectorv1alpha1.VectorPipeline{}). - Watches(&vectorv1alpha1.ClusterVectorPipeline{}, &handler.EnqueueRequestForObject{}). - WithEventFilter(predicate.GenerationChangedPredicate{}). - Complete(r) -} - -func (r *PipelineReconciler) runPipelineCheck(ctx context.Context, p pipeline.Pipeline, vaCtrl *vectoragent.Controller) { - log := log.FromContext(ctx).WithValues("Pipeline", p.GetName()) - // Init CheckConfig - configCheck := configcheck.New( - vaCtrl.Config, - vaCtrl.Client, - vaCtrl.ClientSet, - vaCtrl.Vector, - r.ConfigCheckTimeout, - ) - configCheck.Initiator = configcheck.ConfigCheckInitiatorPipieline - defer r.PipelineCheckWG.Done() - // Start ConfigCheck - reason, err := configCheck.Run(ctx) - if reason != "" { - if err = pipeline.SetFailedStatus(ctx, r.Client, p, reason); err != nil { - log.Error(err, "Failed to set pipeline status") - return - } - if err = pipeline.SetLastAppliedPipelineStatus(ctx, r.Client, p); err != nil { - log.Error(err, "Failed to set pipeline status") - return - } - return - } - - if err != nil { - log.Error(err, "Configcheck error") - return - } - - if err = pipeline.SetSuccessStatus(ctx, r.Client, p); err != nil { - log.Error(err, "Failed to set pipeline status") - return - } - - if err = pipeline.SetLastAppliedPipelineStatus(ctx, r.Client, p); err != nil { - log.Error(err, "Failed to set pipeline status") - return - } - // Start vector reconcilation - if *p.GetConfigCheckResult() { - VectorAgentReconciliationSourceChannel <- event.GenericEvent{Object: vaCtrl.Vector} - } -} diff --git a/internal/pipeline/pipeline.go b/internal/pipeline/pipeline.go index 39c02c0c..58a9bdf6 100644 --- a/internal/pipeline/pipeline.go +++ b/internal/pipeline/pipeline.go @@ -36,7 +36,7 @@ type Pipeline interface { UpdateStatus(context.Context, client.Client) error } -func GetValidPipelines(ctx context.Context, client client.Client) ([]Pipeline, error) { +func GetValidPipelines(ctx context.Context, client client.Client, selector vectorv1alpha1.VectorSelectorSpec) ([]Pipeline, error) { var validPipelines []Pipeline vps, err := GetVectorPipelines(ctx, client) if err != nil { @@ -48,14 +48,14 @@ func GetValidPipelines(ctx context.Context, client client.Client) ([]Pipeline, e } if len(vps) != 0 { for _, vp := range vps { - if !vp.IsDeleted() && vp.IsValid() { + if !vp.IsDeleted() && vp.IsValid() && MatchLabels(selector.MatchLabels, vp.Labels) { validPipelines = append(validPipelines, vp.DeepCopy()) } } } if len(cvps) != 0 { for _, cvp := range cvps { - if !cvp.IsDeleted() && cvp.IsValid() { + if !cvp.IsDeleted() && cvp.IsValid() && MatchLabels(selector.MatchLabels, cvp.Labels) { validPipelines = append(validPipelines, cvp.DeepCopy()) } } @@ -66,6 +66,11 @@ func GetValidPipelines(ctx context.Context, client client.Client) ([]Pipeline, e func SetSuccessStatus(ctx context.Context, client client.Client, p Pipeline) error { p.SetConfigCheck(true) p.SetReason(nil) + hash, err := GetSpecHash(p) + if err != nil { + return err + } + p.SetLastAppliedPipeline(hash) return p.UpdateStatus(ctx, client) } @@ -74,11 +79,6 @@ func SetFailedStatus(ctx context.Context, client client.Client, p Pipeline, reas p.SetConfigCheck(false) p.SetReason(&reason) - - return p.UpdateStatus(ctx, client) -} - -func SetLastAppliedPipelineStatus(ctx context.Context, client client.Client, p Pipeline) error { hash, err := GetSpecHash(p) if err != nil { return err @@ -103,3 +103,15 @@ func GetClusterVectorPipelines(ctx context.Context, client client.Client) ([]vec } return cvps.Items, nil } + +func MatchLabels(selector map[string]string, labels map[string]string) bool { + if selector == nil { + return true + } + for k, v := range selector { + if labels[k] != v { + return false + } + } + return true +} diff --git a/internal/pipeline/pipeline_test.go b/internal/pipeline/pipeline_test.go new file mode 100644 index 00000000..d04adde6 --- /dev/null +++ b/internal/pipeline/pipeline_test.go @@ -0,0 +1,59 @@ +package pipeline + +import ( + "testing" +) + +func TestMatchLabels(t *testing.T) { + tests := []struct { + name string + selector map[string]string + labels map[string]string + want bool + }{ + { + name: "NoSelector", + selector: nil, + labels: map[string]string{"label1": "value1", "label2": "value2"}, + want: true, + }, + { + name: "MatchingLabels", + selector: map[string]string{"label1": "value1", "label2": "value2"}, + labels: map[string]string{"label1": "value1", "label2": "value2"}, + want: true, + }, + { + name: "MismatchedLabelValues", + selector: map[string]string{"label1": "value1", "label2": "value2"}, + labels: map[string]string{"label1": "value1", "label2": "mismatch"}, + want: false, + }, + { + name: "ExtraLabelsInMap", + selector: map[string]string{"label1": "value1"}, + labels: map[string]string{"label1": "value1", "label2": "value2"}, + want: true, + }, + { + name: "SelectorWithNoMatches", + selector: map[string]string{"label1": "value1", "label2": "value2"}, + labels: map[string]string{"label3": "value3"}, + want: false, + }, + { + name: "SelectorWithNoMatches2", + selector: map[string]string{"label1": "value1", "label2": "value2"}, + labels: map[string]string{"label1": "label1"}, + want: false, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + if got := MatchLabels(test.selector, test.labels); got != test.want { + t.Errorf("MatchLabels() = %v, want %v", got, test.want) + } + }) + } +} diff --git a/internal/vector/vectoragent/vectoragent.go b/internal/vector/vectoragent/vectoragent.go index 4816d880..462b6332 100644 --- a/internal/vector/vectoragent/vectoragent.go +++ b/internal/vector/vectoragent/vectoragent.go @@ -35,17 +35,20 @@ type Controller struct { } func NewController(v *vectorv1alpha1.Vector, c client.Client, cs *kubernetes.Clientset) *Controller { - return &Controller{ + ctrl := &Controller{ Client: c, Vector: v, ClientSet: cs, } + ctrl.SetDefault() + return ctrl } -func (ctrl *Controller) SetSuccessStatus(ctx context.Context) error { +func (ctrl *Controller) SetSuccessStatus(ctx context.Context, hash *uint32) error { var status = true ctrl.Vector.Status.ConfigCheckResult = &status ctrl.Vector.Status.Reason = nil + ctrl.Vector.Status.LastAppliedConfigHash = hash return k8s.UpdateStatus(ctx, ctrl.Vector, ctrl.Client) } @@ -57,10 +60,3 @@ func (ctrl *Controller) SetFailedStatus(ctx context.Context, reason string) erro return k8s.UpdateStatus(ctx, ctrl.Vector, ctrl.Client) } - -func (ctrl *Controller) SetLastAppliedPipelineStatus(ctx context.Context, hash *uint32) error { - - ctrl.Vector.Status.LastAppliedConfigHash = hash - - return k8s.UpdateStatus(ctx, ctrl.Vector, ctrl.Client) -} From 24fd6422d84fa9137bf7aab02b261f04ddcad165 Mon Sep 17 00:00:00 2001 From: aa1ex <115736127+aa1ex@users.noreply.github.com> Date: Fri, 27 Sep 2024 17:38:01 +0300 Subject: [PATCH 270/316] Vector aggregator api (#149) add aggregator logic Signed-off-by: Aleksandr Aleksandrov --- PROJECT | 9 + api/v1alpha1/clustervectorpipeline.go | 20 +- api/v1alpha1/clustervectorpipeline_types.go | 1 + api/v1alpha1/vector_common_types.go | 133 + api/v1alpha1/vector_types.go | 140 +- api/v1alpha1/vectoraggregator_types.go | 74 + api/v1alpha1/vectorpipeline.go | 24 + api/v1alpha1/vectorpipeline_types.go | 8 +- api/v1alpha1/zz_generated.deepcopy.go | 168 +- cmd/main.go | 38 +- ...ity.kaasops.io_clustervectorpipelines.yaml | 5 + ...vability.kaasops.io_vectoraggregators.yaml | 4961 +++++++++++++++++ ...ervability.kaasops.io_vectorpipelines.yaml | 5 + .../observability.kaasops.io_vectors.yaml | 2 +- config/crd/kustomization.yaml | 2 + config/rbac/kustomization.yaml | 2 + config/rbac/role.yaml | 4 + config/rbac/vectoraggregator_editor_role.yaml | 27 + config/rbac/vectoraggregator_viewer_role.yaml | 23 + config/samples/kustomization.yaml | 1 + ...servability_v1alpha1_vectoraggregator.yaml | 9 + go.mod | 4 +- internal/common/annotations.go | 5 + internal/config/agent.go | 91 + internal/config/aggregator.go | 113 + internal/config/config.go | 167 +- internal/config/configcheck/configcheck.go | 46 +- .../config/configcheck/configcheck_config.go | 4 +- internal/config/default.go | 38 +- internal/config/types.go | 43 +- internal/config/vector_source_types.go | 99 + internal/controller/pipeline_controller.go | 152 +- internal/controller/suite_test.go | 5 +- internal/controller/vector_controller.go | 29 +- .../controller/vectoraggregator_controller.go | 277 + .../vectoraggregator_controller_test.go | 100 + internal/pipeline/hash.go | 23 +- internal/pipeline/pipeline.go | 91 +- internal/vector/aggregator/config.go | 38 + internal/vector/aggregator/controller.go | 259 + internal/vector/aggregator/deployment.go | 268 + internal/vector/aggregator/podmonitor.go | 38 + internal/vector/aggregator/rbac.go | 100 + internal/vector/aggregator/service.go | 113 + 44 files changed, 7372 insertions(+), 387 deletions(-) create mode 100644 api/v1alpha1/vector_common_types.go create mode 100644 api/v1alpha1/vectoraggregator_types.go create mode 100644 config/crd/bases/observability.kaasops.io_vectoraggregators.yaml create mode 100644 config/rbac/vectoraggregator_editor_role.yaml create mode 100644 config/rbac/vectoraggregator_viewer_role.yaml create mode 100644 config/samples/observability_v1alpha1_vectoraggregator.yaml create mode 100644 internal/common/annotations.go create mode 100644 internal/config/agent.go create mode 100644 internal/config/aggregator.go create mode 100644 internal/config/vector_source_types.go create mode 100644 internal/controller/vectoraggregator_controller.go create mode 100644 internal/controller/vectoraggregator_controller_test.go create mode 100644 internal/vector/aggregator/config.go create mode 100644 internal/vector/aggregator/controller.go create mode 100644 internal/vector/aggregator/deployment.go create mode 100644 internal/vector/aggregator/podmonitor.go create mode 100644 internal/vector/aggregator/rbac.go create mode 100644 internal/vector/aggregator/service.go diff --git a/PROJECT b/PROJECT index c04525fc..6675ee1b 100644 --- a/PROJECT +++ b/PROJECT @@ -34,4 +34,13 @@ resources: kind: ClusterVectorPipeline path: github.com/kaasops/vector-operator/api/v1alpha1 version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: kaasops.io + group: observability + kind: VectorAggregator + path: github.com/kaasops/vector-operator/api/v1alpha1 + version: v1alpha1 version: "3" diff --git a/api/v1alpha1/clustervectorpipeline.go b/api/v1alpha1/clustervectorpipeline.go index be1bafec..da07cbe8 100644 --- a/api/v1alpha1/clustervectorpipeline.go +++ b/api/v1alpha1/clustervectorpipeline.go @@ -2,15 +2,12 @@ package v1alpha1 import ( "context" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/kaasops/vector-operator/internal/utils/k8s" "sigs.k8s.io/controller-runtime/pkg/client" ) -var ( - ClusterPipelineKind = "ClusterVectorPipeline" -) - func (vp *ClusterVectorPipeline) GetSpec() VectorPipelineSpec { return vp.Spec } @@ -49,3 +46,18 @@ func (vp *ClusterVectorPipeline) SetLastAppliedPipeline(hash *uint32) { func (vp *ClusterVectorPipeline) UpdateStatus(ctx context.Context, c client.Client) error { return k8s.UpdateStatus(ctx, vp, c) } + +func (vp *ClusterVectorPipeline) GetRole() VectorPipelineRole { + if vp.Status.Role == nil { + return VectorPipelineRoleUnknown + } + return *vp.Status.Role +} + +func (vp *ClusterVectorPipeline) SetRole(role *VectorPipelineRole) { + vp.Status.Role = role +} + +func (vp *ClusterVectorPipeline) GetTypeMeta() metav1.TypeMeta { + return vp.TypeMeta +} diff --git a/api/v1alpha1/clustervectorpipeline_types.go b/api/v1alpha1/clustervectorpipeline_types.go index 898aaf52..a9049b54 100644 --- a/api/v1alpha1/clustervectorpipeline_types.go +++ b/api/v1alpha1/clustervectorpipeline_types.go @@ -28,6 +28,7 @@ import ( //+kubebuilder:resource:scope=Cluster,shortName=cvp //+kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" //+kubebuilder:printcolumn:name="Valid",type="boolean",JSONPath=".status.configCheckResult" +//+kubebuilder:printcolumn:name="Role",type="boolean",JSONPath=".status.role" // ClusterVectorPipeline is the Schema for the clustervectorpipelines API type ClusterVectorPipeline struct { diff --git a/api/v1alpha1/vector_common_types.go b/api/v1alpha1/vector_common_types.go new file mode 100644 index 00000000..3544340d --- /dev/null +++ b/api/v1alpha1/vector_common_types.go @@ -0,0 +1,133 @@ +package v1alpha1 + +import v1 "k8s.io/api/core/v1" + +type VectorCommonStatus struct { + ConfigCheckResult *bool `json:"configCheckResult,omitempty"` + Reason *string `json:"reason,omitempty"` + LastAppliedConfigHash *uint32 `json:"LastAppliedConfigHash,omitempty"` +} + +type VectorCommon struct { + // Image - docker image settings for Vector Agent + // if no specified operator uses default config version + // +optional + Image string `json:"image,omitempty"` + // ImagePullSecrets An optional list of references to secrets in the same namespace + // to use for pulling images from registries + // see http://kubernetes.io/docs/user-guide/images#specifying-imagepullsecrets-on-a-pod + // +optional + ImagePullSecrets []v1.LocalObjectReference `json:"imagePullSecrets,omitempty"` + // ImagePullPolicy of pods + ImagePullPolicy v1.PullPolicy `json:"imagePullPolicy,omitempty"` + // Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + // +optional + Annotations map[string]string `json:"annotations,omitempty"` + // Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + // if not specified - default setting will be used + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Resources",xDescriptors="urn:alm:descriptor:com.tectonic.ui:resourceRequirements" + // +optional + Resources v1.ResourceRequirements `json:"resources,omitempty"` + // Affinity If specified, the pod's scheduling constraints. + // +optional + Affinity *v1.Affinity `json:"affinity,omitempty"` + // Tolerations If specified, the pod's tolerations. + // +optional + Tolerations []v1.Toleration `json:"tolerations,omitempty"` + // SecurityContext holds pod-level security attributes and common container settings. + // This defaults to the default PodSecurityContext. + // +optional + SecurityContext *v1.PodSecurityContext `json:"podSecurityContext,omitempty"` + // SecurityContext holds security configuration that will be applied to a container. + // Some fields are present in both SecurityContext and PodSecurityContext. + // When both are set, the values in SecurityContext take precedence. + // +optional + ContainerSecurityContext *v1.SecurityContext `json:"containerSecurityContext,omitempty"` + // SchedulerName - defines kubernetes scheduler name + // +optional + SchedulerName string `json:"schedulerName,omitempty"` + // RuntimeClassName - defines runtime class for kubernetes pod. + // https://kubernetes.io/docs/concepts/containers/runtime-class/ + RuntimeClassName *string `json:"runtimeClassName,omitempty"` + // HostAliases provides mapping between ip and hostnames, + // that would be propagated to pod, + // cannot be used with HostNetwork. + // +optional + HostAliases []v1.HostAlias `json:"host_aliases,omitempty"` + // PodSecurityPolicyName - defines name for podSecurityPolicy + // in case of empty value, prefixedName will be used. + // +optional + PodSecurityPolicyName string `json:"podSecurityPolicyName,omitempty"` + // PriorityClassName assigned to the Pods + // +optional + PriorityClassName string `json:"priorityClassName,omitempty"` + // HostNetwork controls whether the pod may use the node network namespace + // +optional + HostNetwork bool `json:"hostNetwork,omitempty"` + // Env that will be added to Vector pod + Env []v1.EnvVar `json:"env,omitempty"` + // The directory used for persisting Vector state, such as on-disk buffers, file checkpoints, and more. Please make sure the Vector project has write permissions to this directory. + // https://vector.dev/docs/reference/configuration/global-options/#data_dir + DataDir string `json:"dataDir,omitempty"` + // Vector API params. Allows to interact with a running Vector instance. + // https://vector.dev/docs/reference/api/ + Api ApiSpec `json:"api,omitempty"` + // Enable internal metrics exporter + // +optional + InternalMetrics bool `json:"internalMetrics,omitempty"` + // List of volumes that can be mounted by containers belonging to the pod. + // +optional + Volumes []v1.Volume `json:"volumes,omitempty"` + // Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. + // +optional + ReadinessProbe *v1.Probe `json:"readinessProbe,omitempty"` + // Periodic probe of container liveness. Container will be restarted if the probe fails. + // +optional + LivenessProbe *v1.Probe `json:"livenessProbe,omitempty"` + // Pod volumes to mount into the container's filesystem. + // +optional + VolumeMounts []v1.VolumeMount `json:"volumeMounts,omitempty"` + // Control params for ConfigCheck pods + ConfigCheck ConfigCheck `json:"configCheck,omitempty"` + // Compress config file to fix: metadata.annotations: Too long: must have at most 262144 characters + CompressConfigFile bool `json:"compressConfigFile,omitempty"` + ConfigReloaderImage string `json:"configReloaderImage,omitempty"` + ConfigReloaderResources v1.ResourceRequirements `json:"configReloaderResources,omitempty"` +} + +// ApiSpec is the Schema for the Vector Agent GraphQL API - https://vector.dev/docs/reference/api/ +type ApiSpec struct { + Enabled bool `json:"enabled,omitempty"` + Playground bool `json:"playground,omitempty"` + // Enable ReadinessProbe and LivenessProbe via api /health endpoint. + // If probe enabled via VectorAgent or VectorAggregator, this setting will be ignored for that probe. + // +optional + Healthcheck bool `json:"healthcheck,omitempty"` +} + +// ConfigCheck is the Schema for control params for ConfigCheck pods +type ConfigCheck struct { + Disabled bool `json:"disabled,omitempty"` + // Image - docker image settings for Vector Agent + // if no specified operator uses default config version + // +optional + Image *string `json:"image,omitempty"` + // Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + // if not specified - default setting will be used + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Resources",xDescriptors="urn:alm:descriptor:com.tectonic.ui:resourceRequirements" + // +optional + Resources *v1.ResourceRequirements `json:"resources,omitempty"` + // Affinity If specified, the pod's scheduling constraints. + // +optional + Affinity *v1.Affinity `json:"affinity,omitempty"` + // Tolerations If specified, the pod's tolerations. + // +optional + Tolerations *[]v1.Toleration `json:"tolerations,omitempty"` + // Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. + // +optional + Annotations map[string]string `json:"annotations,omitempty"` +} + +type VectorSelectorSpec struct { + MatchLabels map[string]string `json:"matchLabels,omitempty"` +} diff --git a/api/v1alpha1/vector_types.go b/api/v1alpha1/vector_types.go index 080a59d6..c4dbf703 100644 --- a/api/v1alpha1/vector_types.go +++ b/api/v1alpha1/vector_types.go @@ -17,13 +17,9 @@ limitations under the License. package v1alpha1 import ( - v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. - // VectorSpec defines the desired state of Vector type VectorSpec struct { // Vector Agent @@ -32,147 +28,17 @@ type VectorSpec struct { UseApiServerCache bool `json:"useApiServerCache,omitempty"` // Defines a filter for the Vector Pipeline and Cluster Vector Pipeline by labels. // If not specified, all pipelines will be selected. - Selector VectorSelectorSpec `json:"selector,omitempty"` -} - -type VectorSelectorSpec struct { - MatchLabels map[string]string `json:"matchLabels,omitempty"` + Selector *VectorSelectorSpec `json:"selector,omitempty"` } // VectorStatus defines the observed state of Vector type VectorStatus struct { - ConfigCheckResult *bool `json:"configCheckResult,omitempty"` - Reason *string `json:"reason,omitempty"` - LastAppliedConfigHash *uint32 `json:"LastAppliedConfigHash,omitempty"` + VectorCommonStatus `json:",inline"` } // VectorAgent is the Schema for the Vector Agent type VectorAgent struct { - // Image - docker image settings for Vector Agent - // if no specified operator uses default config version - // +optional - Image string `json:"image,omitempty"` - // ImagePullSecrets An optional list of references to secrets in the same namespace - // to use for pulling images from registries - // see http://kubernetes.io/docs/user-guide/images#specifying-imagepullsecrets-on-a-pod - // +optional - ImagePullSecrets []v1.LocalObjectReference `json:"imagePullSecrets,omitempty"` - // ImagePullPolicy of pods - ImagePullPolicy v1.PullPolicy `json:"imagePullPolicy,omitempty"` - // Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. - // +optional - Annotations map[string]string `json:"annotations,omitempty"` - // Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - // if not specified - default setting will be used - // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Resources",xDescriptors="urn:alm:descriptor:com.tectonic.ui:resourceRequirements" - // +optional - Resources v1.ResourceRequirements `json:"resources,omitempty"` - // Affinity If specified, the pod's scheduling constraints. - // +optional - Affinity *v1.Affinity `json:"affinity,omitempty"` - // Tolerations If specified, the pod's tolerations. - // +optional - Tolerations []v1.Toleration `json:"tolerations,omitempty"` - // SecurityContext holds pod-level security attributes and common container settings. - // This defaults to the default PodSecurityContext. - // +optional - SecurityContext *v1.PodSecurityContext `json:"podSecurityContext,omitempty"` - // SecurityContext holds security configuration that will be applied to a container. - // Some fields are present in both SecurityContext and PodSecurityContext. - // When both are set, the values in SecurityContext take precedence. - // +optional - ContainerSecurityContext *v1.SecurityContext `json:"containerSecurityContext,omitempty"` - // SchedulerName - defines kubernetes scheduler name - // +optional - SchedulerName string `json:"schedulerName,omitempty"` - // RuntimeClassName - defines runtime class for kubernetes pod. - // https://kubernetes.io/docs/concepts/containers/runtime-class/ - RuntimeClassName *string `json:"runtimeClassName,omitempty"` - // HostAliases provides mapping between ip and hostnames, - // that would be propagated to pod, - // cannot be used with HostNetwork. - // +optional - HostAliases []v1.HostAlias `json:"host_aliases,omitempty"` - // PodSecurityPolicyName - defines name for podSecurityPolicy - // in case of empty value, prefixedName will be used. - // +optional - PodSecurityPolicyName string `json:"podSecurityPolicyName,omitempty"` - // PriorityClassName assigned to the Pods - // +optional - PriorityClassName string `json:"priorityClassName,omitempty"` - // HostNetwork controls whether the pod may use the node network namespace - // +optional - HostNetwork bool `json:"hostNetwork,omitempty"` - // Env that will be added to Vector pod - Env []v1.EnvVar `json:"env,omitempty"` - // The directory used for persisting Vector state, such as on-disk buffers, file checkpoints, and more. Please make sure the Vector project has write permissions to this directory. - // https://vector.dev/docs/reference/configuration/global-options/#data_dir - DataDir string `json:"dataDir,omitempty"` - // Vector API params. Allows to interact with a running Vector instance. - // https://vector.dev/docs/reference/api/ - Api ApiSpec `json:"api,omitempty"` - // Enable internal metrics exporter - // +optional - InternalMetrics bool `json:"internalMetrics,omitempty"` - // List of volumes that can be mounted by containers belonging to the pod. - // +optional - Volumes []v1.Volume `json:"volumes,omitempty"` - // Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. - // +optional - ReadinessProbe *v1.Probe `json:"readinessProbe,omitempty"` - // Periodic probe of container liveness. Container will be restarted if the probe fails. - // +optional - LivenessProbe *v1.Probe `json:"livenessProbe,omitempty"` - // Pod volumes to mount into the container's filesystem. - // +optional - VolumeMounts []v1.VolumeMount `json:"volumeMounts,omitempty"` - // Control params for ConfigCheck pods - ConfigCheck ConfigCheck `json:"configCheck,omitempty"` - // Compress config file to fix: metadata.annotations: Too long: must have at most 262144 characters - CompressConfigFile bool `json:"compressConfigFile,omitempty"` - ConfigReloaderImage string `json:"configReloaderImage,omitempty"` - ConfigReloaderResources v1.ResourceRequirements `json:"configReloaderResources,omitempty"` -} - -// ApiSpec is the Schema for the Vector Agent GraphQL API - https://vector.dev/docs/reference/api/ -type ApiSpec struct { - Enabled bool `json:"enabled,omitempty"` - Playground bool `json:"playground,omitempty"` - // Enable ReadinessProbe and LivenessProbe via api /health endpoint. - // If probe enabled via VectorAgent, this setting will be ignored for that probe. - // +optional - Healthcheck bool `json:"healthcheck,omitempty"` -} - -// ConfigCheck is the Schema for control params for ConfigCheck pods -type ConfigCheck struct { - Disabled bool `json:"disabled,omitempty"` - // Image - docker image settings for Vector Agent - // if no specified operator uses default config version - // +optional - Image *string `json:"image,omitempty"` - // Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - // if not specified - default setting will be used - // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Resources",xDescriptors="urn:alm:descriptor:com.tectonic.ui:resourceRequirements" - // +optional - Resources *v1.ResourceRequirements `json:"resources,omitempty"` - // Affinity If specified, the pod's scheduling constraints. - // +optional - Affinity *v1.Affinity `json:"affinity,omitempty"` - // Tolerations If specified, the pod's tolerations. - // +optional - Tolerations *[]v1.Toleration `json:"tolerations,omitempty"` - // Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. - // +optional - Annotations map[string]string `json:"annotations,omitempty"` -} - -// VectorAggregator is the Schema for the Vector Aggregator -type VectorAggregator struct { - Enable bool `json:"enable,omitempty"` - // +kubebuilder:default:="timberio/vector:0.24.0-distroless-libc" - Image string `json:"image,omitempty"` - Replicas int `json:"replicas,omitempty"` + VectorCommon `json:",inline"` } //+kubebuilder:object:root=true diff --git a/api/v1alpha1/vectoraggregator_types.go b/api/v1alpha1/vectoraggregator_types.go new file mode 100644 index 00000000..66711d52 --- /dev/null +++ b/api/v1alpha1/vectoraggregator_types.go @@ -0,0 +1,74 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// VectorAggregatorSpec defines the desired state of VectorAggregator +type VectorAggregatorSpec struct { + VectorCommon `json:",inline"` + Replicas int32 `json:"replicas,omitempty"` + // Defines a filter for the Vector Pipeline and Cluster Vector Pipeline by labels. + // If not specified, all pipelines will be selected. + Selector *VectorSelectorSpec `json:"selector,omitempty"` +} + +// VectorAggregatorStatus defines the observed state of VectorAggregator +type VectorAggregatorStatus struct { + VectorCommonStatus `json:",inline"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" +// +kubebuilder:printcolumn:name="Valid",type="boolean",JSONPath=".status.configCheckResult" + +// VectorAggregator is the Schema for the vectoraggregators API +type VectorAggregator struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec VectorAggregatorSpec `json:"spec,omitempty"` + Status VectorAggregatorStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// VectorAggregatorList contains a list of VectorAggregator +type VectorAggregatorList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []VectorAggregator `json:"items"` +} + +func init() { + SchemeBuilder.Register(&VectorAggregator{}, &VectorAggregatorList{}) +} + +func (v *VectorAggregator) IsBeingDeleted() bool { + return !v.ObjectMeta.DeletionTimestamp.IsZero() +} + +func (v *VectorAggregator) HasFinalizer(finalizerName string) bool { + return controllerutil.ContainsFinalizer(v, finalizerName) +} diff --git a/api/v1alpha1/vectorpipeline.go b/api/v1alpha1/vectorpipeline.go index 9a30b01b..5fa963c0 100644 --- a/api/v1alpha1/vectorpipeline.go +++ b/api/v1alpha1/vectorpipeline.go @@ -2,11 +2,20 @@ package v1alpha1 import ( "context" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/kaasops/vector-operator/internal/utils/k8s" "sigs.k8s.io/controller-runtime/pkg/client" ) +type VectorPipelineRole string + +const ( + VectorPipelineRoleUnknown VectorPipelineRole = "unknown" + VectorPipelineRoleAgent VectorPipelineRole = "agent" + VectorPipelineRoleAggregator VectorPipelineRole = "aggregator" +) + func (vp *VectorPipeline) GetSpec() VectorPipelineSpec { return vp.Spec } @@ -45,3 +54,18 @@ func (vp *VectorPipeline) SetLastAppliedPipeline(hash *uint32) { func (vp *VectorPipeline) UpdateStatus(ctx context.Context, c client.Client) error { return k8s.UpdateStatus(ctx, vp, c) } + +func (vp *VectorPipeline) GetRole() VectorPipelineRole { + if vp.Status.Role == nil { + return VectorPipelineRoleUnknown + } + return *vp.Status.Role +} + +func (vp *VectorPipeline) SetRole(role *VectorPipelineRole) { + vp.Status.Role = role +} + +func (vp *VectorPipeline) GetTypeMeta() metav1.TypeMeta { + return vp.TypeMeta +} diff --git a/api/v1alpha1/vectorpipeline_types.go b/api/v1alpha1/vectorpipeline_types.go index 4c689b4a..49b9c07a 100644 --- a/api/v1alpha1/vectorpipeline_types.go +++ b/api/v1alpha1/vectorpipeline_types.go @@ -36,9 +36,10 @@ type VectorPipelineSpec struct { // VectorPipelineStatus defines the observed state of VectorPipeline type VectorPipelineStatus struct { - ConfigCheckResult *bool `json:"configCheckResult,omitempty"` - Reason *string `json:"reason,omitempty"` - LastAppliedPipelineHash *uint32 `json:"LastAppliedPipelineHash,omitempty"` + Role *VectorPipelineRole `json:"role,omitempty"` + ConfigCheckResult *bool `json:"configCheckResult,omitempty"` + Reason *string `json:"reason,omitempty"` + LastAppliedPipelineHash *uint32 `json:"LastAppliedPipelineHash,omitempty"` } //+kubebuilder:object:root=true @@ -46,6 +47,7 @@ type VectorPipelineStatus struct { //+kubebuilder:resource:shortName=vp,categories=all //+kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" //+kubebuilder:printcolumn:name="Valid",type="boolean",JSONPath=".status.configCheckResult" +//+kubebuilder:printcolumn:name="Role",type="boolean",JSONPath=".status.role" // VectorPipeline is the Schema for the vectorpipelines API type VectorPipeline struct { diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index a57c6cd9..591c6cdd 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -176,6 +176,118 @@ func (in *Vector) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VectorAgent) DeepCopyInto(out *VectorAgent) { + *out = *in + in.VectorCommon.DeepCopyInto(&out.VectorCommon) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VectorAgent. +func (in *VectorAgent) DeepCopy() *VectorAgent { + if in == nil { + return nil + } + out := new(VectorAgent) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VectorAggregator) DeepCopyInto(out *VectorAggregator) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VectorAggregator. +func (in *VectorAggregator) DeepCopy() *VectorAggregator { + if in == nil { + return nil + } + out := new(VectorAggregator) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *VectorAggregator) 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 *VectorAggregatorList) DeepCopyInto(out *VectorAggregatorList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]VectorAggregator, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VectorAggregatorList. +func (in *VectorAggregatorList) DeepCopy() *VectorAggregatorList { + if in == nil { + return nil + } + out := new(VectorAggregatorList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *VectorAggregatorList) 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 *VectorAggregatorSpec) DeepCopyInto(out *VectorAggregatorSpec) { + *out = *in + in.VectorCommon.DeepCopyInto(&out.VectorCommon) + if in.Selector != nil { + in, out := &in.Selector, &out.Selector + *out = new(VectorSelectorSpec) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VectorAggregatorSpec. +func (in *VectorAggregatorSpec) DeepCopy() *VectorAggregatorSpec { + if in == nil { + return nil + } + out := new(VectorAggregatorSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VectorAggregatorStatus) DeepCopyInto(out *VectorAggregatorStatus) { + *out = *in + in.VectorCommonStatus.DeepCopyInto(&out.VectorCommonStatus) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VectorAggregatorStatus. +func (in *VectorAggregatorStatus) DeepCopy() *VectorAggregatorStatus { + if in == nil { + return nil + } + out := new(VectorAggregatorStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VectorCommon) DeepCopyInto(out *VectorCommon) { *out = *in if in.ImagePullSecrets != nil { in, out := &in.ImagePullSecrets, &out.ImagePullSecrets @@ -260,27 +372,42 @@ func (in *VectorAgent) DeepCopyInto(out *VectorAgent) { in.ConfigReloaderResources.DeepCopyInto(&out.ConfigReloaderResources) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VectorAgent. -func (in *VectorAgent) DeepCopy() *VectorAgent { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VectorCommon. +func (in *VectorCommon) DeepCopy() *VectorCommon { if in == nil { return nil } - out := new(VectorAgent) + out := new(VectorCommon) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *VectorAggregator) DeepCopyInto(out *VectorAggregator) { +func (in *VectorCommonStatus) DeepCopyInto(out *VectorCommonStatus) { *out = *in + if in.ConfigCheckResult != nil { + in, out := &in.ConfigCheckResult, &out.ConfigCheckResult + *out = new(bool) + **out = **in + } + if in.Reason != nil { + in, out := &in.Reason, &out.Reason + *out = new(string) + **out = **in + } + if in.LastAppliedConfigHash != nil { + in, out := &in.LastAppliedConfigHash, &out.LastAppliedConfigHash + *out = new(uint32) + **out = **in + } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VectorAggregator. -func (in *VectorAggregator) DeepCopy() *VectorAggregator { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VectorCommonStatus. +func (in *VectorCommonStatus) DeepCopy() *VectorCommonStatus { if in == nil { return nil } - out := new(VectorAggregator) + out := new(VectorCommonStatus) in.DeepCopyInto(out) return out } @@ -409,6 +536,11 @@ func (in *VectorPipelineSpec) DeepCopy() *VectorPipelineSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VectorPipelineStatus) DeepCopyInto(out *VectorPipelineStatus) { *out = *in + if in.Role != nil { + in, out := &in.Role, &out.Role + *out = new(VectorPipelineRole) + **out = **in + } if in.ConfigCheckResult != nil { in, out := &in.ConfigCheckResult, &out.ConfigCheckResult *out = new(bool) @@ -466,7 +598,11 @@ func (in *VectorSpec) DeepCopyInto(out *VectorSpec) { *out = new(VectorAgent) (*in).DeepCopyInto(*out) } - in.Selector.DeepCopyInto(&out.Selector) + if in.Selector != nil { + in, out := &in.Selector, &out.Selector + *out = new(VectorSelectorSpec) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VectorSpec. @@ -482,21 +618,7 @@ func (in *VectorSpec) DeepCopy() *VectorSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VectorStatus) DeepCopyInto(out *VectorStatus) { *out = *in - if in.ConfigCheckResult != nil { - in, out := &in.ConfigCheckResult, &out.ConfigCheckResult - *out = new(bool) - **out = **in - } - if in.Reason != nil { - in, out := &in.Reason, &out.Reason - *out = new(string) - **out = **in - } - if in.LastAppliedConfigHash != nil { - in, out := &in.LastAppliedConfigHash, &out.LastAppliedConfigHash - *out = new(uint32) - **out = **in - } + in.VectorCommonStatus.DeepCopyInto(&out.VectorCommonStatus) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VectorStatus. diff --git a/cmd/main.go b/cmd/main.go index 1dd3b938..f80cc533 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -21,18 +21,20 @@ import ( "crypto/tls" "flag" "fmt" - "github.com/kaasops/vector-operator/internal/utils/k8s" + "os" + "time" + monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/client-go/discovery" "k8s.io/client-go/kubernetes" - "os" "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/event" - "time" + + "github.com/kaasops/vector-operator/internal/utils/k8s" // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) // to ensure that exec-entrypoint and run can make use of them. @@ -201,22 +203,40 @@ func main() { os.Exit(1) } - vectorPipelineEventCh := make(chan event.GenericEvent, 10) - defer close(vectorPipelineEventCh) + vectorAgentsPipelineEventCh := make(chan event.GenericEvent, 10) + defer close(vectorAgentsPipelineEventCh) + vectorAggregatorsPipelineEventCh := make(chan event.GenericEvent, 10) + defer close(vectorAggregatorsPipelineEventCh) if err = (&controller.PipelineReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Clientset: clientset, + ConfigCheckTimeout: configCheckTimeout, + VectorAgentEventCh: vectorAgentsPipelineEventCh, + VectorAggregatorsEventCh: vectorAggregatorsPipelineEventCh, + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "VectorPipeline") + os.Exit(1) + } + + vectorAggregatorsEventCh := make(chan event.GenericEvent) + defer close(vectorAggregatorsEventCh) + + if err = (&controller.VectorAggregatorReconciler{ Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), Clientset: clientset, + Scheme: mgr.GetScheme(), ConfigCheckTimeout: configCheckTimeout, - VectorAgentEventCh: vectorPipelineEventCh, + EventChan: vectorAggregatorsEventCh, }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "VectorPipeline") + setupLog.Error(err, "unable to create controller", "controller", "VectorAggregator") os.Exit(1) } // +kubebuilder:scaffold:builder - go reconcileWithDelay(context.Background(), vectorPipelineEventCh, vectorAgentEventCh, time.Second*10) + go reconcileWithDelay(context.Background(), vectorAgentsPipelineEventCh, vectorAgentEventCh, time.Second*10) + go reconcileWithDelay(context.Background(), vectorAggregatorsPipelineEventCh, vectorAggregatorsEventCh, time.Second*10) if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { setupLog.Error(err, "unable to set up health check") diff --git a/config/crd/bases/observability.kaasops.io_clustervectorpipelines.yaml b/config/crd/bases/observability.kaasops.io_clustervectorpipelines.yaml index bc1f9bb1..994403bd 100644 --- a/config/crd/bases/observability.kaasops.io_clustervectorpipelines.yaml +++ b/config/crd/bases/observability.kaasops.io_clustervectorpipelines.yaml @@ -23,6 +23,9 @@ spec: - jsonPath: .status.configCheckResult name: Valid type: boolean + - jsonPath: .status.role + name: Role + type: boolean name: v1alpha1 schema: openAPIV3Schema: @@ -69,6 +72,8 @@ spec: type: boolean reason: type: string + role: + type: string type: object type: object served: true diff --git a/config/crd/bases/observability.kaasops.io_vectoraggregators.yaml b/config/crd/bases/observability.kaasops.io_vectoraggregators.yaml new file mode 100644 index 00000000..08367ca6 --- /dev/null +++ b/config/crd/bases/observability.kaasops.io_vectoraggregators.yaml @@ -0,0 +1,4961 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: vectoraggregators.observability.kaasops.io +spec: + group: observability.kaasops.io + names: + kind: VectorAggregator + listKind: VectorAggregatorList + plural: vectoraggregators + singular: vectoraggregator + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.configCheckResult + name: Valid + type: boolean + name: v1alpha1 + schema: + openAPIV3Schema: + description: VectorAggregator is the Schema for the vectoraggregators API + 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: + type: object + spec: + description: VectorAggregatorSpec defines the desired state of VectorAggregator + properties: + affinity: + description: Affinity If specified, the pod's scheduling constraints. + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for the + pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. + items: + description: |- + An empty preferred scheduling term matches all objects with implicit weight 0 + (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated with the + corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + description: Weight associated with matching the corresponding + nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. + The terms are ORed. + items: + description: |- + A null or empty node selector term matches no objects. The requirements of + them are ANDed. + The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate + this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules (e.g. + avoid putting this pod in the same node, zone, etc. as some + other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the anti-affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the anti-affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the anti-affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + annotations: + additionalProperties: + type: string + description: Annotations is an unstructured key value map stored with + a resource that may be set by external tools to store and retrieve + arbitrary metadata. + type: object + api: + description: |- + Vector API params. Allows to interact with a running Vector instance. + https://vector.dev/docs/reference/api/ + properties: + enabled: + type: boolean + healthcheck: + description: |- + Enable ReadinessProbe and LivenessProbe via api /health endpoint. + If probe enabled via VectorAgent or VectorAggregator, this setting will be ignored for that probe. + type: boolean + playground: + type: boolean + type: object + compressConfigFile: + description: 'Compress config file to fix: metadata.annotations: Too + long: must have at most 262144 characters' + type: boolean + configCheck: + description: Control params for ConfigCheck pods + properties: + affinity: + description: Affinity If specified, the pod's scheduling constraints. + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for + the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. + items: + description: |- + An empty preferred scheduling term matches all objects with implicit weight 0 + (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated with + the corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + description: Weight associated with matching the + corresponding nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. + The terms are ORed. + items: + description: |- + A null or empty node selector term matches no objects. The requirements of + them are ANDed. + The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. + co-locate this pod in the same node, zone, etc. as some + other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules + (e.g. avoid putting this pod in the same node, zone, etc. + as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the anti-affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the anti-affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the anti-affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + annotations: + additionalProperties: + type: string + description: Annotations is an unstructured key value map stored + with a resource that may be set by external tools to store and + retrieve arbitrary metadata. + type: object + disabled: + type: boolean + image: + description: |- + Image - docker image settings for Vector Agent + if no specified operator uses default config version + type: string + resources: + description: |- + Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + if not specified - default setting will be used + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + tolerations: + description: Tolerations If specified, the pod's tolerations. + items: + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . + properties: + effect: + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + type: object + configReloaderImage: + type: string + configReloaderResources: + description: ResourceRequirements describes the compute resource requirements. + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + containerSecurityContext: + description: |- + SecurityContext holds security configuration that will be applied to a container. + Some fields are present in both SecurityContext and PodSecurityContext. + When both are set, the values in SecurityContext take precedence. + properties: + allowPrivilegeEscalation: + description: |- + AllowPrivilegeEscalation controls whether a process can gain more + privileges than its parent process. This bool directly controls if + the no_new_privs flag will be set on the container process. + AllowPrivilegeEscalation is true always when the container is: + 1) run as Privileged + 2) has CAP_SYS_ADMIN + Note that this field cannot be set when spec.os.name is windows. + type: boolean + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by this container. If set, this profile + overrides the pod's appArmorProfile. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object + capabilities: + description: |- + The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container runtime. + Note that this field cannot be set when spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities type + type: string + type: array + x-kubernetes-list-type: atomic + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities type + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + description: |- + Run container in privileged mode. + Processes in privileged containers are essentially equivalent to root on the host. + Defaults to false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: |- + procMount denotes the type of proc mount to use for the containers. + The default value is Default which uses the container runtime defaults for + readonly paths and masked paths. + This requires the ProcMountType feature flag to be enabled. + Note that this field cannot be set when spec.os.name is windows. + type: string + readOnlyRootFilesystem: + description: |- + Whether this container has a read-only root filesystem. + Default is false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: |- + The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies to + the container. + type: string + role: + description: Role is a SELinux role label that applies to + the container. + type: string + type: + description: Type is a SELinux type label that applies to + the container. + type: string + user: + description: User is a SELinux user label that applies to + the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by this container. If seccomp options are + provided at both the pod & container level, the container options + override the pod options. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + windowsOptions: + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options from the PodSecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the GMSA + credential spec to use. + type: string + hostProcess: + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + dataDir: + description: |- + The directory used for persisting Vector state, such as on-disk buffers, file checkpoints, and more. Please make sure the Vector project has write permissions to this directory. + https://vector.dev/docs/reference/configuration/global-options/#data_dir + type: string + env: + description: Env that will be added to Vector pod + items: + description: EnvVar represents an environment variable present in + a Container. + properties: + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + type: string + value: + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". + type: string + valueFrom: + description: Source for the environment variable's value. Cannot + be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. + properties: + apiVersion: + description: Version of the schema the FieldPath is + written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified + API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the exposed + resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + host_aliases: + description: |- + HostAliases provides mapping between ip and hostnames, + that would be propagated to pod, + cannot be used with HostNetwork. + items: + description: |- + HostAlias holds the mapping between IP and hostnames that will be injected as an entry in the + pod's hosts file. + properties: + hostnames: + description: Hostnames for the above IP address. + items: + type: string + type: array + x-kubernetes-list-type: atomic + ip: + description: IP address of the host file entry. + type: string + required: + - ip + type: object + type: array + hostNetwork: + description: HostNetwork controls whether the pod may use the node + network namespace + type: boolean + image: + description: |- + Image - docker image settings for Vector Agent + if no specified operator uses default config version + type: string + imagePullPolicy: + description: ImagePullPolicy of pods + type: string + imagePullSecrets: + description: |- + ImagePullSecrets An optional list of references to secrets in the same namespace + to use for pulling images from registries + see http://kubernetes.io/docs/user-guide/images#specifying-imagepullsecrets-on-a-pod + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + type: array + internalMetrics: + description: Enable internal metrics exporter + type: boolean + livenessProbe: + description: Periodic probe of container liveness. Container will + be restarted if the probe fails. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + properties: + port: + description: Port number of the gRPC service. Number must + be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows + repeated headers. + items: + description: HTTPHeader describes a custom header to be + used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + podSecurityContext: + description: |- + SecurityContext holds pod-level security attributes and common container settings. + This defaults to the default PodSecurityContext. + properties: + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by the containers in this pod. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object + fsGroup: + description: |- + A special supplemental group that applies to all containers in a pod. + Some volume types allow the Kubelet to change the ownership of that volume + to be owned by the pod: + + 1. The owning GID will be the FSGroup + 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) + 3. The permission bits are OR'd with rw-rw---- + + If unset, the Kubelet will not modify the ownership and permissions of any volume. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + fsGroupChangePolicy: + description: |- + fsGroupChangePolicy defines behavior of changing ownership and permission of the volume + before being exposed inside Pod. This field will only apply to + volume types which support fsGroup based ownership(and permissions). + It will have no effect on ephemeral volume types such as: secret, configmaps + and emptydir. + Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used. + Note that this field cannot be set when spec.os.name is windows. + type: string + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: |- + The SELinux context to be applied to all containers. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in SecurityContext. If set in + both SecurityContext and PodSecurityContext, the value specified in SecurityContext + takes precedence for that container. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies to + the container. + type: string + role: + description: Role is a SELinux role label that applies to + the container. + type: string + type: + description: Type is a SELinux type label that applies to + the container. + type: string + user: + description: User is a SELinux user label that applies to + the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by the containers in this pod. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + supplementalGroups: + description: |- + A list of groups applied to the first process run in each container, in + addition to the container's primary GID and fsGroup (if specified). If + the SupplementalGroupsPolicy feature is enabled, the + supplementalGroupsPolicy field determines whether these are in addition + to or instead of any group memberships defined in the container image. + If unspecified, no additional groups are added, though group memberships + defined in the container image may still be used, depending on the + supplementalGroupsPolicy field. + Note that this field cannot be set when spec.os.name is windows. + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + description: |- + Defines how supplemental groups of the first container processes are calculated. + Valid values are "Merge" and "Strict". If not specified, "Merge" is used. + (Alpha) Using the field requires the SupplementalGroupsPolicy feature gate to be enabled + and the container runtime must implement support for this feature. + Note that this field cannot be set when spec.os.name is windows. + type: string + sysctls: + description: |- + Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported + sysctls (by the container runtime) might fail to launch. + Note that this field cannot be set when spec.os.name is windows. + items: + description: Sysctl defines a kernel parameter to be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options within a container's SecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the GMSA + credential spec to use. + type: string + hostProcess: + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + podSecurityPolicyName: + description: |- + PodSecurityPolicyName - defines name for podSecurityPolicy + in case of empty value, prefixedName will be used. + type: string + priorityClassName: + description: PriorityClassName assigned to the Pods + type: string + readinessProbe: + description: Periodic probe of container service readiness. Container + will be removed from service endpoints if the probe fails. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + properties: + port: + description: Port number of the gRPC service. Number must + be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows + repeated headers. + items: + description: HTTPHeader describes a custom header to be + used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + replicas: + format: int32 + type: integer + resources: + description: |- + Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + if not specified - default setting will be used + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + runtimeClassName: + description: |- + RuntimeClassName - defines runtime class for kubernetes pod. + https://kubernetes.io/docs/concepts/containers/runtime-class/ + type: string + schedulerName: + description: SchedulerName - defines kubernetes scheduler name + type: string + selector: + description: |- + Defines a filter for the Vector Pipeline and Cluster Vector Pipeline by labels. + If not specified, all pipelines will be selected. + properties: + matchLabels: + additionalProperties: + type: string + type: object + type: object + tolerations: + description: Tolerations If specified, the pod's tolerations. + items: + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . + properties: + effect: + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + volumeMounts: + description: Pod volumes to mount into the container's filesystem. + items: + description: VolumeMount describes a mounting of a Volume within + a container. + properties: + mountPath: + description: |- + Path within the container at which the volume should be mounted. Must + not contain ':'. + type: string + mountPropagation: + description: |- + mountPropagation determines how mounts are propagated from the host + to container and the other way around. + When not set, MountPropagationNone is used. + This field is beta in 1.10. + When RecursiveReadOnly is set to IfPossible or to Enabled, MountPropagation must be None or unspecified + (which defaults to None). + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: |- + Mounted read-only if true, read-write otherwise (false or unspecified). + Defaults to false. + type: boolean + recursiveReadOnly: + description: |- + RecursiveReadOnly specifies whether read-only mounts should be handled + recursively. + + If ReadOnly is false, this field has no meaning and must be unspecified. + + If ReadOnly is true, and this field is set to Disabled, the mount is not made + recursively read-only. If this field is set to IfPossible, the mount is made + recursively read-only, if it is supported by the container runtime. If this + field is set to Enabled, the mount is made recursively read-only if it is + supported by the container runtime, otherwise the pod will not be started and + an error will be generated to indicate the reason. + + If this field is set to IfPossible or Enabled, MountPropagation must be set to + None (or be unspecified, which defaults to None). + + If this field is not specified, it is treated as an equivalent of Disabled. + type: string + subPath: + description: |- + Path within the volume from which the container's volume should be mounted. + Defaults to "" (volume's root). + type: string + subPathExpr: + description: |- + Expanded path within the volume from which the container's volume should be mounted. + Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. + Defaults to "" (volume's root). + SubPathExpr and SubPath are mutually exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + volumes: + description: List of volumes that can be mounted by containers belonging + to the pod. + items: + description: Volume represents a named volume in a pod that may + be accessed by any container in the pod. + properties: + awsElasticBlockStore: + description: |- + awsElasticBlockStore represents an AWS Disk resource that is attached to a + kubelet's host machine and then exposed to the pod. + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + properties: + fsType: + description: |- + fsType is the filesystem type of the volume that you want to mount. + Tip: Ensure that the filesystem type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + type: string + partition: + description: |- + partition is the partition in the volume that you want to mount. + If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition as "1". + Similarly, the volume partition for /dev/sda is "0" (or you can leave the property empty). + format: int32 + type: integer + readOnly: + description: |- + readOnly value true will force the readOnly setting in VolumeMounts. + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + type: boolean + volumeID: + description: |- + volumeID is unique ID of the persistent disk resource in AWS (Amazon EBS volume). + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + type: string + required: + - volumeID + type: object + azureDisk: + description: azureDisk represents an Azure Data Disk mount on + the host and bind mount to the pod. + properties: + cachingMode: + description: 'cachingMode is the Host Caching mode: None, + Read Only, Read Write.' + type: string + diskName: + description: diskName is the Name of the data disk in the + blob storage + type: string + diskURI: + description: diskURI is the URI of data disk in the blob + storage + type: string + fsType: + default: ext4 + description: |- + fsType is Filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + kind: + description: 'kind expected values are Shared: multiple + blob disks per storage account Dedicated: single blob + disk per storage account Managed: azure managed data + disk (only in managed availability set). defaults to shared' + type: string + readOnly: + default: false + description: |- + readOnly Defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + description: azureFile represents an Azure File Service mount + on the host and bind mount to the pod. + properties: + readOnly: + description: |- + readOnly defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + secretName: + description: secretName is the name of secret that contains + Azure Storage Account Name and Key + type: string + shareName: + description: shareName is the azure share Name + type: string + required: + - secretName + - shareName + type: object + cephfs: + description: cephFS represents a Ceph FS mount on the host that + shares a pod's lifetime + properties: + monitors: + description: |- + monitors is Required: Monitors is a collection of Ceph monitors + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it + items: + type: string + type: array + x-kubernetes-list-type: atomic + path: + description: 'path is Optional: Used as the mounted root, + rather than the full Ceph tree, default is /' + type: string + readOnly: + description: |- + readOnly is Optional: Defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it + type: boolean + secretFile: + description: |- + secretFile is Optional: SecretFile is the path to key ring for User, default is /etc/ceph/user.secret + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it + type: string + secretRef: + description: |- + secretRef is Optional: SecretRef is reference to the authentication secret for User, default is empty. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + user: + description: |- + user is optional: User is the rados user name, default is admin + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it + type: string + required: + - monitors + type: object + cinder: + description: |- + cinder represents a cinder volume attached and mounted on kubelets host machine. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md + properties: + fsType: + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md + type: string + readOnly: + description: |- + readOnly defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md + type: boolean + secretRef: + description: |- + secretRef is optional: points to a secret object containing parameters used to connect + to OpenStack. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + description: |- + volumeID used to identify the volume in cinder. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md + type: string + required: + - volumeID + type: object + configMap: + description: configMap represents a configMap that should populate + this volume + properties: + defaultMode: + description: |- + defaultMode is optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + ConfigMap will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: optional specify whether the ConfigMap or its + keys must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + description: csi (Container Storage Interface) represents ephemeral + storage that is handled by certain external CSI drivers (Beta + feature). + properties: + driver: + description: |- + driver is the name of the CSI driver that handles this volume. + Consult with your admin for the correct name as registered in the cluster. + type: string + fsType: + description: |- + fsType to mount. Ex. "ext4", "xfs", "ntfs". + If not provided, the empty value is passed to the associated CSI driver + which will determine the default filesystem to apply. + type: string + nodePublishSecretRef: + description: |- + nodePublishSecretRef is a reference to the secret object containing + sensitive information to pass to the CSI driver to complete the CSI + NodePublishVolume and NodeUnpublishVolume calls. + This field is optional, and may be empty if no secret is required. If the + secret object contains more than one secret, all secret references are passed. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + description: |- + readOnly specifies a read-only configuration for the volume. + Defaults to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: + type: string + description: |- + volumeAttributes stores driver-specific properties that are passed to the CSI + driver. Consult your driver's documentation for supported values. + type: object + required: + - driver + type: object + downwardAPI: + description: downwardAPI represents downward API about the pod + that should populate this volume + properties: + defaultMode: + description: |- + Optional: mode bits to use on created files by default. Must be a + Optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + items: + description: Items is a list of downward API volume file + items: + description: DownwardAPIVolumeFile represents information + to create the file containing the pod field + properties: + fieldRef: + description: 'Required: Selects a field of the pod: + only annotations, labels, name, namespace and uid + are supported.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + description: |- + Optional: mode bits used to set permissions on this file, must be an octal value + between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: 'Required: Path is the relative path + name of the file to be created. Must not be absolute + or contain the ''..'' path. Must be utf-8 encoded. + The first item of the relative path must not start + with ''..''' + type: string + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + emptyDir: + description: |- + emptyDir represents a temporary directory that shares a pod's lifetime. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir + properties: + medium: + description: |- + medium represents what type of storage medium should back this directory. + The default is "" which means to use the node's default medium. + Must be an empty string (default) or Memory. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + description: |- + sizeLimit is the total amount of local storage required for this EmptyDir volume. + The size limit is also applicable for memory medium. + The maximum usage on memory medium EmptyDir would be the minimum value between + the SizeLimit specified here and the sum of memory limits of all containers in a pod. + The default is nil which means that the limit is undefined. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + description: |- + ephemeral represents a volume that is handled by a cluster storage driver. + The volume's lifecycle is tied to the pod that defines it - it will be created before the pod starts, + and deleted when the pod is removed. + + Use this if: + a) the volume is only needed while the pod runs, + b) features of normal volumes like restoring from snapshot or capacity + tracking are needed, + c) the storage driver is specified through a storage class, and + d) the storage driver supports dynamic volume provisioning through + a PersistentVolumeClaim (see EphemeralVolumeSource for more + information on the connection between this volume type + and PersistentVolumeClaim). + + Use PersistentVolumeClaim or one of the vendor-specific + APIs for volumes that persist for longer than the lifecycle + of an individual pod. + + Use CSI for light-weight local ephemeral volumes if the CSI driver is meant to + be used that way - see the documentation of the driver for + more information. + + A pod can use both types of ephemeral volumes and + persistent volumes at the same time. + properties: + volumeClaimTemplate: + description: |- + Will be used to create a stand-alone PVC to provision the volume. + The pod in which this EphemeralVolumeSource is embedded will be the + owner of the PVC, i.e. the PVC will be deleted together with the + pod. The name of the PVC will be `-` where + `` is the name from the `PodSpec.Volumes` array + entry. Pod validation will reject the pod if the concatenated name + is not valid for a PVC (for example, too long). + + An existing PVC with that name that is not owned by the pod + will *not* be used for the pod to avoid using an unrelated + volume by mistake. Starting the pod is then blocked until + the unrelated PVC is removed. If such a pre-created PVC is + meant to be used by the pod, the PVC has to updated with an + owner reference to the pod once the pod exists. Normally + this should not be necessary, but it may be useful when + manually reconstructing a broken cluster. + + This field is read-only and no changes will be made by Kubernetes + to the PVC after it has been created. + + Required, must not be nil. + properties: + metadata: + description: |- + May contain labels and annotations that will be copied into the PVC + when creating it. No other fields are allowed and will be rejected during + validation. + type: object + spec: + description: |- + The specification for the PersistentVolumeClaim. The entire content is + copied unchanged into the PVC that gets created from this + template. The same fields as in a PersistentVolumeClaim + are also valid here. + properties: + accessModes: + description: |- + accessModes contains the desired access modes the volume should have. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1 + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + description: |- + dataSource field can be used to specify either: + * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) + * An existing PVC (PersistentVolumeClaim) + If the provisioner or an external controller can support the specified data source, + it will create a new volume based on the contents of the specified data source. + When the AnyVolumeDataSource feature gate is enabled, dataSource contents will be copied to dataSourceRef, + and dataSourceRef contents will be copied to dataSource when dataSourceRef.namespace is not specified. + If the namespace is specified, then dataSourceRef will not be copied to dataSource. + properties: + apiGroup: + description: |- + APIGroup is the group for the resource being referenced. + If APIGroup is not specified, the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource being + referenced + type: string + name: + description: Name is the name of resource being + referenced + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + description: |- + dataSourceRef specifies the object from which to populate the volume with data, if a non-empty + volume is desired. This may be any object from a non-empty API group (non + core object) or a PersistentVolumeClaim object. + When this field is specified, volume binding will only succeed if the type of + the specified object matches some installed volume populator or dynamic + provisioner. + This field will replace the functionality of the dataSource field and as such + if both fields are non-empty, they must have the same value. For backwards + compatibility, when namespace isn't specified in dataSourceRef, + both fields (dataSource and dataSourceRef) will be set to the same + value automatically if one of them is empty and the other is non-empty. + When namespace is specified in dataSourceRef, + dataSource isn't set to the same value and must be empty. + There are three important differences between dataSource and dataSourceRef: + * While dataSource only allows two specific types of objects, dataSourceRef + allows any non-core object, as well as PersistentVolumeClaim objects. + * While dataSource ignores disallowed values (dropping them), dataSourceRef + preserves all values, and generates an error if a disallowed value is + specified. + * While dataSource only allows local objects, dataSourceRef allows objects + in any namespaces. + (Beta) Using this field requires the AnyVolumeDataSource feature gate to be enabled. + (Alpha) Using the namespace field of dataSourceRef requires the CrossNamespaceVolumeDataSource feature gate to be enabled. + properties: + apiGroup: + description: |- + APIGroup is the group for the resource being referenced. + If APIGroup is not specified, the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource being + referenced + type: string + name: + description: Name is the name of resource being + referenced + type: string + namespace: + description: |- + Namespace is the namespace of resource being referenced + Note that when a namespace is specified, a gateway.networking.k8s.io/ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. + (Alpha) This field requires the CrossNamespaceVolumeDataSource feature gate to be enabled. + type: string + required: + - kind + - name + type: object + resources: + description: |- + resources represents the minimum resources the volume should have. + If RecoverVolumeExpansionFailure feature is enabled users are allowed to specify resource requirements + that are lower than previous value but must still be higher than capacity recorded in the + status field of the claim. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + selector: + description: selector is a label query over volumes + to consider for binding. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + description: |- + storageClassName is the name of the StorageClass required by the claim. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1 + type: string + volumeAttributesClassName: + description: |- + volumeAttributesClassName may be used to set the VolumeAttributesClass used by this claim. + If specified, the CSI driver will create or update the volume with the attributes defined + in the corresponding VolumeAttributesClass. This has a different purpose than storageClassName, + it can be changed after the claim is created. An empty string value means that no VolumeAttributesClass + will be applied to the claim but it's not allowed to reset this field to empty string once it is set. + If unspecified and the PersistentVolumeClaim is unbound, the default VolumeAttributesClass + will be set by the persistentvolume controller if it exists. + If the resource referred to by volumeAttributesClass does not exist, this PersistentVolumeClaim will be + set to a Pending state, as reflected by the modifyVolumeStatus field, until such as a resource + exists. + More info: https://kubernetes.io/docs/concepts/storage/volume-attributes-classes/ + (Beta) Using this field requires the VolumeAttributesClass feature gate to be enabled (off by default). + type: string + volumeMode: + description: |- + volumeMode defines what type of volume is required by the claim. + Value of Filesystem is implied when not included in claim spec. + type: string + volumeName: + description: volumeName is the binding reference + to the PersistentVolume backing this claim. + type: string + type: object + required: + - spec + type: object + type: object + fc: + description: fc represents a Fibre Channel resource that is + attached to a kubelet's host machine and then exposed to the + pod. + properties: + fsType: + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + lun: + description: 'lun is Optional: FC target lun number' + format: int32 + type: integer + readOnly: + description: |- + readOnly is Optional: Defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + targetWWNs: + description: 'targetWWNs is Optional: FC target worldwide + names (WWNs)' + items: + type: string + type: array + x-kubernetes-list-type: atomic + wwids: + description: |- + wwids Optional: FC volume world wide identifiers (wwids) + Either wwids or combination of targetWWNs and lun must be set, but not both simultaneously. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + flexVolume: + description: |- + flexVolume represents a generic volume resource that is + provisioned/attached using an exec based plugin. + properties: + driver: + description: driver is the name of the driver to use for + this volume. + type: string + fsType: + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". The default filesystem depends on FlexVolume script. + type: string + options: + additionalProperties: + type: string + description: 'options is Optional: this field holds extra + command options if any.' + type: object + readOnly: + description: |- + readOnly is Optional: defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: |- + secretRef is Optional: secretRef is reference to the secret object containing + sensitive information to pass to the plugin scripts. This may be + empty if no secret object is specified. If the secret object + contains more than one secret, all secrets are passed to the plugin + scripts. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + description: flocker represents a Flocker volume attached to + a kubelet's host machine. This depends on the Flocker control + service being running + properties: + datasetName: + description: |- + datasetName is Name of the dataset stored as metadata -> name on the dataset for Flocker + should be considered as deprecated + type: string + datasetUUID: + description: datasetUUID is the UUID of the dataset. This + is unique identifier of a Flocker dataset + type: string + type: object + gcePersistentDisk: + description: |- + gcePersistentDisk represents a GCE Disk resource that is attached to a + kubelet's host machine and then exposed to the pod. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + properties: + fsType: + description: |- + fsType is filesystem type of the volume that you want to mount. + Tip: Ensure that the filesystem type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + type: string + partition: + description: |- + partition is the partition in the volume that you want to mount. + If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition as "1". + Similarly, the volume partition for /dev/sda is "0" (or you can leave the property empty). + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + format: int32 + type: integer + pdName: + description: |- + pdName is unique name of the PD resource in GCE. Used to identify the disk in GCE. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + type: string + readOnly: + description: |- + readOnly here will force the ReadOnly setting in VolumeMounts. + Defaults to false. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + type: boolean + required: + - pdName + type: object + gitRepo: + description: |- + gitRepo represents a git repository at a particular revision. + DEPRECATED: GitRepo is deprecated. To provision a container with a git repo, mount an + EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir + into the Pod's container. + properties: + directory: + description: |- + directory is the target directory name. + Must not contain or start with '..'. If '.' is supplied, the volume directory will be the + git repository. Otherwise, if specified, the volume will contain the git repository in + the subdirectory with the given name. + type: string + repository: + description: repository is the URL + type: string + revision: + description: revision is the commit hash for the specified + revision. + type: string + required: + - repository + type: object + glusterfs: + description: |- + glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. + More info: https://examples.k8s.io/volumes/glusterfs/README.md + properties: + endpoints: + description: |- + endpoints is the endpoint name that details Glusterfs topology. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod + type: string + path: + description: |- + path is the Glusterfs volume path. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod + type: string + readOnly: + description: |- + readOnly here will force the Glusterfs volume to be mounted with read-only permissions. + Defaults to false. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod + type: boolean + required: + - endpoints + - path + type: object + hostPath: + description: |- + hostPath represents a pre-existing file or directory on the host + machine that is directly exposed to the container. This is generally + used for system agents or other privileged things that are allowed + to see the host machine. Most containers will NOT need this. + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + properties: + path: + description: |- + path of the directory on the host. + If the path is a symlink, it will follow the link to the real path. + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + type: string + type: + description: |- + type for HostPath Volume + Defaults to "" + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + type: string + required: + - path + type: object + image: + description: |- + image represents an OCI object (a container image or artifact) pulled and mounted on the kubelet's host machine. + The volume is resolved at pod startup depending on which PullPolicy value is provided: + + - Always: the kubelet always attempts to pull the reference. Container creation will fail If the pull fails. + - Never: the kubelet never pulls the reference and only uses a local image or artifact. Container creation will fail if the reference isn't present. + - IfNotPresent: the kubelet pulls if the reference isn't already present on disk. Container creation will fail if the reference isn't present and the pull fails. + + The volume gets re-resolved if the pod gets deleted and recreated, which means that new remote content will become available on pod recreation. + A failure to resolve or pull the image during pod startup will block containers from starting and may add significant latency. Failures will be retried using normal volume backoff and will be reported on the pod reason and message. + The types of objects that may be mounted by this volume are defined by the container runtime implementation on a host machine and at minimum must include all valid types supported by the container image field. + The OCI object gets mounted in a single directory (spec.containers[*].volumeMounts.mountPath) by merging the manifest layers in the same way as for container images. + The volume will be mounted read-only (ro) and non-executable files (noexec). + Sub path mounts for containers are not supported (spec.containers[*].volumeMounts.subpath). + The field spec.securityContext.fsGroupChangePolicy has no effect on this volume type. + properties: + pullPolicy: + description: |- + Policy for pulling OCI objects. Possible values are: + Always: the kubelet always attempts to pull the reference. Container creation will fail If the pull fails. + Never: the kubelet never pulls the reference and only uses a local image or artifact. Container creation will fail if the reference isn't present. + IfNotPresent: the kubelet pulls if the reference isn't already present on disk. Container creation will fail if the reference isn't present and the pull fails. + Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. + type: string + reference: + description: |- + Required: Image or artifact reference to be used. + Behaves in the same way as pod.spec.containers[*].image. + Pull secrets will be assembled in the same way as for the container image by looking up node credentials, SA image pull secrets, and pod spec image pull secrets. + More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config management to default or override + container images in workload controllers like Deployments and StatefulSets. + type: string + type: object + iscsi: + description: |- + iscsi represents an ISCSI Disk resource that is attached to a + kubelet's host machine and then exposed to the pod. + More info: https://examples.k8s.io/volumes/iscsi/README.md + properties: + chapAuthDiscovery: + description: chapAuthDiscovery defines whether support iSCSI + Discovery CHAP authentication + type: boolean + chapAuthSession: + description: chapAuthSession defines whether support iSCSI + Session CHAP authentication + type: boolean + fsType: + description: |- + fsType is the filesystem type of the volume that you want to mount. + Tip: Ensure that the filesystem type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi + type: string + initiatorName: + description: |- + initiatorName is the custom iSCSI Initiator Name. + If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface + : will be created for the connection. + type: string + iqn: + description: iqn is the target iSCSI Qualified Name. + type: string + iscsiInterface: + default: default + description: |- + iscsiInterface is the interface Name that uses an iSCSI transport. + Defaults to 'default' (tcp). + type: string + lun: + description: lun represents iSCSI Target Lun number. + format: int32 + type: integer + portals: + description: |- + portals is the iSCSI Target Portal List. The portal is either an IP or ip_addr:port if the port + is other than default (typically TCP ports 860 and 3260). + items: + type: string + type: array + x-kubernetes-list-type: atomic + readOnly: + description: |- + readOnly here will force the ReadOnly setting in VolumeMounts. + Defaults to false. + type: boolean + secretRef: + description: secretRef is the CHAP Secret for iSCSI target + and initiator authentication + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + description: |- + targetPortal is iSCSI Target Portal. The Portal is either an IP or ip_addr:port if the port + is other than default (typically TCP ports 860 and 3260). + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + description: |- + name of the volume. + Must be a DNS_LABEL and unique within the pod. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + nfs: + description: |- + nfs represents an NFS mount on the host that shares a pod's lifetime + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs + properties: + path: + description: |- + path that is exported by the NFS server. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs + type: string + readOnly: + description: |- + readOnly here will force the NFS export to be mounted with read-only permissions. + Defaults to false. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs + type: boolean + server: + description: |- + server is the hostname or IP address of the NFS server. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + description: |- + persistentVolumeClaimVolumeSource represents a reference to a + PersistentVolumeClaim in the same namespace. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims + properties: + claimName: + description: |- + claimName is the name of a PersistentVolumeClaim in the same namespace as the pod using this volume. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims + type: string + readOnly: + description: |- + readOnly Will force the ReadOnly setting in VolumeMounts. + Default false. + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + description: photonPersistentDisk represents a PhotonController + persistent disk attached and mounted on kubelets host machine + properties: + fsType: + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + pdID: + description: pdID is the ID that identifies Photon Controller + persistent disk + type: string + required: + - pdID + type: object + portworxVolume: + description: portworxVolume represents a portworx volume attached + and mounted on kubelets host machine + properties: + fsType: + description: |- + fSType represents the filesystem type to mount + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs". Implicitly inferred to be "ext4" if unspecified. + type: string + readOnly: + description: |- + readOnly defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + volumeID: + description: volumeID uniquely identifies a Portworx volume + type: string + required: + - volumeID + type: object + projected: + description: projected items for all in one resources secrets, + configmaps, and downward API + properties: + defaultMode: + description: |- + defaultMode are the mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + sources: + description: |- + sources is the list of volume projections. Each entry in this list + handles one source. + items: + description: |- + Projection that may be projected along with other supported volume types. + Exactly one of these fields must be set. + properties: + clusterTrustBundle: + description: |- + ClusterTrustBundle allows a pod to access the `.spec.trustBundle` field + of ClusterTrustBundle objects in an auto-updating file. + + Alpha, gated by the ClusterTrustBundleProjection feature gate. + + ClusterTrustBundle objects can either be selected by name, or by the + combination of signer name and a label selector. + + Kubelet performs aggressive normalization of the PEM contents written + into the pod filesystem. Esoteric PEM features such as inter-block + comments and block headers are stripped. Certificates are deduplicated. + The ordering of certificates within the file is arbitrary, and Kubelet + may change the order over time. + properties: + labelSelector: + description: |- + Select all ClusterTrustBundles that match this label selector. Only has + effect if signerName is set. Mutually-exclusive with name. If unset, + interpreted as "match nothing". If set but empty, interpreted as "match + everything". + properties: + matchExpressions: + description: matchExpressions is a list of + label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + name: + description: |- + Select a single ClusterTrustBundle by object name. Mutually-exclusive + with signerName and labelSelector. + type: string + optional: + description: |- + If true, don't block pod startup if the referenced ClusterTrustBundle(s) + aren't available. If using name, then the named ClusterTrustBundle is + allowed not to exist. If using signerName, then the combination of + signerName and labelSelector is allowed to match zero + ClusterTrustBundles. + type: boolean + path: + description: Relative path from the volume root + to write the bundle. + type: string + signerName: + description: |- + Select all ClusterTrustBundles that match this signer name. + Mutually-exclusive with name. The contents of all selected + ClusterTrustBundles will be unified and deduplicated. + type: string + required: + - path + type: object + configMap: + description: configMap information about the configMap + data to project + properties: + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + ConfigMap will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: optional specify whether the ConfigMap + or its keys must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + description: downwardAPI information about the downwardAPI + data to project + properties: + items: + description: Items is a list of DownwardAPIVolume + file + items: + description: DownwardAPIVolumeFile represents + information to create the file containing + the pod field + properties: + fieldRef: + description: 'Required: Selects a field + of the pod: only annotations, labels, + name, namespace and uid are supported.' + properties: + apiVersion: + description: Version of the schema the + FieldPath is written in terms of, + defaults to "v1". + type: string + fieldPath: + description: Path of the field to select + in the specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + description: |- + Optional: mode bits used to set permissions on this file, must be an octal value + between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: 'Required: Path is the relative + path name of the file to be created. Must + not be absolute or contain the ''..'' + path. Must be utf-8 encoded. The first + item of the relative path must not start + with ''..''' + type: string + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. + properties: + containerName: + description: 'Container name: required + for volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format + of the exposed resources, defaults + to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to + select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + secret: + description: secret information about the secret data + to project + properties: + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + Secret will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: optional field specify whether the + Secret or its key must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + description: serviceAccountToken is information about + the serviceAccountToken data to project + properties: + audience: + description: |- + audience is the intended audience of the token. A recipient of a token + must identify itself with an identifier specified in the audience of the + token, and otherwise should reject the token. The audience defaults to the + identifier of the apiserver. + type: string + expirationSeconds: + description: |- + expirationSeconds is the requested duration of validity of the service + account token. As the token approaches expiration, the kubelet volume + plugin will proactively rotate the service account token. The kubelet will + start trying to rotate the token if the token is older than 80 percent of + its time to live or if the token is older than 24 hours.Defaults to 1 hour + and must be at least 10 minutes. + format: int64 + type: integer + path: + description: |- + path is the path relative to the mount point of the file to project the + token into. + type: string + required: + - path + type: object + type: object + type: array + x-kubernetes-list-type: atomic + type: object + quobyte: + description: quobyte represents a Quobyte mount on the host + that shares a pod's lifetime + properties: + group: + description: |- + group to map volume access to + Default is no group + type: string + readOnly: + description: |- + readOnly here will force the Quobyte volume to be mounted with read-only permissions. + Defaults to false. + type: boolean + registry: + description: |- + registry represents a single or multiple Quobyte Registry services + specified as a string as host:port pair (multiple entries are separated with commas) + which acts as the central registry for volumes + type: string + tenant: + description: |- + tenant owning the given Quobyte volume in the Backend + Used with dynamically provisioned Quobyte volumes, value is set by the plugin + type: string + user: + description: |- + user to map volume access to + Defaults to serivceaccount user + type: string + volume: + description: volume is a string that references an already + created Quobyte volume by name. + type: string + required: + - registry + - volume + type: object + rbd: + description: |- + rbd represents a Rados Block Device mount on the host that shares a pod's lifetime. + More info: https://examples.k8s.io/volumes/rbd/README.md + properties: + fsType: + description: |- + fsType is the filesystem type of the volume that you want to mount. + Tip: Ensure that the filesystem type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd + type: string + image: + description: |- + image is the rados image name. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + type: string + keyring: + default: /etc/ceph/keyring + description: |- + keyring is the path to key ring for RBDUser. + Default is /etc/ceph/keyring. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + type: string + monitors: + description: |- + monitors is a collection of Ceph monitors. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + items: + type: string + type: array + x-kubernetes-list-type: atomic + pool: + default: rbd + description: |- + pool is the rados pool name. + Default is rbd. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + type: string + readOnly: + description: |- + readOnly here will force the ReadOnly setting in VolumeMounts. + Defaults to false. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + type: boolean + secretRef: + description: |- + secretRef is name of the authentication secret for RBDUser. If provided + overrides keyring. + Default is nil. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + user: + default: admin + description: |- + user is the rados user name. + Default is admin. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + type: string + required: + - image + - monitors + type: object + scaleIO: + description: scaleIO represents a ScaleIO persistent volume + attached and mounted on Kubernetes nodes. + properties: + fsType: + default: xfs + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". + Default is "xfs". + type: string + gateway: + description: gateway is the host address of the ScaleIO + API Gateway. + type: string + protectionDomain: + description: protectionDomain is the name of the ScaleIO + Protection Domain for the configured storage. + type: string + readOnly: + description: |- + readOnly Defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: |- + secretRef references to the secret for ScaleIO user and other + sensitive information. If this is not provided, Login operation will fail. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + description: sslEnabled Flag enable/disable SSL communication + with Gateway, default false + type: boolean + storageMode: + default: ThinProvisioned + description: |- + storageMode indicates whether the storage for a volume should be ThickProvisioned or ThinProvisioned. + Default is ThinProvisioned. + type: string + storagePool: + description: storagePool is the ScaleIO Storage Pool associated + with the protection domain. + type: string + system: + description: system is the name of the storage system as + configured in ScaleIO. + type: string + volumeName: + description: |- + volumeName is the name of a volume already created in the ScaleIO system + that is associated with this volume source. + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + description: |- + secret represents a secret that should populate this volume. + More info: https://kubernetes.io/docs/concepts/storage/volumes#secret + properties: + defaultMode: + description: |- + defaultMode is Optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values + for mode bits. Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + items: + description: |- + items If unspecified, each key-value pair in the Data field of the referenced + Secret will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + optional: + description: optional field specify whether the Secret or + its keys must be defined + type: boolean + secretName: + description: |- + secretName is the name of the secret in the pod's namespace to use. + More info: https://kubernetes.io/docs/concepts/storage/volumes#secret + type: string + type: object + storageos: + description: storageOS represents a StorageOS volume attached + and mounted on Kubernetes nodes. + properties: + fsType: + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + readOnly: + description: |- + readOnly defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: |- + secretRef specifies the secret to use for obtaining the StorageOS API + credentials. If not specified, default values will be attempted. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + description: |- + volumeName is the human-readable name of the StorageOS volume. Volume + names are only unique within a namespace. + type: string + volumeNamespace: + description: |- + volumeNamespace specifies the scope of the volume within StorageOS. If no + namespace is specified then the Pod's namespace will be used. This allows the + Kubernetes name scoping to be mirrored within StorageOS for tighter integration. + Set VolumeName to any name to override the default behaviour. + Set to "default" if you are not using namespaces within StorageOS. + Namespaces that do not pre-exist within StorageOS will be created. + type: string + type: object + vsphereVolume: + description: vsphereVolume represents a vSphere volume attached + and mounted on kubelets host machine + properties: + fsType: + description: |- + fsType is filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + storagePolicyID: + description: storagePolicyID is the storage Policy Based + Management (SPBM) profile ID associated with the StoragePolicyName. + type: string + storagePolicyName: + description: storagePolicyName is the storage Policy Based + Management (SPBM) profile name. + type: string + volumePath: + description: volumePath is the path that identifies vSphere + volume vmdk + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + type: object + status: + description: VectorAggregatorStatus defines the observed state of VectorAggregator + properties: + LastAppliedConfigHash: + format: int32 + type: integer + configCheckResult: + type: boolean + reason: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml b/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml index 7961d1cf..bb6a81f2 100644 --- a/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml +++ b/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml @@ -25,6 +25,9 @@ spec: - jsonPath: .status.configCheckResult name: Valid type: boolean + - jsonPath: .status.role + name: Role + type: boolean name: v1alpha1 schema: openAPIV3Schema: @@ -70,6 +73,8 @@ spec: type: boolean reason: type: string + role: + type: string type: object type: object served: true diff --git a/config/crd/bases/observability.kaasops.io_vectors.yaml b/config/crd/bases/observability.kaasops.io_vectors.yaml index 61494279..0fbd4cb2 100644 --- a/config/crd/bases/observability.kaasops.io_vectors.yaml +++ b/config/crd/bases/observability.kaasops.io_vectors.yaml @@ -993,7 +993,7 @@ spec: healthcheck: description: |- Enable ReadinessProbe and LivenessProbe via api /health endpoint. - If probe enabled via VectorAgent, this setting will be ignored for that probe. + If probe enabled via VectorAgent or VectorAggregator, this setting will be ignored for that probe. type: boolean playground: type: boolean diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 55fa95fa..5352cb24 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -5,6 +5,7 @@ resources: - bases/observability.kaasops.io_vectors.yaml - bases/observability.kaasops.io_vectorpipelines.yaml - bases/observability.kaasops.io_clustervectorpipelines.yaml +- bases/observability.kaasops.io_vectoraggregators.yaml # +kubebuilder:scaffold:crdkustomizeresource patches: @@ -17,6 +18,7 @@ patches: #- path: patches/cainjection_in_vectors.yaml #- path: patches/cainjection_in_vectorpipelines.yaml #- path: patches/cainjection_in_clustervectorpipelines.yaml +#- path: patches/cainjection_in_vectoraggregators.yaml # +kubebuilder:scaffold:crdkustomizecainjectionpatch # [WEBHOOK] To enable webhook, uncomment the following section diff --git a/config/rbac/kustomization.yaml b/config/rbac/kustomization.yaml index 02562986..75b75cc3 100644 --- a/config/rbac/kustomization.yaml +++ b/config/rbac/kustomization.yaml @@ -22,6 +22,8 @@ resources: # default, aiding admins in cluster management. Those roles are # not used by the Project itself. You can comment the following lines # if you do not want those helpers be installed with your Project. +- vectoraggregator_editor_role.yaml +- vectoraggregator_viewer_role.yaml - clustervectorpipeline_editor_role.yaml - clustervectorpipeline_viewer_role.yaml - vectorpipeline_editor_role.yaml diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 867b6c0d..62ca5634 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -39,6 +39,7 @@ rules: - apps resources: - daemonsets + - deployments verbs: - create - delete @@ -51,6 +52,7 @@ rules: - observability.kaasops.io resources: - clustervectorpipelines + - vectoraggregators - vectorpipelines - vectors verbs: @@ -65,6 +67,7 @@ rules: - observability.kaasops.io resources: - clustervectorpipelines/finalizers + - vectoraggregators/finalizers - vectorpipelines/finalizers - vectors/finalizers verbs: @@ -73,6 +76,7 @@ rules: - observability.kaasops.io resources: - clustervectorpipelines/status + - vectoraggregators/status - vectorpipelines/status - vectors/status verbs: diff --git a/config/rbac/vectoraggregator_editor_role.yaml b/config/rbac/vectoraggregator_editor_role.yaml new file mode 100644 index 00000000..45adea94 --- /dev/null +++ b/config/rbac/vectoraggregator_editor_role.yaml @@ -0,0 +1,27 @@ +# permissions for end users to edit vectoraggregators. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: vector-operator + app.kubernetes.io/managed-by: kustomize + name: vectoraggregator-editor-role +rules: +- apiGroups: + - observability.kaasops.io + resources: + - vectoraggregators + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - observability.kaasops.io + resources: + - vectoraggregators/status + verbs: + - get diff --git a/config/rbac/vectoraggregator_viewer_role.yaml b/config/rbac/vectoraggregator_viewer_role.yaml new file mode 100644 index 00000000..2ac4bdc2 --- /dev/null +++ b/config/rbac/vectoraggregator_viewer_role.yaml @@ -0,0 +1,23 @@ +# permissions for end users to view vectoraggregators. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: vector-operator + app.kubernetes.io/managed-by: kustomize + name: vectoraggregator-viewer-role +rules: +- apiGroups: + - observability.kaasops.io + resources: + - vectoraggregators + verbs: + - get + - list + - watch +- apiGroups: + - observability.kaasops.io + resources: + - vectoraggregators/status + verbs: + - get diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index 1db9c8bd..aa9d538a 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -3,4 +3,5 @@ resources: - observability_v1alpha1_vector.yaml - observability_v1alpha1_vectorpipeline.yaml - observability_v1alpha1_clustervectorpipeline.yaml +- observability_v1alpha1_vectoraggregator.yaml # +kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/samples/observability_v1alpha1_vectoraggregator.yaml b/config/samples/observability_v1alpha1_vectoraggregator.yaml new file mode 100644 index 00000000..50921075 --- /dev/null +++ b/config/samples/observability_v1alpha1_vectoraggregator.yaml @@ -0,0 +1,9 @@ +apiVersion: observability.kaasops.io/v1alpha1 +kind: VectorAggregator +metadata: + labels: + app.kubernetes.io/name: vector-operator + app.kubernetes.io/managed-by: kustomize + name: vectoraggregator-sample +spec: + # TODO(user): Add fields here diff --git a/go.mod b/go.mod index 258dc1a5..d870d121 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,9 @@ require ( github.com/onsi/ginkgo/v2 v2.19.0 github.com/onsi/gomega v1.33.1 github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.60.1 + github.com/stoewer/go-strcase v1.2.0 github.com/stretchr/testify v1.9.0 + golang.org/x/sync v0.7.0 gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.31.0 k8s.io/apimachinery v0.31.0 @@ -77,7 +79,6 @@ require ( github.com/prometheus/procfs v0.15.1 // indirect github.com/spf13/cobra v1.8.1 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/stoewer/go-strcase v1.2.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fastjson v1.6.4 // indirect github.com/valyala/fastrand v1.1.0 // indirect @@ -99,7 +100,6 @@ require ( golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect golang.org/x/net v0.26.0 // indirect golang.org/x/oauth2 v0.21.0 // indirect - golang.org/x/sync v0.7.0 // indirect golang.org/x/sys v0.21.0 // indirect golang.org/x/term v0.21.0 // indirect golang.org/x/text v0.16.0 // indirect diff --git a/internal/common/annotations.go b/internal/common/annotations.go new file mode 100644 index 00000000..32cedac3 --- /dev/null +++ b/internal/common/annotations.go @@ -0,0 +1,5 @@ +package common + +const ( + AnnotationServiceName = "observability.kaasops.io/service-name" +) diff --git a/internal/config/agent.go b/internal/config/agent.go new file mode 100644 index 00000000..0c9fbbdf --- /dev/null +++ b/internal/config/agent.go @@ -0,0 +1,91 @@ +package config + +import ( + "fmt" + vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" + "github.com/kaasops/vector-operator/internal/pipeline" + "github.com/kaasops/vector-operator/internal/utils/k8s" + "gopkg.in/yaml.v2" + "k8s.io/apimachinery/pkg/labels" + goyaml "sigs.k8s.io/yaml" +) + +func BuildAgentConfig(p VectorConfigParams, pipelines ...pipeline.Pipeline) ([]byte, error) { + cfg, err := buildAgentConfig(p, pipelines...) + if err != nil { + return nil, err + } + yamlBytes, err := yaml.Marshal(cfg) + if err != nil { + return nil, err + } + jsonBytes, err := goyaml.YAMLToJSON(yamlBytes) + if err != nil { + return nil, err + } + return jsonBytes, nil +} + +func buildAgentConfig(params VectorConfigParams, pipelines ...pipeline.Pipeline) (*VectorConfig, error) { + cfg := newVectorConfig(params) + + for _, pipeline := range pipelines { + p := &PipelineConfig{} + if err := UnmarshalJson(pipeline.GetSpec(), p); err != nil { + return nil, fmt.Errorf("failed to unmarshal pipeline %s: %w", pipeline.GetName(), err) + } + for k, v := range p.Sources { + // Validate source + if _, ok := pipeline.(*vectorv1alpha1.VectorPipeline); ok { + if v.Type != KubernetesLogsType { + return nil, ErrNotAllowedSourceType + } + _, err := labels.Parse(v.ExtraLabelSelector) + if err != nil { + return nil, fmt.Errorf("invalid pod selector for source %s: %w", k, err) + } + _, err = labels.Parse(v.ExtraNamespaceLabelSelector) + if err != nil { + return nil, fmt.Errorf("invalid namespace selector for source %s: %w", k, err) + } + if v.ExtraNamespaceLabelSelector == "" { + v.ExtraNamespaceLabelSelector = k8s.NamespaceNameToLabel(pipeline.GetNamespace()) + } + if v.ExtraNamespaceLabelSelector != k8s.NamespaceNameToLabel(pipeline.GetNamespace()) { + return nil, ErrClusterScopeNotAllowed + } + } + if v.Type == KubernetesLogsType && params.UseApiServerCache { + v.UseApiServerCache = true + } + v.Name = addPrefix(pipeline.GetNamespace(), pipeline.GetName(), k) + cfg.Sources[v.Name] = v + } + for k, v := range p.Transforms { + v.Name = addPrefix(pipeline.GetNamespace(), pipeline.GetName(), k) + for i, inputName := range v.Inputs { + v.Inputs[i] = addPrefix(pipeline.GetNamespace(), pipeline.GetName(), inputName) + } + cfg.Transforms[v.Name] = v + } + for k, v := range p.Sinks { + v.Name = addPrefix(pipeline.GetNamespace(), pipeline.GetName(), k) + for i, inputName := range v.Inputs { + v.Inputs[i] = addPrefix(pipeline.GetNamespace(), pipeline.GetName(), inputName) + } + cfg.Sinks[v.Name] = v + } + } + + // Add exporter pipeline + if params.InternalMetrics && !isExporterSinkExists(cfg.Sinks) { + cfg.Sources[DefaultInternalMetricsSourceName] = defaultInternalMetricsSource + cfg.Sinks[DefaultInternalMetricsSinkName] = defaultInternalMetricsSink + } + + if len(cfg.Sources) == 0 && len(cfg.Sinks) == 0 { + cfg.PipelineConfig = defaultAgentPipelineConfig + } + + return cfg, nil +} diff --git a/internal/config/aggregator.go b/internal/config/aggregator.go new file mode 100644 index 00000000..6c6b98de --- /dev/null +++ b/internal/config/aggregator.go @@ -0,0 +1,113 @@ +package config + +import ( + "errors" + "fmt" + "github.com/kaasops/vector-operator/internal/common" + "github.com/kaasops/vector-operator/internal/pipeline" + corev1 "k8s.io/api/core/v1" + "net" + "strconv" + "strings" +) + +func BuildAggregatorConfig(params VectorConfigParams, pipelines ...pipeline.Pipeline) (*VectorConfig, error) { + cfg := newVectorConfig(params) + + cfg.Sources = make(map[string]*Source) + cfg.Transforms = make(map[string]*Transform) + cfg.Sinks = make(map[string]*Sink) + + cfg.internal.servicePort = make(map[string]*ServicePort) + + for _, pipeline := range pipelines { + p := &PipelineConfig{} + if err := UnmarshalJson(pipeline.GetSpec(), p); err != nil { + return nil, fmt.Errorf("failed to unmarshal pipeline %s: %w", pipeline.GetName(), err) + } + for k, v := range p.Sources { + settings := v + + if val, ok := v.Options["address"]; ok { + address, _ := val.(string) + if _, port, err := net.SplitHostPort(address); err == nil { + portN, err := parsePort(port) + if err != nil { + return nil, fmt.Errorf("failed to parse port %s: %w", port, err) + } + protocol := extractProtocol(v.Options) + err = cfg.internal.addServicePort(&ServicePort{ + Port: portN, + Protocol: protocol, + Namespace: pipeline.GetNamespace(), + SourceName: k, + PipelineName: pipeline.GetName(), + ServiceName: pipeline.GetAnnotations()[common.AnnotationServiceName], + }) + if err != nil { + return nil, err + } + } + } + + v.Name = addPrefix(pipeline.GetNamespace(), pipeline.GetName(), k) + cfg.Sources[v.Name] = settings + } + for k, v := range p.Transforms { + v.Name = addPrefix(pipeline.GetNamespace(), pipeline.GetName(), k) + for i, inputName := range v.Inputs { + v.Inputs[i] = addPrefix(pipeline.GetNamespace(), pipeline.GetName(), inputName) + } + cfg.Transforms[v.Name] = v + } + for k, v := range p.Sinks { + v.Name = addPrefix(pipeline.GetNamespace(), pipeline.GetName(), k) + for i, inputName := range v.Inputs { + v.Inputs[i] = addPrefix(pipeline.GetNamespace(), pipeline.GetName(), inputName) + } + cfg.Sinks[v.Name] = v + } + } + + // Add exporter pipeline + if params.InternalMetrics && !isExporterSinkExists(cfg.Sinks) { + cfg.Sources[DefaultInternalMetricsSourceName] = defaultInternalMetricsSource + cfg.Sinks[DefaultInternalMetricsSinkName] = defaultInternalMetricsSink + } + if len(cfg.Sources) == 0 && len(cfg.Sinks) == 0 { + cfg.PipelineConfig = defaultAggregatorPipelineConfig + err := cfg.internal.addServicePort(&ServicePort{ + Port: DefaultAggregatorSourcePort, + Protocol: corev1.ProtocolTCP, + Namespace: DefaultNamespace, + SourceName: DefaultInternalMetricsSourceName, + PipelineName: DefaultPipelineName, + }) + if err != nil { + return nil, err + } + } + + return cfg, nil +} + +func parsePort(port string) (int32, error) { + p, err := strconv.ParseInt(port, 10, 32) + if err != nil { + return 0, err + } + if p < 0 || p > 65535 { + return 0, errors.New("port out of range") + } + return int32(p), nil +} + +func extractProtocol(opts map[string]any) corev1.Protocol { + protocol := corev1.ProtocolTCP + if val, ok := opts["mode"]; ok { + if s, ok := val.(string); ok && strings.ToLower(s) == "udp" { + return corev1.ProtocolUDP + } + } + return protocol +} diff --git a/internal/config/config.go b/internal/config/config.go index 25d04aa8..eacfaed2 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -20,32 +20,36 @@ import ( "encoding/json" "errors" "fmt" - "strconv" - vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" - "github.com/kaasops/vector-operator/internal/pipeline" - "github.com/kaasops/vector-operator/internal/utils/k8s" "github.com/kaasops/vector-operator/internal/vector/vectoragent" "github.com/mitchellh/mapstructure" "gopkg.in/yaml.v2" - "k8s.io/apimachinery/pkg/labels" + "net" goyaml "sigs.k8s.io/yaml" + "strconv" ) var ( - ErrNotAllowedSourceType error = errors.New("type kubernetes_logs only allowed") - ErrClusterScopeNotAllowed error = errors.New("logs from external namespace not allowed") + ErrNotAllowedSourceType = errors.New("type kubernetes_logs only allowed") + ErrClusterScopeNotAllowed = errors.New("logs from external namespace not allowed") ) -func New(vector *vectorv1alpha1.Vector) *VectorConfig { +type VectorConfigParams struct { + ApiEnabled bool + PlaygroundEnabled bool + UseApiServerCache bool + InternalMetrics bool +} + +func newVectorConfig(p VectorConfigParams) *VectorConfig { sources := make(map[string]*Source) transforms := make(map[string]*Transform) sinks := make(map[string]*Sink) api := &ApiSpec{ - Address: "0.0.0.0:" + strconv.Itoa(vectoragent.ApiPort), - Enabled: vector.Spec.Agent.Api.Enabled, - Playground: vector.Spec.Agent.Api.Playground, + Address: net.JoinHostPort("0.0.0.0", strconv.Itoa(vectoragent.ApiPort)), + Enabled: p.ApiEnabled, + Playground: p.PlaygroundEnabled, } return &VectorConfig{ @@ -59,86 +63,6 @@ func New(vector *vectorv1alpha1.Vector) *VectorConfig { } } -func BuildByteConfig(vaCtrl *vectoragent.Controller, pipelines ...pipeline.Pipeline) ([]byte, error) { - config, err := BuildConfig(vaCtrl, pipelines...) - if err != nil { - return nil, err - } - yaml_byte, err := yaml.Marshal(config) - if err != nil { - return nil, err - } - json_byte, err := goyaml.YAMLToJSON(yaml_byte) - if err != nil { - return nil, err - } - return json_byte, nil -} - -func BuildConfig(vaCtrl *vectoragent.Controller, pipelines ...pipeline.Pipeline) (*VectorConfig, error) { - config := New(vaCtrl.Vector) - - for _, pipeline := range pipelines { - p := &PipelineConfig{} - if err := UnmarshalJson(pipeline.GetSpec(), p); err != nil { - return nil, fmt.Errorf("failed to unmarshal pipeline %s: %w", pipeline.GetName(), err) - } - for k, v := range p.Sources { - // Validate source - if _, ok := pipeline.(*vectorv1alpha1.VectorPipeline); ok { - if v.Type != KubernetesSourceType { - return nil, ErrNotAllowedSourceType - } - _, err := labels.Parse(v.ExtraLabelSelector) - if err != nil { - return nil, fmt.Errorf("invalid pod selector for source %s: %w", k, err) - } - _, err = labels.Parse(v.ExtraNamespaceLabelSelector) - if err != nil { - return nil, fmt.Errorf("invalid namespace selector for source %s: %w", k, err) - } - if v.ExtraNamespaceLabelSelector == "" { - v.ExtraNamespaceLabelSelector = k8s.NamespaceNameToLabel(pipeline.GetNamespace()) - } - if v.ExtraNamespaceLabelSelector != k8s.NamespaceNameToLabel(pipeline.GetNamespace()) { - return nil, ErrClusterScopeNotAllowed - } - } - if v.Type == KubernetesSourceType && vaCtrl.Vector.Spec.UseApiServerCache { - v.UseApiServerCache = true - } - v.Name = addPrefix(pipeline.GetNamespace(), pipeline.GetName(), k) - config.Sources[v.Name] = v - } - for k, v := range p.Transforms { - v.Name = addPrefix(pipeline.GetNamespace(), pipeline.GetName(), k) - for i, inputName := range v.Inputs { - v.Inputs[i] = addPrefix(pipeline.GetNamespace(), pipeline.GetName(), inputName) - } - config.Transforms[v.Name] = v - } - for k, v := range p.Sinks { - v.Name = addPrefix(pipeline.GetNamespace(), pipeline.GetName(), k) - for i, inputName := range v.Inputs { - v.Inputs[i] = addPrefix(pipeline.GetNamespace(), pipeline.GetName(), inputName) - } - config.Sinks[v.Name] = v - } - } - - // Add exporter pipeline - if vaCtrl.Vector.Spec.Agent.InternalMetrics && !isExporterSinkExists(config.Sinks) { - config.Sources[DefaultInternalMetricsSourceName] = defaultInternalMetricsSource - config.Sinks[DefaultInternalMetricsSinkName] = defaultInternalMetricsSink - } - - if len(config.Sources) == 0 && len(config.Sinks) == 0 { - config.PipelineConfig = defaultPipelineConfig - } - - return config, nil -} - func UnmarshalJson(spec vectorv1alpha1.VectorPipelineSpec, p *PipelineConfig) error { b, err := json.Marshal(spec) if err != nil { @@ -162,9 +86,68 @@ func UnmarshalJson(spec vectorv1alpha1.VectorPipelineSpec, p *PipelineConfig) er func isExporterSinkExists(sinks map[string]*Sink) bool { for _, sink := range sinks { - if sink.Type == InternalMetricsSinkType { + if sink.Type == PrometheusExporterType { return true } } return false } + +func (c *VectorConfig) MarshalJSON() ([]byte, error) { + yamlByte, err := yaml.Marshal(c) + if err != nil { + return nil, err + } + jsonByte, err := goyaml.YAMLToJSON(yamlByte) + if err != nil { + return nil, err + } + return jsonByte, nil +} + +func (c *PipelineConfig) VectorRole() (*vectorv1alpha1.VectorPipelineRole, error) { + if len(c.Sources) == 0 { + return nil, fmt.Errorf("sources list is empty") + } + agentCount := 0 + aggregatorCount := 0 + for _, s := range c.Sources { + switch { + case isAgent(s.Type): + agentCount++ + fallthrough // some types can be both an agent and an aggregator at the same time + case isAggregator(s.Type): + aggregatorCount++ + default: + return nil, fmt.Errorf("unsupported source type: %s", s.Type) + } + } + switch { + case len(c.Sources) == agentCount: + role := vectorv1alpha1.VectorPipelineRoleAgent + return &role, nil + case len(c.Sources) == aggregatorCount: + role := vectorv1alpha1.VectorPipelineRoleAggregator + return &role, nil + } + return nil, fmt.Errorf("unknown vector role") +} + +type SPGroup struct { + PipelineName string + Namespace string + ServiceName string +} + +func (c *VectorConfig) GetSourcesServicePorts() map[SPGroup][]*ServicePort { + m := make(map[SPGroup][]*ServicePort) + for _, s := range c.internal.servicePort { + spg := SPGroup{ + PipelineName: s.PipelineName, + Namespace: s.Namespace, + ServiceName: s.ServiceName, + } + m[spg] = append(m[spg], s) + } + return m +} diff --git a/internal/config/configcheck/configcheck.go b/internal/config/configcheck/configcheck.go index 2cf979dd..d6118ae5 100644 --- a/internal/config/configcheck/configcheck.go +++ b/internal/config/configcheck/configcheck.go @@ -19,6 +19,7 @@ package configcheck import ( "context" "errors" + api_errors "k8s.io/apimachinery/pkg/api/errors" "math/rand" "time" @@ -66,46 +67,47 @@ func New( config []byte, c client.Client, cs *kubernetes.Clientset, - va *vectorv1alpha1.Vector, + vc *vectorv1alpha1.VectorCommon, + name, namespace string, timeout time.Duration, initiator string, ) *ConfigCheck { - image := va.Spec.Agent.Image - if va.Spec.Agent.ConfigCheck.Image != nil { - image = *va.Spec.Agent.ConfigCheck.Image + image := vc.Image + if vc.ConfigCheck.Image != nil { + image = *vc.ConfigCheck.Image } - env := va.Spec.Agent.Env + env := vc.Env - tolerations := va.Spec.Agent.Tolerations - if va.Spec.Agent.ConfigCheck.Tolerations != nil { - tolerations = *va.Spec.Agent.ConfigCheck.Tolerations + tolerations := vc.Tolerations + if vc.ConfigCheck.Tolerations != nil { + tolerations = *vc.ConfigCheck.Tolerations } - resources := va.Spec.Agent.Resources - if va.Spec.Agent.ConfigCheck.Resources != nil { - resources = *va.Spec.Agent.ConfigCheck.Resources + resources := vc.Resources + if vc.ConfigCheck.Resources != nil { + resources = *vc.ConfigCheck.Resources } return &ConfigCheck{ Config: config, Client: c, ClientSet: cs, - Name: va.Name, - Namespace: va.Namespace, + Name: name, + Namespace: namespace, Image: image, - ImagePullPolicy: va.Spec.Agent.ImagePullPolicy, - ImagePullSecrets: va.Spec.Agent.ImagePullSecrets, + ImagePullPolicy: vc.ImagePullPolicy, + ImagePullSecrets: vc.ImagePullSecrets, Envs: env, Tolerations: tolerations, Resources: resources, - SecurityContext: va.Spec.Agent.SecurityContext, - ContainerSecurityContext: va.Spec.Agent.ContainerSecurityContext, - CompressedConfig: va.Spec.Agent.CompressConfigFile, - ConfigReloaderImage: va.Spec.Agent.ConfigReloaderImage, - ConfigReloaderResources: va.Spec.Agent.ConfigReloaderResources, + SecurityContext: vc.SecurityContext, + ContainerSecurityContext: vc.ContainerSecurityContext, + CompressedConfig: vc.CompressConfigFile, + ConfigReloaderImage: vc.ConfigReloaderImage, + ConfigReloaderResources: vc.ConfigReloaderResources, ConfigCheckTimeout: timeout, - Annotations: va.Spec.Agent.ConfigCheck.Annotations, + Annotations: vc.ConfigCheck.Annotations, Initiator: initiator, } } @@ -114,7 +116,7 @@ func (cc *ConfigCheck) Run(ctx context.Context) (string, error) { log := log.FromContext(ctx).WithValues("Vector ConfigCheck", cc.Initiator) log.Info("================= Started ConfigCheck =================") - if err := cc.ensureVectorConfigCheckRBAC(ctx); err != nil { + if err := cc.ensureVectorConfigCheckRBAC(ctx); err != nil && !api_errors.IsAlreadyExists(err) { // TODO(aa1ex): error is silenced, is that ok? return "", err } diff --git a/internal/config/configcheck/configcheck_config.go b/internal/config/configcheck/configcheck_config.go index a8f37a5c..b7846150 100644 --- a/internal/config/configcheck/configcheck_config.go +++ b/internal/config/configcheck/configcheck_config.go @@ -28,14 +28,14 @@ import ( func (cc *ConfigCheck) createVectorConfigCheckConfig(ctx context.Context) (*corev1.Secret, error) { log := log.FromContext(ctx).WithValues("Vector ConfigCheck", cc.Initiator) labels := labelsForVectorConfigCheck() - var data []byte = cc.Config + var data = cc.Config if cc.CompressedConfig { data = compression.Compress(cc.Config, log) } config := map[string][]byte{ - "agent.json": data, + "config.json": data, } secret := &corev1.Secret{ diff --git a/internal/config/default.go b/internal/config/default.go index 6a8fadd3..380a154a 100644 --- a/internal/config/default.go +++ b/internal/config/default.go @@ -1,23 +1,33 @@ package config +import "fmt" + const ( // types - KubernetesSourceType = "kubernetes_logs" - BlackholeSinkType = "blackhole" - InternalMetricsSourceType = "internal_metrics" - InternalMetricsSinkType = "prometheus_exporter" + BlackholeSinkType = "blackhole" + PrometheusExporterType = "prometheus_exporter" // default names DefaultSourceName = "defaultSource" DefaultSinkName = "defaultSink" DefaultInternalMetricsSourceName = "internalMetricsSource" DefaultInternalMetricsSinkName = "internalMetricsSink" + DefaultAggregatorSourcePort = 8989 + DefaultNamespace = "default" + DefaultPipelineName = "default-pipeline" ) var ( - defaultSource = &Source{ + defaultAgentSource = &Source{ Name: DefaultSourceName, - Type: KubernetesSourceType, + Type: KubernetesLogsType, + } + defaultAggregatorSource = &Source{ + Name: DefaultSourceName, + Type: VectorType, + Options: map[string]any{ + "address": fmt.Sprintf("0.0.0.0:%d", DefaultAggregatorSourcePort), + }, } defaultSink = &Sink{ Name: DefaultSinkName, @@ -28,9 +38,17 @@ var ( "print_interval_secs": 60, }, } - defaultPipelineConfig = PipelineConfig{ + defaultAgentPipelineConfig = PipelineConfig{ + Sources: map[string]*Source{ + DefaultSourceName: defaultAgentSource, + }, + Sinks: map[string]*Sink{ + DefaultSinkName: defaultSink, + }, + } + defaultAggregatorPipelineConfig = PipelineConfig{ Sources: map[string]*Source{ - DefaultSourceName: defaultSource, + DefaultSourceName: defaultAggregatorSource, }, Sinks: map[string]*Sink{ DefaultSinkName: defaultSink, @@ -39,11 +57,11 @@ var ( defaultInternalMetricsSource = &Source{ Name: DefaultInternalMetricsSourceName, - Type: InternalMetricsSourceType, + Type: InternalMetricsType, } defaultInternalMetricsSink = &Sink{ Name: DefaultInternalMetricsSinkName, - Type: InternalMetricsSinkType, + Type: PrometheusExporterType, Inputs: []string{DefaultInternalMetricsSourceName}, } ) diff --git a/internal/config/types.go b/internal/config/types.go index 4116e725..fa94d539 100644 --- a/internal/config/types.go +++ b/internal/config/types.go @@ -16,10 +16,16 @@ limitations under the License. package config +import ( + "fmt" + corev1 "k8s.io/api/core/v1" +) + type VectorConfig struct { DataDir string `yaml:"data_dir"` Api *ApiSpec `yaml:"api"` PipelineConfig `yaml:",inline"` + internal internalConfig `yaml:"-"` } type PipelineConfig struct { @@ -35,13 +41,13 @@ type ApiSpec struct { } type Source struct { - Name string `yaml:"-"` - Type string `mapstructure:"type" yaml:"type"` - ExtraNamespaceLabelSelector string `mapstructure:"extra_namespace_label_selector" yaml:"extra_namespace_label_selector,omitempty"` - ExtraLabelSelector string `mapstructure:"extra_label_selector" yaml:"extra_label_selector,omitempty"` - ExtraFieldSelector string `mapstructure:"extra_field_selector" yaml:"extra_field_selector,omitempty"` - UseApiServerCache bool `mapstructure:"use_apiserver_cache" yaml:"use_apiserver_cache,omitempty"` - Options map[string]interface{} `mapstructure:",remain" yaml:",inline,omitempty"` + Name string `yaml:"-"` + Type string `mapstructure:"type" yaml:"type"` + ExtraNamespaceLabelSelector string `mapstructure:"extra_namespace_label_selector" yaml:"extra_namespace_label_selector,omitempty"` + ExtraLabelSelector string `mapstructure:"extra_label_selector" yaml:"extra_label_selector,omitempty"` + ExtraFieldSelector string `mapstructure:"extra_field_selector" yaml:"extra_field_selector,omitempty"` + UseApiServerCache bool `mapstructure:"use_apiserver_cache" yaml:"use_apiserver_cache,omitempty"` + Options map[string]any `mapstructure:",remain" yaml:",inline,omitempty"` } type Transform struct { @@ -64,3 +70,26 @@ type pipelineConfig_ struct { Transforms map[string]interface{} Sinks map[string]interface{} } + +type ServicePort struct { + PipelineName string + SourceName string + Namespace string + Port int32 + Protocol corev1.Protocol + ServiceName string +} + +type internalConfig struct { + servicePort map[string]*ServicePort +} + +func (c *internalConfig) addServicePort(port *ServicePort) error { + key := fmt.Sprintf("%d/%s", port.Port, port.Protocol) + if v, ok := c.servicePort[key]; !ok { + c.servicePort[key] = port + } else { + return fmt.Errorf("duplicate port %s in %s and %s", key, v.PipelineName, port.PipelineName) + } + return nil +} diff --git a/internal/config/vector_source_types.go b/internal/config/vector_source_types.go new file mode 100644 index 00000000..281ef7be --- /dev/null +++ b/internal/config/vector_source_types.go @@ -0,0 +1,99 @@ +package config + +const ( + // types + AMQPType = "amqp" + ApacheMetricsType = "apache_metrics" + AWSKinesisFirehoseType = "aws_kinesis_firehose" + AWSS3Type = "aws_s3" + AWSSQSType = "aws_sqs" + DatadogAgentType = "datadog_agent" + DemoLogsType = "demo_logs" + DNSTapType = "dnstap" + DockerLogsType = "docker_logs" + EventStoreDBMetricsType = "eventstoredb_metrics" + FileType = "file" + FluentType = "fluent" + GCPPubSubType = "gcp_pubsub" + HerokuLogsType = "heroku_logs" + HostMetricsType = "host_metrics" + HTTPClientType = "http_client" + HTTPServerType = "http_server" + InternalLogsType = "internal_logs" + InternalMetricsType = "internal_metrics" + JournaldType = "journald" + KafkaType = "kafka" + KubernetesLogsType = "kubernetes_logs" + LogstashType = "logstash" + MongoDBMetricsType = "mongodb_metrics" + NATSType = "nats" + NginxMetricsType = "nginx_metrics" + OpenTelemetryType = "opentelemetry" + PostgreSQLMetricsType = "postgresql_metrics" + PrometheusPushgatewayType = "prometheus_pushgateway" + PrometheusRemoteWriteType = "prometheus_remote_write" + PrometheusScrapeType = "prometheus_scrape" + PulsarType = "pulsar" + RedisType = "redis" + SocketType = "socket" + SplunkHECType = "splunk_hec" + StatsDType = "statsd" + VectorType = "vector" +) + +var aggregatorTypes = map[string]struct{}{ + AMQPType: {}, + AWSS3Type: {}, + AWSSQSType: {}, + AWSKinesisFirehoseType: {}, + DatadogAgentType: {}, + FluentType: {}, + GCPPubSubType: {}, + HTTPClientType: {}, + HTTPServerType: {}, + HerokuLogsType: {}, + InternalLogsType: {}, + InternalMetricsType: {}, + KafkaType: {}, + LogstashType: {}, + NATSType: {}, + OpenTelemetryType: {}, + PulsarType: {}, + RedisType: {}, + SocketType: {}, + SplunkHECType: {}, + StatsDType: {}, + VectorType: {}, +} + +var agentTypes = map[string]struct{}{ + ApacheMetricsType: {}, + DNSTapType: {}, + DemoLogsType: {}, + DockerLogsType: {}, + EventStoreDBMetricsType: {}, + FileType: {}, + HTTPClientType: {}, + HostMetricsType: {}, + InternalLogsType: {}, + InternalMetricsType: {}, + JournaldType: {}, + KubernetesLogsType: {}, + MongoDBMetricsType: {}, + NginxMetricsType: {}, + OpenTelemetryType: {}, + PostgreSQLMetricsType: {}, + PrometheusPushgatewayType: {}, + PrometheusRemoteWriteType: {}, + PrometheusScrapeType: {}, +} + +func isAggregator(name string) bool { + _, ok := aggregatorTypes[name] + return ok +} + +func isAgent(name string) bool { + _, ok := agentTypes[name] + return ok +} diff --git a/internal/controller/pipeline_controller.go b/internal/controller/pipeline_controller.go index 797e6253..c5b37ec6 100644 --- a/internal/controller/pipeline_controller.go +++ b/internal/controller/pipeline_controller.go @@ -20,15 +20,16 @@ import ( "context" "errors" "fmt" + "github.com/kaasops/vector-operator/internal/config/configcheck" + "github.com/kaasops/vector-operator/internal/vector/aggregator" + "github.com/kaasops/vector-operator/internal/vector/vectoragent" "golang.org/x/sync/errgroup" "sigs.k8s.io/controller-runtime/pkg/controller" "time" "github.com/kaasops/vector-operator/api/v1alpha1" "github.com/kaasops/vector-operator/internal/config" - "github.com/kaasops/vector-operator/internal/config/configcheck" "github.com/kaasops/vector-operator/internal/pipeline" - "github.com/kaasops/vector-operator/internal/vector/vectoragent" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes" ctrl "sigs.k8s.io/controller-runtime" @@ -36,7 +37,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/event" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/predicate" ) type PipelineReconciler struct { @@ -44,9 +44,10 @@ type PipelineReconciler struct { Scheme *runtime.Scheme // Temp. Wait this issue - https://github.com/kubernetes-sigs/controller-runtime/issues/452 - Clientset *kubernetes.Clientset - ConfigCheckTimeout time.Duration - VectorAgentEventCh chan event.GenericEvent + Clientset *kubernetes.Clientset + ConfigCheckTimeout time.Duration + VectorAgentEventCh chan event.GenericEvent + VectorAggregatorsEventCh chan event.GenericEvent } var ( @@ -68,11 +69,16 @@ func (r *PipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c } vectorAgents, err := listVectorAgents(ctx, r.Client) if err != nil { - log.Error(err, "Failed to get Instances") + log.Error(err, "Failed to get vector agents") + return ctrl.Result{}, nil + } + vectorAggregators, err := listVectorAggregators(ctx, r.Client) + if err != nil { + log.Error(err, "Failed to get vector aggregators") return ctrl.Result{}, nil } - if len(vectorAgents) == 0 { + if len(vectorAgents) == 0 && len(vectorAggregators) == 0 { log.Info("Vectors not found") return ctrl.Result{}, nil } @@ -82,45 +88,117 @@ func (r *PipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c for _, vector := range vectorAgents { r.VectorAgentEventCh <- event.GenericEvent{Object: vector} } + for _, vector := range vectorAggregators { + r.VectorAggregatorsEventCh <- event.GenericEvent{Object: vector} + } return ctrl.Result{}, nil } - // Check Pipeline hash - checkResult, err := pipeline.CheckHash(pipelineCR) + notChanged, err := pipeline.IsPipelineChanged(pipelineCR) if err != nil { return ctrl.Result{}, err } - if checkResult { + if notChanged { log.Info("Pipeline has no changes. Finish Reconcile Pipeline") return ctrl.Result{}, nil } + p := &config.PipelineConfig{} + if err := config.UnmarshalJson(pipelineCR.GetSpec(), p); err != nil { + return ctrl.Result{}, fmt.Errorf("failed to unmarshal pipeline %s: %w", pipelineCR.GetName(), err) + } + pipelineVectorRole, err := p.VectorRole() + if err != nil { + if err := pipeline.SetFailedStatus(ctx, r.Client, pipelineCR, err.Error()); err != nil { + log.Error(err, "Failed to set pipeline status") + return ctrl.Result{}, err + } + log.Error(err, "Failed to determine pipeline role") + return ctrl.Result{}, nil + } + pipelineCR.SetRole(pipelineVectorRole) + eg := errgroup.Group{} - for _, vector := range vectorAgents { - eg.Go(func() error { - vaCtrl := vectoragent.NewController(vector, r.Client, r.Clientset) - byteConfig, err := config.BuildByteConfig(vaCtrl, pipelineCR) - if err != nil { - return fmt.Errorf("agent %s/%s build config failed: %w: %w", vector.Namespace, vector.Name, ErrBuildConfigFailed, err) - } - - vaCtrl.Config = byteConfig - configCheck := configcheck.New( - vaCtrl.Config, - vaCtrl.Client, - vaCtrl.ClientSet, - vaCtrl.Vector, - r.ConfigCheckTimeout, - configcheck.ConfigCheckInitiatorPipieline, - ) - - reason, err := configCheck.Run(ctx) - if reason != "" { - return fmt.Errorf("agent %s/%s config check failed: %s", vector.Namespace, vector.Name, reason) - } - return err - }) + if *pipelineVectorRole == v1alpha1.VectorPipelineRoleAgent { + + for _, vector := range vectorAgents { + eg.Go(func() error { + vaCtrl := vectoragent.NewController(vector, r.Client, r.Clientset) + byteConfig, err := config.BuildAgentConfig(config.VectorConfigParams{ + ApiEnabled: vaCtrl.Vector.Spec.Agent.Api.Enabled, + PlaygroundEnabled: vaCtrl.Vector.Spec.Agent.Api.Playground, + UseApiServerCache: vaCtrl.Vector.Spec.UseApiServerCache, + InternalMetrics: vaCtrl.Vector.Spec.Agent.InternalMetrics, + }, pipelineCR) + if err != nil { + return fmt.Errorf("agent %s/%s build config failed: %w: %w", vector.Namespace, vector.Name, ErrBuildConfigFailed, err) + } + + vaCtrl.Config = byteConfig + configCheck := configcheck.New( + vaCtrl.Config, + vaCtrl.Client, + vaCtrl.ClientSet, + &vaCtrl.Vector.Spec.Agent.VectorCommon, + vaCtrl.Vector.Name, + vaCtrl.Vector.Namespace, + r.ConfigCheckTimeout, + configcheck.ConfigCheckInitiatorPipieline, + ) + + reason, err := configCheck.Run(ctx) + if reason != "" { + return fmt.Errorf("agent %s/%s config check failed: %s", vector.Namespace, vector.Name, reason) + } + return err + }) + } + + } else { + + for _, vector := range vectorAggregators { + eg.Go(func() error { + vaCtrl := aggregator.NewController(vector, r.Client, r.Clientset) + cfg, err := config.BuildAggregatorConfig(config.VectorConfigParams{ + ApiEnabled: vaCtrl.VectorAggregator.Spec.Api.Enabled, + PlaygroundEnabled: vaCtrl.VectorAggregator.Spec.Api.Playground, + InternalMetrics: vaCtrl.VectorAggregator.Spec.InternalMetrics, + }, pipelineCR) + if err != nil { + return fmt.Errorf("aggregator %s/%s build config failed: %w: %w", vector.Namespace, vector.Name, ErrBuildConfigFailed, err) + } + if err != nil { + return err + } + + byteConfig, err := cfg.MarshalJSON() + if err != nil { + return err + } + + vaCtrl.ConfigBytes = byteConfig + vaCtrl.Config = cfg + + configCheck := configcheck.New( + vaCtrl.ConfigBytes, + vaCtrl.Client, + vaCtrl.ClientSet, + &vaCtrl.VectorAggregator.Spec.VectorCommon, + vaCtrl.VectorAggregator.Name, + vaCtrl.VectorAggregator.Namespace, + r.ConfigCheckTimeout, + configcheck.ConfigCheckInitiatorPipieline, + ) + + reason, err := configCheck.Run(ctx) + if reason != "" { + return fmt.Errorf("aggregator %s/%s config check failed: %s", vector.Namespace, vector.Name, reason) + } + return err + }) + } + } if err = eg.Wait(); err != nil { @@ -138,6 +216,9 @@ func (r *PipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c for _, vector := range vectorAgents { r.VectorAgentEventCh <- event.GenericEvent{Object: vector} } + for _, vector := range vectorAggregators { + r.VectorAggregatorsEventCh <- event.GenericEvent{Object: vector} + } log.Info("finish Reconcile Pipeline") return ctrl.Result{}, nil @@ -166,7 +247,6 @@ func (r *PipelineReconciler) SetupWithManager(mgr ctrl.Manager) error { For(&v1alpha1.VectorPipeline{}). WithOptions(controller.Options{MaxConcurrentReconciles: 20}). Watches(&v1alpha1.ClusterVectorPipeline{}, &handler.EnqueueRequestForObject{}). - WithEventFilter(predicate.GenerationChangedPredicate{}). Complete(r) } diff --git a/internal/controller/suite_test.go b/internal/controller/suite_test.go index ae92768a..decb5ad0 100644 --- a/internal/controller/suite_test.go +++ b/internal/controller/suite_test.go @@ -19,13 +19,14 @@ package controller import ( "context" "fmt" - "k8s.io/client-go/kubernetes" - "k8s.io/utils/pointer" "path/filepath" "runtime" "testing" "time" + "k8s.io/client-go/kubernetes" + "k8s.io/utils/pointer" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" diff --git a/internal/controller/vector_controller.go b/internal/controller/vector_controller.go index a166409d..bc47b9fa 100644 --- a/internal/controller/vector_controller.go +++ b/internal/controller/vector_controller.go @@ -152,7 +152,7 @@ func (r *VectorReconciler) findVectorCustomResourceInstance(ctx context.Context, } return nil, err } - setTypeMetaIfNeeded(vectorCR) + setAgentTypeMetaIfNeeded(vectorCR) return vectorCR, nil } @@ -165,7 +165,7 @@ func (r *VectorReconciler) reconcileVectors(ctx context.Context, client client.C if vector.DeletionTimestamp != nil { continue } - setTypeMetaIfNeeded(vector) + setAgentTypeMetaIfNeeded(vector) if _, err := r.createOrUpdateVector(ctx, client, clientset, vector, configOnly); err != nil { return ctrl.Result{}, err } @@ -179,15 +179,28 @@ func (r *VectorReconciler) createOrUpdateVector(ctx context.Context, client clie vaCtrl := vectoragent.NewController(v, client, clientset) // Get Vector Config file - pipelines, err := pipeline.GetValidPipelines(ctx, vaCtrl.Client, vaCtrl.Vector.Spec.Selector) + pipelines, err := pipeline.GetValidPipelines(ctx, vaCtrl.Client, pipeline.FilterPipelines{ + Scope: pipeline.AllPipelines, + Selector: vaCtrl.Vector.Spec.Selector, + Role: v1alpha1.VectorPipelineRoleAgent, + }) if err != nil { return ctrl.Result{}, err } // Get Config in Json ([]byte) - byteConfig, err := config.BuildByteConfig(vaCtrl, pipelines...) + byteConfig, err := config.BuildAgentConfig(config.VectorConfigParams{ + ApiEnabled: vaCtrl.Vector.Spec.Agent.Api.Enabled, + PlaygroundEnabled: vaCtrl.Vector.Spec.Agent.Api.Playground, + UseApiServerCache: vaCtrl.Vector.Spec.UseApiServerCache, + InternalMetrics: vaCtrl.Vector.Spec.Agent.InternalMetrics, + }, pipelines...) if err != nil { - return ctrl.Result{}, err + if err := vaCtrl.SetFailedStatus(ctx, err.Error()); err != nil { + return ctrl.Result{}, err + } + log.Error(err, "Build config failed") + return ctrl.Result{}, nil } cfgHash := hash.Get(byteConfig) @@ -197,7 +210,9 @@ func (r *VectorReconciler) createOrUpdateVector(ctx context.Context, client clie byteConfig, vaCtrl.Client, vaCtrl.ClientSet, - vaCtrl.Vector, + &vaCtrl.Vector.Spec.Agent.VectorCommon, + vaCtrl.Vector.Name, + vaCtrl.Vector.Namespace, r.ConfigCheckTimeout, configcheck.ConfigCheckInitiatorVector, ) @@ -233,7 +248,7 @@ func (r *VectorReconciler) createOrUpdateVector(ctx context.Context, client clie return ctrl.Result{}, nil } -func setTypeMetaIfNeeded(cr *v1alpha1.Vector) { +func setAgentTypeMetaIfNeeded(cr *v1alpha1.Vector) { // https://github.com/kubernetes/kubernetes/issues/80609 if cr.Kind == "" || cr.APIVersion == "" { cr.Kind = "Vector" diff --git a/internal/controller/vectoraggregator_controller.go b/internal/controller/vectoraggregator_controller.go new file mode 100644 index 00000000..3e3e4a3b --- /dev/null +++ b/internal/controller/vectoraggregator_controller.go @@ -0,0 +1,277 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "context" + "errors" + "github.com/kaasops/vector-operator/internal/config" + "github.com/kaasops/vector-operator/internal/config/configcheck" + "github.com/kaasops/vector-operator/internal/pipeline" + "github.com/kaasops/vector-operator/internal/utils/hash" + "github.com/kaasops/vector-operator/internal/utils/k8s" + "github.com/kaasops/vector-operator/internal/vector/aggregator" + monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + api_errors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/client-go/kubernetes" + "sigs.k8s.io/controller-runtime/pkg/builder" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/predicate" + "sigs.k8s.io/controller-runtime/pkg/source" + "time" + + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/kaasops/vector-operator/api/v1alpha1" +) + +const aggregatorFinalizerName = "vectoraggregator.observability.kaasops.io/finalizer" + +// VectorAggregatorReconciler reconciles a VectorAggregator object +type VectorAggregatorReconciler struct { + client.Client + Scheme *runtime.Scheme + + Clientset *kubernetes.Clientset + ConfigCheckTimeout time.Duration + EventChan chan event.GenericEvent +} + +// +kubebuilder:rbac:groups=observability.kaasops.io,resources=vectoraggregators,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=observability.kaasops.io,resources=vectoraggregators/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=observability.kaasops.io,resources=vectoraggregators/finalizers,verbs=update + +// +kubebuilder:rbac:groups="",resources=serviceaccounts,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups="",resources=services,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups="",resources=pods,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups="",resources=pods/log,verbs=get;list +// +kubebuilder:rbac:groups="",resources=namespaces,verbs=list;watch +// +kubebuilder:rbac:groups="",resources=nodes,verbs=list;watch +// +kubebuilder:rbac:groups="",resources=events,verbs=list;watch +// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups="rbac.authorization.k8s.io",resources=clusterrolebindings,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups="rbac.authorization.k8s.io",resources=clusterroles,verbs=get;list;watch;create;update;patch;delete + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the VectorAggregator object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.0/pkg/reconcile +func (r *VectorAggregatorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + log := log.FromContext(ctx) + log.Info("Start Reconcile VectorAggregator") + + if req.Namespace == "" { // cluster resources (ClusterRole and ClusterRoleBinding) don't have ns + vectorAggregators, err := listVectorAggregators(ctx, r.Client) + if err != nil { + log.Error(err, "Failed to list vector aggregators instances") + return ctrl.Result{}, err + } + filtered := make([]*v1alpha1.VectorAggregator, 0, len(vectorAggregators)) + for _, vector := range vectorAggregators { + if vector.Name == req.Name { + filtered = append(filtered, vector) + } + } + return r.reconcileVectorAggregators(ctx, r.Client, r.Clientset, filtered...) + } + + vectorCR := &v1alpha1.VectorAggregator{} + err := r.Get(ctx, req.NamespacedName, vectorCR) + if err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } + setAggregatorTypeMetaIfNeeded(vectorCR) + + if vectorCR.IsBeingDeleted() { + if controllerutil.ContainsFinalizer(vectorCR, aggregatorFinalizerName) { + if err := r.deleteVectorAggregator(ctx, vectorCR); err != nil { + if !api_errors.IsNotFound(err) { + return ctrl.Result{}, err + } + } + controllerutil.RemoveFinalizer(vectorCR, aggregatorFinalizerName) + if err := r.Update(ctx, vectorCR); err != nil { + return ctrl.Result{}, err + } + } + return ctrl.Result{}, nil + } + + if !vectorCR.HasFinalizer(aggregatorFinalizerName) { + controllerutil.AddFinalizer(vectorCR, aggregatorFinalizerName) + if err := r.Update(ctx, vectorCR); err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{}, nil + } + + return r.createOrUpdateVectorAggregator(ctx, r.Client, r.Clientset, vectorCR) +} + +// SetupWithManager sets up the controller with the Manager. +func (r *VectorAggregatorReconciler) SetupWithManager(mgr ctrl.Manager) error { + monitoringCRD, err := k8s.ResourceExists(r.Clientset.DiscoveryClient, monitorv1.SchemeGroupVersion.String(), monitorv1.PodMonitorsKind) + if err != nil { + return err + } + + builder := ctrl.NewControllerManagedBy(mgr). + For(&v1alpha1.VectorAggregator{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})). + WatchesRawSource(source.Channel(r.EventChan, &handler.EnqueueRequestForObject{})). + Owns(&appsv1.Deployment{}). + Owns(&corev1.Service{}). + Owns(&corev1.Secret{}). + Owns(&corev1.ServiceAccount{}). + Owns(&rbacv1.ClusterRole{}). + Owns(&rbacv1.ClusterRoleBinding{}) + + if monitoringCRD { + builder.Owns(&monitorv1.PodMonitor{}) + } + + if err = builder.Complete(r); err != nil { + return err + } + return nil +} + +func listVectorAggregators(ctx context.Context, client client.Client) (vectors []*v1alpha1.VectorAggregator, err error) { + vectorList := v1alpha1.VectorAggregatorList{} + err = client.List(ctx, &vectorList) + if err != nil { + return nil, err + } + for _, v := range vectorList.Items { + setAggregatorTypeMetaIfNeeded(&v) + vectors = append(vectors, &v) + } + return vectors, nil +} + +func (r *VectorAggregatorReconciler) createOrUpdateVectorAggregator(ctx context.Context, client client.Client, clientset *kubernetes.Clientset, v *v1alpha1.VectorAggregator) (ctrl.Result, error) { + log := log.FromContext(ctx).WithValues("VectorAggregator", v.Name) + vaCtrl := aggregator.NewController(v, client, clientset) + + pipelines, err := pipeline.GetValidPipelines(ctx, vaCtrl.Client, pipeline.FilterPipelines{ + Scope: pipeline.NamespacedPipeline, + Selector: v.Spec.Selector, + Role: v1alpha1.VectorPipelineRoleAggregator, + Namespace: vaCtrl.VectorAggregator.Namespace, + }) + if err != nil { + return ctrl.Result{}, err + } + + cfg, err := config.BuildAggregatorConfig(config.VectorConfigParams{ + ApiEnabled: vaCtrl.VectorAggregator.Spec.Api.Enabled, + PlaygroundEnabled: vaCtrl.VectorAggregator.Spec.Api.Playground, + InternalMetrics: vaCtrl.VectorAggregator.Spec.InternalMetrics, + }, pipelines...) + if err != nil { + if err := vaCtrl.SetFailedStatus(ctx, err.Error()); err != nil { + return ctrl.Result{}, err + } + log.Error(err, "Build config failed") + return ctrl.Result{}, nil + } + + byteCfg, err := cfg.MarshalJSON() + if err != nil { + return ctrl.Result{}, err + } + cfgHash := hash.Get(byteCfg) + + if !vaCtrl.VectorAggregator.Spec.ConfigCheck.Disabled { + if vaCtrl.VectorAggregator.Status.LastAppliedConfigHash == nil || *vaCtrl.VectorAggregator.Status.LastAppliedConfigHash != cfgHash { + reason, err := configcheck.New( + byteCfg, + vaCtrl.Client, + vaCtrl.ClientSet, + &vaCtrl.VectorAggregator.Spec.VectorCommon, + vaCtrl.VectorAggregator.Name, + vaCtrl.VectorAggregator.Namespace, + r.ConfigCheckTimeout, + configcheck.ConfigCheckInitiatorVector, + ).Run(ctx) + if err != nil { + if errors.Is(err, configcheck.ValidationError) { + if err := vaCtrl.SetFailedStatus(ctx, reason); err != nil { + return ctrl.Result{}, err + } + log.Error(err, "Invalid config") + return ctrl.Result{}, nil + } + return ctrl.Result{}, err + } + } + } + + vaCtrl.ConfigBytes = byteCfg + vaCtrl.Config = cfg + + if err := vaCtrl.EnsureVectorAggregator(ctx); err != nil { + return ctrl.Result{}, err + } + + if err := vaCtrl.SetSuccessStatus(ctx, &cfgHash); err != nil { + return ctrl.Result{}, err + } + + return ctrl.Result{}, nil +} + +func (r *VectorAggregatorReconciler) reconcileVectorAggregators(ctx context.Context, c client.Client, clientset *kubernetes.Clientset, aggregators ...*v1alpha1.VectorAggregator) (ctrl.Result, error) { + if len(aggregators) == 0 { + return ctrl.Result{}, nil + } + + for _, ag := range aggregators { + if ag.DeletionTimestamp != nil { + continue + } + if _, err := r.createOrUpdateVectorAggregator(ctx, c, clientset, ag); err != nil { + return ctrl.Result{}, err + } + } + return ctrl.Result{}, nil +} + +func (r *VectorAggregatorReconciler) deleteVectorAggregator(ctx context.Context, v *v1alpha1.VectorAggregator) error { + return aggregator.NewController(v, r.Client, r.Clientset).DeleteVectorAggregator(ctx) +} + +func setAggregatorTypeMetaIfNeeded(cr *v1alpha1.VectorAggregator) { + // https://github.com/kubernetes/kubernetes/issues/80609 + if cr.Kind == "" || cr.APIVersion == "" { + cr.Kind = "VectorAggregator" + cr.APIVersion = "observability.kaasops.io/v1alpha1" + } +} diff --git a/internal/controller/vectoraggregator_controller_test.go b/internal/controller/vectoraggregator_controller_test.go new file mode 100644 index 00000000..d65b3f0e --- /dev/null +++ b/internal/controller/vectoraggregator_controller_test.go @@ -0,0 +1,100 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "context" + "sigs.k8s.io/controller-runtime/pkg/event" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + observabilityv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" +) + +var _ = Describe("VectorAggregator Controller", func() { + Context("When reconciling a resource", func() { + const resourceName = "test-resource" + + ctx := context.Background() + + typeNamespacedName := types.NamespacedName{ + Name: resourceName, + Namespace: "default", // TODO(user):Modify as needed + } + vectoraggregator := &observabilityv1alpha1.VectorAggregator{} + + BeforeEach(func() { + By("creating the custom resource for the Kind VectorAggregator") + err := k8sClient.Get(ctx, typeNamespacedName, vectoraggregator) + if err != nil && errors.IsNotFound(err) { + resource := &observabilityv1alpha1.VectorAggregator{ + ObjectMeta: metav1.ObjectMeta{ + Name: resourceName, + Namespace: "default", + }, + // TODO(user): Specify other spec details if needed. + } + Expect(k8sClient.Create(ctx, resource)).To(Succeed()) + } + }) + + AfterEach(func() { + // TODO(user): Cleanup logic after each test, like removing the resource instance. + resource := &observabilityv1alpha1.VectorAggregator{} + err := k8sClient.Get(ctx, typeNamespacedName, resource) + Expect(err).NotTo(HaveOccurred()) + + By("Cleanup the specific resource instance VectorAggregator") + Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) + controllerReconciler := &VectorAggregatorReconciler{ + Client: k8sClient, + Scheme: k8sClient.Scheme(), + Clientset: clientset, + ConfigCheckTimeout: time.Second * 10, + EventChan: make(chan event.GenericEvent, 1), + } + // remove finalizer + _, err = controllerReconciler.Reconcile(ctx, reconcile.Request{ + NamespacedName: typeNamespacedName, + }) + Expect(err).NotTo(HaveOccurred()) + }) + It("should successfully reconcile the resource", func() { + By("Reconciling the created resource") + controllerReconciler := &VectorAggregatorReconciler{ + Client: k8sClient, + Scheme: k8sClient.Scheme(), + Clientset: clientset, + ConfigCheckTimeout: time.Second * 10, + EventChan: make(chan event.GenericEvent, 1), + } + _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ + NamespacedName: typeNamespacedName, + }) + Expect(err).NotTo(HaveOccurred()) + // TODO(user): Add more specific assertions depending on your controller's reconciliation logic. + // Example: If you expect a certain status condition after reconciliation, verify it here. + }) + }) +}) diff --git a/internal/pipeline/hash.go b/internal/pipeline/hash.go index 6a4415ef..84c6178b 100644 --- a/internal/pipeline/hash.go +++ b/internal/pipeline/hash.go @@ -18,12 +18,23 @@ package pipeline import ( "encoding/json" - + "github.com/kaasops/vector-operator/api/v1alpha1" + "github.com/kaasops/vector-operator/internal/common" "github.com/kaasops/vector-operator/internal/utils/hash" ) -func GetSpecHash(pipeline Pipeline) (*uint32, error) { - a, err := json.Marshal(pipeline.GetSpec()) +type tmp struct { + Spec v1alpha1.VectorPipelineSpec + Labels map[string]string + ServiceName string +} + +func GetPipelineHash(pipeline Pipeline) (*uint32, error) { + a, err := json.Marshal(tmp{ + Spec: pipeline.GetSpec(), + Labels: pipeline.GetLabels(), + ServiceName: pipeline.GetAnnotations()[common.AnnotationServiceName], + }) if err != nil { return nil, err } @@ -31,9 +42,9 @@ func GetSpecHash(pipeline Pipeline) (*uint32, error) { return &hash, nil } -// CheckHash returns true, if hash in .status.lastAppliedPipelineHash matches with spec Hash -func CheckHash(pipeline Pipeline) (bool, error) { - hash, err := GetSpecHash(pipeline) +// IsPipelineChanged returns true, if hash in .status.lastAppliedPipelineHash matches with spec Hash +func IsPipelineChanged(pipeline Pipeline) (bool, error) { + hash, err := GetPipelineHash(pipeline) if err != nil { return false, err } diff --git a/internal/pipeline/pipeline.go b/internal/pipeline/pipeline.go index 58a9bdf6..f2678499 100644 --- a/internal/pipeline/pipeline.go +++ b/internal/pipeline/pipeline.go @@ -18,14 +18,16 @@ package pipeline import ( "context" + "fmt" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" + "github.com/kaasops/vector-operator/api/v1alpha1" "sigs.k8s.io/controller-runtime/pkg/client" ) type Pipeline interface { client.Object - GetSpec() vectorv1alpha1.VectorPipelineSpec + GetSpec() v1alpha1.VectorPipelineSpec SetConfigCheck(bool) SetReason(*string) GetLastAppliedPipeline() *uint32 @@ -34,29 +36,70 @@ type Pipeline interface { IsValid() bool IsDeleted() bool UpdateStatus(context.Context, client.Client) error + GetRole() v1alpha1.VectorPipelineRole + SetRole(*v1alpha1.VectorPipelineRole) + GetTypeMeta() v1.TypeMeta } -func GetValidPipelines(ctx context.Context, client client.Client, selector vectorv1alpha1.VectorSelectorSpec) ([]Pipeline, error) { +type FilterPipelines struct { + Scope FilterScope + Selector *v1alpha1.VectorSelectorSpec + Role v1alpha1.VectorPipelineRole + Namespace string +} + +type FilterScope int + +const ( + AllPipelines FilterScope = iota + NamespacedPipeline FilterScope = iota + ClusterPipelines FilterScope = iota +) + +func GetValidPipelines(ctx context.Context, client client.Client, filter FilterPipelines) ([]Pipeline, error) { var validPipelines []Pipeline - vps, err := GetVectorPipelines(ctx, client) - if err != nil { - return nil, err - } - cvps, err := GetClusterVectorPipelines(ctx, client) - if err != nil { - return nil, err + + matchLabels := map[string]string{} + if filter.Selector != nil && filter.Selector.MatchLabels != nil { + matchLabels = filter.Selector.MatchLabels } - if len(vps) != 0 { - for _, vp := range vps { - if !vp.IsDeleted() && vp.IsValid() && MatchLabels(selector.MatchLabels, vp.Labels) { - validPipelines = append(validPipelines, vp.DeepCopy()) + + if filter.Scope == AllPipelines || filter.Scope == NamespacedPipeline { + + if filter.Scope == NamespacedPipeline && filter.Namespace == "" { + return nil, fmt.Errorf("namespace not specified") + } + + vps, err := GetVectorPipelines(ctx, client) + if err != nil { + return nil, err + } + if len(vps) != 0 { + for _, vp := range vps { + if !vp.IsDeleted() && + vp.IsValid() && + vp.GetRole() == filter.Role && + vp.Namespace == filter.Namespace && + MatchLabels(matchLabels, vp.Labels) { + validPipelines = append(validPipelines, vp.DeepCopy()) + } } } } - if len(cvps) != 0 { - for _, cvp := range cvps { - if !cvp.IsDeleted() && cvp.IsValid() && MatchLabels(selector.MatchLabels, cvp.Labels) { - validPipelines = append(validPipelines, cvp.DeepCopy()) + + if filter.Scope == AllPipelines || filter.Scope == ClusterPipelines { + cvps, err := GetClusterVectorPipelines(ctx, client) + if err != nil { + return nil, err + } + if len(cvps) != 0 { + for _, cvp := range cvps { + if !cvp.IsDeleted() && + cvp.IsValid() && + cvp.GetRole() == filter.Role && + MatchLabels(matchLabels, cvp.Labels) { + validPipelines = append(validPipelines, cvp.DeepCopy()) + } } } } @@ -66,7 +109,7 @@ func GetValidPipelines(ctx context.Context, client client.Client, selector vecto func SetSuccessStatus(ctx context.Context, client client.Client, p Pipeline) error { p.SetConfigCheck(true) p.SetReason(nil) - hash, err := GetSpecHash(p) + hash, err := GetPipelineHash(p) if err != nil { return err } @@ -79,7 +122,7 @@ func SetFailedStatus(ctx context.Context, client client.Client, p Pipeline, reas p.SetConfigCheck(false) p.SetReason(&reason) - hash, err := GetSpecHash(p) + hash, err := GetPipelineHash(p) if err != nil { return err } @@ -88,16 +131,16 @@ func SetFailedStatus(ctx context.Context, client client.Client, p Pipeline, reas return p.UpdateStatus(ctx, client) } -func GetVectorPipelines(ctx context.Context, client client.Client) ([]vectorv1alpha1.VectorPipeline, error) { - vps := vectorv1alpha1.VectorPipelineList{} +func GetVectorPipelines(ctx context.Context, client client.Client) ([]v1alpha1.VectorPipeline, error) { + vps := v1alpha1.VectorPipelineList{} if err := client.List(ctx, &vps); err != nil { return nil, err } return vps.Items, nil } -func GetClusterVectorPipelines(ctx context.Context, client client.Client) ([]vectorv1alpha1.ClusterVectorPipeline, error) { - cvps := vectorv1alpha1.ClusterVectorPipelineList{} +func GetClusterVectorPipelines(ctx context.Context, client client.Client) ([]v1alpha1.ClusterVectorPipeline, error) { + cvps := v1alpha1.ClusterVectorPipelineList{} if err := client.List(ctx, &cvps); err != nil { return nil, err } diff --git a/internal/vector/aggregator/config.go b/internal/vector/aggregator/config.go new file mode 100644 index 00000000..d800860e --- /dev/null +++ b/internal/vector/aggregator/config.go @@ -0,0 +1,38 @@ +package aggregator + +import ( + "context" + "github.com/kaasops/vector-operator/internal/utils/compression" + "github.com/kaasops/vector-operator/internal/utils/k8s" + corev1 "k8s.io/api/core/v1" + "sigs.k8s.io/controller-runtime/pkg/log" +) + +func (ctrl *Controller) ensureVectorAggregatorConfig(ctx context.Context) error { + log := log.FromContext(ctx).WithValues("vector-aggregator-secret", ctrl.VectorAggregator.Name) + log.Info("start Reconcile Vector Aggregator Secret") + vectorAggregatorSecret, err := ctrl.createVectorAggregatorConfig(ctx) + if err != nil { + return err + } + return k8s.CreateOrUpdateResource(ctx, vectorAggregatorSecret, ctrl.Client) +} + +func (ctrl *Controller) createVectorAggregatorConfig(ctx context.Context) (*corev1.Secret, error) { + log := log.FromContext(ctx).WithValues("vector-aggregator-config", ctrl.VectorAggregator.Name) + labels := ctrl.labelsForVectorAggregator() + annotations := ctrl.annotationsForVectorAggregator() + data := ctrl.ConfigBytes + + if ctrl.VectorAggregator.Spec.CompressConfigFile { + data = compression.Compress(ctrl.ConfigBytes, log) + } + config := map[string][]byte{ + "config.json": data, + } + secret := &corev1.Secret{ + ObjectMeta: ctrl.objectMetaVectorAggregator(labels, annotations, ctrl.VectorAggregator.Namespace), + Data: config, + } + return secret, nil +} diff --git a/internal/vector/aggregator/controller.go b/internal/vector/aggregator/controller.go new file mode 100644 index 00000000..ca0d7c26 --- /dev/null +++ b/internal/vector/aggregator/controller.go @@ -0,0 +1,259 @@ +package aggregator + +import ( + "context" + vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" + "github.com/kaasops/vector-operator/internal/config" + "github.com/kaasops/vector-operator/internal/utils/k8s" + monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + corev1 "k8s.io/api/core/v1" + resourcev1 "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/client-go/kubernetes" + "k8s.io/utils/ptr" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" +) + +type Controller struct { + client.Client + VectorAggregator *vectorv1alpha1.VectorAggregator + ConfigBytes []byte + Config *config.VectorConfig + ClientSet *kubernetes.Clientset +} + +func NewController(v *vectorv1alpha1.VectorAggregator, c client.Client, cs *kubernetes.Clientset) *Controller { + ctrl := &Controller{ + Client: c, + VectorAggregator: v, + ClientSet: cs, + } + ctrl.setDefault() + return ctrl +} + +func (ctrl *Controller) EnsureVectorAggregator(ctx context.Context) error { + log := log.FromContext(ctx).WithValues("vector-aggregator", ctrl.VectorAggregator.Name) + log.Info("start Reconcile Vector Aggregator") + + monitoringCRD, err := k8s.ResourceExists(ctrl.ClientSet.Discovery(), monitorv1.SchemeGroupVersion.String(), monitorv1.PodMonitorsKind) + if err != nil { + return err + } + + if err := ctrl.ensureVectorAggregatorConfig(ctx); err != nil { + return err + } + + if err := ctrl.ensureVectorAggregatorRBAC(ctx); err != nil { + return err + } + + if err := ctrl.ensureVectorAggregatorService(ctx); err != nil { + return err + } + + if ctrl.VectorAggregator.Spec.InternalMetrics && monitoringCRD { + if err := ctrl.ensureVectorAggregatorPodMonitor(ctx); err != nil { + return err + } + } + + if err := ctrl.ensureVectorAggregatorDeployment(ctx); err != nil { + return err + } + return nil +} + +func (ctrl *Controller) DeleteVectorAggregator(ctx context.Context) error { + if err := ctrl.deleteVectorAggregatorClusterRole(ctx); err != nil { + return err + } + if err := ctrl.deleteVectorAggregatorClusterRoleBinding(ctx); err != nil { + return err + } + return nil +} + +func (ctrl *Controller) setDefault() { + if ctrl.VectorAggregator.Spec.Image == "" { + ctrl.VectorAggregator.Spec.Image = "timberio/vector:0.28.1-distroless-libc" + } + + if ctrl.VectorAggregator.Spec.Resources.Requests == nil { + ctrl.VectorAggregator.Spec.Resources.Requests = corev1.ResourceList{ + corev1.ResourceMemory: resourcev1.MustParse("200Mi"), + corev1.ResourceCPU: resourcev1.MustParse("100m"), + } + } + if ctrl.VectorAggregator.Spec.Resources.Limits == nil { + ctrl.VectorAggregator.Spec.Resources.Limits = corev1.ResourceList{ + corev1.ResourceMemory: resourcev1.MustParse("1024Mi"), + corev1.ResourceCPU: resourcev1.MustParse("1000m"), + } + } + + if ctrl.VectorAggregator.Spec.DataDir == "" { + ctrl.VectorAggregator.Spec.DataDir = "/var/lib/vector" + } + + if ctrl.VectorAggregator.Spec.Volumes == nil { + ctrl.VectorAggregator.Spec.Volumes = []corev1.Volume{ + { + Name: "var-log", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/var/log/", + }, + }, + }, + { + Name: "journal", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/var/log/journal", + }, + }, + }, + { + Name: "var-lib", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/var/lib/", + }, + }, + }, + } + } + + if ctrl.VectorAggregator.Spec.ReadinessProbe == nil && ctrl.VectorAggregator.Spec.Api.Enabled && ctrl.VectorAggregator.Spec.Api.Healthcheck { + ctrl.VectorAggregator.Spec.ReadinessProbe = &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/health", + Port: intstr.IntOrString{ + Type: intstr.Type(0), + IntVal: 8686, + }, + }, + }, + PeriodSeconds: 20, + InitialDelaySeconds: 15, + TimeoutSeconds: 3, + SuccessThreshold: 0, + FailureThreshold: 0, + } + } + if ctrl.VectorAggregator.Spec.LivenessProbe == nil && ctrl.VectorAggregator.Spec.Api.Enabled && ctrl.VectorAggregator.Spec.Api.Healthcheck { + ctrl.VectorAggregator.Spec.LivenessProbe = &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/health", + Port: intstr.IntOrString{ + Type: intstr.Type(0), + IntVal: 8686, + }, + }, + }, + PeriodSeconds: 20, + InitialDelaySeconds: 15, + TimeoutSeconds: 3, + SuccessThreshold: 0, + FailureThreshold: 0, + } + } + + if ctrl.VectorAggregator.Spec.VolumeMounts == nil { + ctrl.VectorAggregator.Spec.VolumeMounts = []corev1.VolumeMount{ + { + Name: "var-log", + MountPath: "/var/log/", + }, + { + Name: "journal", + MountPath: "/run/log/journal", + }, + { + Name: "var-lib", + MountPath: "/var/lib/", + }, + } + } + if ctrl.VectorAggregator.Spec.CompressConfigFile && ctrl.VectorAggregator.Spec.ConfigReloaderImage == "" { + ctrl.VectorAggregator.Spec.ConfigReloaderImage = "docker.io/kaasops/config-reloader:v0.1.4" + } + if ctrl.VectorAggregator.Spec.CompressConfigFile && ctrl.VectorAggregator.Spec.ConfigReloaderResources.Requests == nil { + ctrl.VectorAggregator.Spec.ConfigReloaderResources.Requests = corev1.ResourceList{ + corev1.ResourceMemory: resourcev1.MustParse("200Mi"), + corev1.ResourceCPU: resourcev1.MustParse("100m"), + } + } + if ctrl.VectorAggregator.Spec.CompressConfigFile && ctrl.VectorAggregator.Spec.ConfigReloaderResources.Limits == nil { + ctrl.VectorAggregator.Spec.ConfigReloaderResources.Limits = corev1.ResourceList{ + corev1.ResourceMemory: resourcev1.MustParse("1024Mi"), + corev1.ResourceCPU: resourcev1.MustParse("1000m"), + } + } +} + +func (ctrl *Controller) SetSuccessStatus(ctx context.Context, hash *uint32) error { + var status = true + ctrl.VectorAggregator.Status.ConfigCheckResult = &status + ctrl.VectorAggregator.Status.Reason = nil + ctrl.VectorAggregator.Status.LastAppliedConfigHash = hash + return k8s.UpdateStatus(ctx, ctrl.VectorAggregator, ctrl.Client) +} + +func (ctrl *Controller) SetFailedStatus(ctx context.Context, reason string) error { + var status = false + ctrl.VectorAggregator.Status.ConfigCheckResult = &status + ctrl.VectorAggregator.Status.Reason = &reason + return k8s.UpdateStatus(ctx, ctrl.VectorAggregator, ctrl.Client) +} + +func (ctrl *Controller) labelsForVectorAggregator() map[string]string { + return map[string]string{ + k8s.ManagedByLabelKey: "vector-operator", + k8s.NameLabelKey: "vector", + k8s.ComponentLabelKey: "Aggregator", + k8s.InstanceLabelKey: ctrl.VectorAggregator.Name, + } +} + +func (ctrl *Controller) annotationsForVectorAggregator() map[string]string { + return ctrl.VectorAggregator.Spec.Annotations +} + +func (ctrl *Controller) objectMetaVectorAggregator(labels map[string]string, annotations map[string]string, namespace string) metav1.ObjectMeta { + return metav1.ObjectMeta{ + Name: ctrl.getNameVectorAggregator(), + Namespace: namespace, + Labels: labels, + Annotations: annotations, + OwnerReferences: ctrl.getControllerReference(), + } +} + +func (ctrl *Controller) getNameVectorAggregator() string { + name := ctrl.VectorAggregator.Name + "-aggregator" + return name +} + +func (ctrl *Controller) getControllerReference() []metav1.OwnerReference { + return []metav1.OwnerReference{ + { + APIVersion: ctrl.VectorAggregator.APIVersion, + Kind: ctrl.VectorAggregator.Kind, + Name: ctrl.VectorAggregator.GetName(), + UID: ctrl.VectorAggregator.GetUID(), + BlockOwnerDeletion: ptr.To(true), + Controller: ptr.To(true), + }, + } +} + +func (ctrl *Controller) GetServiceName() string { + return ctrl.getNameVectorAggregator() +} diff --git a/internal/vector/aggregator/deployment.go b/internal/vector/aggregator/deployment.go new file mode 100644 index 00000000..e434f21c --- /dev/null +++ b/internal/vector/aggregator/deployment.go @@ -0,0 +1,268 @@ +package aggregator + +import ( + "context" + "github.com/kaasops/vector-operator/internal/utils/k8s" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/log" +) + +func (ctrl *Controller) ensureVectorAggregatorDeployment(ctx context.Context) error { + log := log.FromContext(ctx).WithValues("vector-aggregator-deployment", ctrl.VectorAggregator.Name) + log.Info("start Reconcile Vector Aggregator Deployment") + return k8s.CreateOrUpdateResource(ctx, ctrl.createVectorAggregatorDeployment(), ctrl.Client) +} + +func (ctrl *Controller) createVectorAggregatorDeployment() *appsv1.Deployment { + labels := ctrl.labelsForVectorAggregator() + annotations := ctrl.annotationsForVectorAggregator() + var initContainers []corev1.Container + var containers []corev1.Container + containers = append(containers, *ctrl.VectorAggregatorContainer()) + + if ctrl.VectorAggregator.Spec.CompressConfigFile { + initContainers = append(initContainers, *ctrl.ConfigReloaderInitContainer()) + } + + if ctrl.VectorAggregator.Spec.CompressConfigFile { + containers = append(containers, *ctrl.ConfigReloaderSidecarContainer()) + } + + deployment := &appsv1.Deployment{ + ObjectMeta: ctrl.objectMetaVectorAggregator(labels, annotations, ctrl.VectorAggregator.Namespace), + Spec: appsv1.DeploymentSpec{ + Selector: &metav1.LabelSelector{MatchLabels: labels}, + Replicas: &ctrl.VectorAggregator.Spec.Replicas, + Template: corev1.PodTemplateSpec{ + ObjectMeta: ctrl.objectMetaVectorAggregator(labels, annotations, ctrl.VectorAggregator.Namespace), + Spec: corev1.PodSpec{ + ServiceAccountName: ctrl.getNameVectorAggregator(), + Volumes: ctrl.generateVectorAggregatorVolume(), + SecurityContext: ctrl.VectorAggregator.Spec.SecurityContext, + ImagePullSecrets: ctrl.VectorAggregator.Spec.ImagePullSecrets, + Affinity: ctrl.VectorAggregator.Spec.Affinity, + RuntimeClassName: ctrl.VectorAggregator.Spec.RuntimeClassName, + SchedulerName: ctrl.VectorAggregator.Spec.SchedulerName, + Tolerations: ctrl.VectorAggregator.Spec.Tolerations, + PriorityClassName: ctrl.VectorAggregator.Spec.PodSecurityPolicyName, + HostNetwork: ctrl.VectorAggregator.Spec.HostNetwork, + HostAliases: ctrl.VectorAggregator.Spec.HostAliases, + InitContainers: initContainers, + Containers: containers, + }, + }, + }, + } + + return deployment +} + +func (ctrl *Controller) VectorAggregatorContainer() *corev1.Container { + return &corev1.Container{ + Name: ctrl.getNameVectorAggregator(), + Image: ctrl.VectorAggregator.Spec.Image, + Args: []string{"--config-dir", "/etc/vector", "--watch-config"}, + Env: ctrl.generateVectorAggregatorEnvs(), + Ports: []corev1.ContainerPort{ + { + Name: "prom-exporter", + ContainerPort: 9598, + Protocol: "TCP", + }, + }, + VolumeMounts: ctrl.generateVectorAggregatorVolumeMounts(), + ReadinessProbe: ctrl.VectorAggregator.Spec.ReadinessProbe, + LivenessProbe: ctrl.VectorAggregator.Spec.LivenessProbe, + Resources: ctrl.VectorAggregator.Spec.Resources, + SecurityContext: ctrl.VectorAggregator.Spec.ContainerSecurityContext, + ImagePullPolicy: ctrl.VectorAggregator.Spec.ImagePullPolicy, + } +} + +func (ctrl *Controller) ConfigReloaderInitContainer() *corev1.Container { + return &corev1.Container{ + Name: "init-config-reloader", + Image: ctrl.VectorAggregator.Spec.ConfigReloaderImage, + ImagePullPolicy: ctrl.VectorAggregator.Spec.ImagePullPolicy, + Resources: ctrl.VectorAggregator.Spec.ConfigReloaderResources, + SecurityContext: ctrl.VectorAggregator.Spec.ContainerSecurityContext, + Args: []string{ + "--init-mode=true", + "--volume-dir-archive=/tmp/archive", + "--dir-for-unarchive=/etc/vector", + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "config", + MountPath: "/etc/vector", + }, + { + Name: "app-config-compress", + MountPath: "/tmp/archive", + }, + }, + } +} + +func (ctrl *Controller) ConfigReloaderSidecarContainer() *corev1.Container { + return &corev1.Container{ + Name: "config-reloader", + Image: ctrl.VectorAggregator.Spec.ConfigReloaderImage, + ImagePullPolicy: ctrl.VectorAggregator.Spec.ImagePullPolicy, + Resources: ctrl.VectorAggregator.Spec.ConfigReloaderResources, + SecurityContext: ctrl.VectorAggregator.Spec.ContainerSecurityContext, + Args: []string{ + "--init-mode=false", + "--volume-dir-archive=/tmp/archive", + "--dir-for-unarchive=/etc/vector", + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "config", + MountPath: "/etc/vector", + }, + { + Name: "app-config-compress", + MountPath: "/tmp/archive", + }, + }, + } +} + +func (ctrl *Controller) generateVectorAggregatorVolume() []corev1.Volume { + volume := ctrl.VectorAggregator.Spec.Volumes + configVolumeSource := corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: ctrl.getNameVectorAggregator(), + }, + } + if ctrl.VectorAggregator.Spec.CompressConfigFile { + configVolumeSource = corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + } + + } + volume = append(volume, []corev1.Volume{ + { + Name: "config", + VolumeSource: configVolumeSource, + }, + { + Name: "data", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: ctrl.VectorAggregator.Spec.DataDir, + }, + }, + }, + { + Name: "procfs", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/proc", + }, + }, + }, + { + Name: "sysfs", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/sys", + }, + }, + }, + }...) + + if ctrl.VectorAggregator.Spec.CompressConfigFile { + volume = append(volume, corev1.Volume{ + Name: "app-config-compress", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: ctrl.getNameVectorAggregator(), + }, + }, + }) + } + + return volume +} + +func (ctrl *Controller) generateVectorAggregatorVolumeMounts() []corev1.VolumeMount { + volumeMount := ctrl.VectorAggregator.Spec.VolumeMounts + + volumeMount = append(volumeMount, []corev1.VolumeMount{ + { + Name: "config", + MountPath: "/etc/vector", + }, + { + Name: "data", + MountPath: "/vector-data-dir", + }, + { + Name: "procfs", + MountPath: "/host/proc", + }, + { + Name: "sysfs", + MountPath: "/host/sys", + }, + }...) + + if ctrl.VectorAggregator.Spec.CompressConfigFile { + volumeMount = append(volumeMount, []corev1.VolumeMount{ + { + Name: "app-config-compress", + MountPath: "/tmp/archive", + }, + }...) + } + + return volumeMount +} + +func (ctrl *Controller) generateVectorAggregatorEnvs() []corev1.EnvVar { + envs := ctrl.VectorAggregator.Spec.Env + + envs = append(envs, []corev1.EnvVar{ + { + Name: "VECTOR_SELF_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + APIVersion: "v1", + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "VECTOR_SELF_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + APIVersion: "v1", + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "VECTOR_SELF_POD_NAMESPACE", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + APIVersion: "v1", + FieldPath: "metadata.namespace", + }, + }, + }, + { + Name: "PROCFS_ROOT", + Value: "/host/proc", + }, + { + Name: "SYSFS_ROOT", + Value: "/host/sys", + }, + }...) + + return envs +} diff --git a/internal/vector/aggregator/podmonitor.go b/internal/vector/aggregator/podmonitor.go new file mode 100644 index 00000000..cc067d2f --- /dev/null +++ b/internal/vector/aggregator/podmonitor.go @@ -0,0 +1,38 @@ +package aggregator + +import ( + "context" + "github.com/kaasops/vector-operator/internal/utils/k8s" + monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/log" +) + +func (ctrl *Controller) ensureVectorAggregatorPodMonitor(ctx context.Context) error { + log := log.FromContext(ctx).WithValues("vector-aggregator-podmonitor", ctrl.VectorAggregator.Name) + log.Info("start Reconcile Vector Aggregator PodMonitor") + vectorAggregatorPodMonitor := ctrl.createVectorAggregatorPodMonitor() + return k8s.CreateOrUpdateResource(ctx, vectorAggregatorPodMonitor, ctrl.Client) +} + +func (ctrl *Controller) createVectorAggregatorPodMonitor() *monitorv1.PodMonitor { + labels := ctrl.labelsForVectorAggregator() + annotations := ctrl.annotationsForVectorAggregator() + + podmonitor := &monitorv1.PodMonitor{ + ObjectMeta: ctrl.objectMetaVectorAggregator(labels, annotations, ctrl.VectorAggregator.Namespace), + Spec: monitorv1.PodMonitorSpec{ + PodMetricsEndpoints: []monitorv1.PodMetricsEndpoint{ + { + Path: "/metrics", + Port: "prom-exporter", + }, + }, + Selector: metav1.LabelSelector{ + MatchLabels: labels, + }, + }, + } + + return podmonitor +} diff --git a/internal/vector/aggregator/rbac.go b/internal/vector/aggregator/rbac.go new file mode 100644 index 00000000..3f52da6b --- /dev/null +++ b/internal/vector/aggregator/rbac.go @@ -0,0 +1,100 @@ +package aggregator + +import ( + "context" + "github.com/kaasops/vector-operator/internal/utils/k8s" + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + "sigs.k8s.io/controller-runtime/pkg/log" +) + +const ApiPort = 8686 + +func (ctrl *Controller) ensureVectorAggregatorRBAC(ctx context.Context) error { + log := log.FromContext(ctx).WithValues("vector-aggregator-rbac", ctrl.VectorAggregator.Name) + + log.Info("start Reconcile Vector Aggregator RBAC") + + if err := ctrl.ensureVectorAggregatorServiceAccount(ctx); err != nil { + return err + } + if err := ctrl.ensureVectorAggregatorClusterRole(ctx); err != nil { + return err + } + if err := ctrl.ensureVectorAggregatorClusterRoleBinding(ctx); err != nil { + return err + } + return nil +} + +func (ctrl *Controller) ensureVectorAggregatorServiceAccount(ctx context.Context) error { + return k8s.CreateOrUpdateResource(ctx, ctrl.createVectorAggregatorServiceAccount(), ctrl.Client) +} + +func (ctrl *Controller) ensureVectorAggregatorClusterRole(ctx context.Context) error { + return k8s.CreateOrUpdateResource(ctx, ctrl.createVectorAggregatorClusterRole(), ctrl.Client) +} + +func (ctrl *Controller) ensureVectorAggregatorClusterRoleBinding(ctx context.Context) error { + return k8s.CreateOrUpdateResource(ctx, ctrl.createVectorAggregatorClusterRoleBinding(), ctrl.Client) +} + +func (ctrl *Controller) createVectorAggregatorServiceAccount() *corev1.ServiceAccount { + labels := ctrl.labelsForVectorAggregator() + annotations := ctrl.annotationsForVectorAggregator() + + serviceAccount := &corev1.ServiceAccount{ + ObjectMeta: ctrl.objectMetaVectorAggregator(labels, annotations, ctrl.VectorAggregator.Namespace), + } + + return serviceAccount +} + +func (ctrl *Controller) createVectorAggregatorClusterRole() *rbacv1.ClusterRole { + labels := ctrl.labelsForVectorAggregator() + annotations := ctrl.annotationsForVectorAggregator() + + clusterRole := &rbacv1.ClusterRole{ + ObjectMeta: ctrl.objectMetaVectorAggregator(labels, annotations, ""), + Rules: []rbacv1.PolicyRule{ + { + APIGroups: []string{""}, + Resources: []string{"namespaces", "nodes", "pods"}, + Verbs: []string{"list", "watch"}, + }, + }, + } + + return clusterRole +} + +func (ctrl *Controller) createVectorAggregatorClusterRoleBinding() *rbacv1.ClusterRoleBinding { + labels := ctrl.labelsForVectorAggregator() + annotations := ctrl.annotationsForVectorAggregator() + + clusterRoleBinding := &rbacv1.ClusterRoleBinding{ + ObjectMeta: ctrl.objectMetaVectorAggregator(labels, annotations, ""), + RoleRef: rbacv1.RoleRef{ + Kind: "ClusterRole", + APIGroup: "rbac.authorization.k8s.io", + Name: ctrl.getNameVectorAggregator(), + }, + Subjects: []rbacv1.Subject{ + { + Kind: "ServiceAccount", + Name: ctrl.getNameVectorAggregator(), + Namespace: ctrl.VectorAggregator.Namespace, + }, + }, + } + + return clusterRoleBinding +} + +func (ctrl *Controller) deleteVectorAggregatorClusterRole(ctx context.Context) error { + return ctrl.Client.Delete(ctx, ctrl.createVectorAggregatorClusterRole()) +} + +func (ctrl *Controller) deleteVectorAggregatorClusterRoleBinding(ctx context.Context) error { + return ctrl.Client.Delete(ctx, ctrl.createVectorAggregatorClusterRoleBinding()) +} diff --git a/internal/vector/aggregator/service.go b/internal/vector/aggregator/service.go new file mode 100644 index 00000000..7f0bc350 --- /dev/null +++ b/internal/vector/aggregator/service.go @@ -0,0 +1,113 @@ +package aggregator + +import ( + "context" + "fmt" + "github.com/kaasops/vector-operator/internal/utils/k8s" + "github.com/stoewer/go-strcase" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/util/intstr" + "maps" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" +) + +func (ctrl *Controller) ensureVectorAggregatorService(ctx context.Context) error { + log := log.FromContext(ctx).WithValues("vector-aggregator-service", ctrl.VectorAggregator.Name) + log.Info("start Reconcile Vector Aggregator Service") + existing, err := ctrl.getExistingServices(ctx) + if err != nil { + return err + } + svcs, err := ctrl.createVectorAggregatorServices() + if err != nil { + return err + } + for _, svc := range svcs { + delete(existing, svc.Name) + if err := k8s.CreateOrUpdateResource(ctx, svc, ctrl.Client); err != nil { + return err + } + } + for _, svc := range existing { + if err := ctrl.Client.Delete(ctx, svc); err != nil { + return err + } + } + return nil +} + +func (ctrl *Controller) createVectorAggregatorServices() ([]*corev1.Service, error) { + labels := ctrl.labelsForVectorAggregator() + annotations := ctrl.annotationsForVectorAggregator() + if annotations == nil { + annotations = make(map[string]string) + } + + svcList := make([]*corev1.Service, 0) + + for group, list := range ctrl.Config.GetSourcesServicePorts() { + ann := make(map[string]string, len(annotations)) + maps.Copy(ann, annotations) + + ports := make([]corev1.ServicePort, 0, len(list)) + for _, sp := range list { + ports = append(ports, corev1.ServicePort{ + Name: strcase.KebabCase(sp.SourceName), + Protocol: sp.Protocol, + Port: sp.Port, + TargetPort: intstr.FromInt32(sp.Port), + }) + } + svc := &corev1.Service{ + ObjectMeta: ctrl.objectMetaVectorAggregator(labels, ann, ctrl.VectorAggregator.Namespace), + Spec: corev1.ServiceSpec{ + Ports: ports, + Selector: labels, + }, + } + name := group.ServiceName + if name == "" { + name = strcase.KebabCase(fmt.Sprintf("%s-%s", svc.ObjectMeta.Name, group.PipelineName)) + } + svc.ObjectMeta.Name = name + svcList = append(svcList, svc) + } + + if ctrl.VectorAggregator.Spec.Api.Enabled { + svcList = append(svcList, &corev1.Service{ + ObjectMeta: ctrl.objectMetaVectorAggregator(labels, annotations, ctrl.VectorAggregator.Namespace), + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: "api", + Protocol: corev1.ProtocolTCP, + Port: ApiPort, + TargetPort: intstr.FromInt32(ApiPort), + }, + }, + Selector: labels, + }, + }) + } + + return svcList, nil +} + +func (ctrl *Controller) getExistingServices(ctx context.Context) (map[string]*corev1.Service, error) { + svcList := corev1.ServiceList{} + opts := &client.ListOptions{ + Namespace: ctrl.VectorAggregator.Namespace, + LabelSelector: labels.Set(ctrl.labelsForVectorAggregator()).AsSelector(), + } + err := ctrl.Client.List(ctx, &svcList, opts) + if err != nil { + return nil, err + } + existing := make(map[string]*corev1.Service, len(svcList.Items)) + for _, svc := range svcList.Items { + existing[svc.Name] = &svc + } + return existing, nil +} From e80b76e0770276ebd3dd7b6cca7df5b214811cda Mon Sep 17 00:00:00 2001 From: aa1ex <115736127+aa1ex@users.noreply.github.com> Date: Wed, 2 Oct 2024 12:06:05 +0300 Subject: [PATCH 271/316] Kubernetes events (#151) Add kubernetes events collector Signed-off-by: Aleksandr Aleksandrov --- README.md | 2 +- buf.gen.yaml | 15 + buf.yaml | 9 + cmd/main.go | 7 + internal/common/annotations.go | 4 +- internal/config/aggregator.go | 58 +- internal/config/types.go | 13 +- internal/config/vector_source_types.go | 2 + internal/controller/pipeline_controller.go | 4 +- .../controller/vectoraggregator_controller.go | 6 +- internal/k8sevents/event.go | 54 + internal/k8sevents/manager.go | 73 + internal/k8sevents/watcher.go | 128 + internal/vector/aggregator/controller.go | 10 +- internal/vector/aggregator/service.go | 18 + internal/vector/gen/event.pb.go | 3516 +++++++++++++++++ internal/vector/gen/vector.pb.go | 391 ++ internal/vector/gen/vector_grpc.pb.go | 159 + proto/vector/event.proto | 240 ++ proto/vector/vector.proto | 27 + 20 files changed, 4713 insertions(+), 23 deletions(-) create mode 100644 buf.gen.yaml create mode 100644 buf.yaml create mode 100644 internal/k8sevents/event.go create mode 100644 internal/k8sevents/manager.go create mode 100644 internal/k8sevents/watcher.go create mode 100644 internal/vector/gen/event.pb.go create mode 100644 internal/vector/gen/vector.pb.go create mode 100644 internal/vector/gen/vector_grpc.pb.go create mode 100644 proto/vector/event.proto create mode 100644 proto/vector/vector.proto diff --git a/README.md b/README.md index a12f8b30..8c5fe131 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Connect us in Telegram - https://t.me/+Y0PzGa1d5DFiYThi - [x] Full support of vector config options - [x] Namespace isolation - [ ] Vector config optimization -- [ ] Vector aggregator support +- [x] Vector aggregator support [RoadMap](https://github.com/orgs/kaasops/projects/1) diff --git a/buf.gen.yaml b/buf.gen.yaml new file mode 100644 index 00000000..fa22a2dd --- /dev/null +++ b/buf.gen.yaml @@ -0,0 +1,15 @@ +version: v2 +managed: + enabled: true + override: + - file_option: go_package_prefix + value: github.com/kaasops/vector-operator/internal/vector/gen +plugins: + - remote: buf.build/protocolbuffers/go + out: internal/vector/gen + opt: paths=source_relative + - remote: buf.build/grpc/go + out: internal/vector/gen + opt: paths=source_relative +inputs: + - directory: proto \ No newline at end of file diff --git a/buf.yaml b/buf.yaml new file mode 100644 index 00000000..37379c34 --- /dev/null +++ b/buf.yaml @@ -0,0 +1,9 @@ +version: v2 +modules: + - path: proto/vector +lint: + use: + - DEFAULT +breaking: + use: + - FILE diff --git a/cmd/main.go b/cmd/main.go index f80cc533..733d237f 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -21,6 +21,7 @@ import ( "crypto/tls" "flag" "fmt" + "github.com/kaasops/vector-operator/internal/k8sevents" "os" "time" @@ -208,6 +209,8 @@ func main() { vectorAggregatorsPipelineEventCh := make(chan event.GenericEvent, 10) defer close(vectorAggregatorsPipelineEventCh) + evCollector := k8sevents.NewEventsCollector(clientset, ctrl.Log.WithName("kubernetes-events-collector")) + if err = (&controller.PipelineReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), @@ -215,6 +218,7 @@ func main() { ConfigCheckTimeout: configCheckTimeout, VectorAgentEventCh: vectorAgentsPipelineEventCh, VectorAggregatorsEventCh: vectorAggregatorsPipelineEventCh, + EventsCollector: evCollector, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "VectorPipeline") os.Exit(1) @@ -229,10 +233,12 @@ func main() { Scheme: mgr.GetScheme(), ConfigCheckTimeout: configCheckTimeout, EventChan: vectorAggregatorsEventCh, + EventsCollector: evCollector, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "VectorAggregator") os.Exit(1) } + // +kubebuilder:scaffold:builder go reconcileWithDelay(context.Background(), vectorAgentsPipelineEventCh, vectorAgentEventCh, time.Second*10) @@ -324,6 +330,7 @@ func reconcileWithDelay(ctx context.Context, in, out chan event.GenericEvent, de case <-ctx.Done(): return case ev := <-in: + ticker.Reset(delay) key := fmt.Sprintf("%s/%s", ev.Object.GetNamespace(), ev.Object.GetName()) if _, ok := store[key]; !ok { store[key] = ev diff --git a/internal/common/annotations.go b/internal/common/annotations.go index 32cedac3..a8ac618d 100644 --- a/internal/common/annotations.go +++ b/internal/common/annotations.go @@ -1,5 +1,7 @@ package common const ( - AnnotationServiceName = "observability.kaasops.io/service-name" + AnnotationServiceName = "observability.kaasops.io/service-name" + AnnotationK8sEventsNamespace = "observability.kaasops.io/k8s-events-namespace" + AnnotationK8sEventsPort = "observability.kaasops.io/k8s-events-port" ) diff --git a/internal/config/aggregator.go b/internal/config/aggregator.go index 6c6b98de..266e4e85 100644 --- a/internal/config/aggregator.go +++ b/internal/config/aggregator.go @@ -3,7 +3,6 @@ package config import ( "errors" "fmt" - "github.com/kaasops/vector-operator/internal/common" "github.com/kaasops/vector-operator/internal/pipeline" corev1 "k8s.io/api/core/v1" "net" @@ -28,26 +27,63 @@ func BuildAggregatorConfig(params VectorConfigParams, pipelines ...pipeline.Pipe for k, v := range p.Sources { settings := v - if val, ok := v.Options["address"]; ok { - address, _ := val.(string) - if _, port, err := net.SplitHostPort(address); err == nil { + switch v.Type { + case kubernetesEventsType: + { + address := v.Options["address"].(string) + if address == "" { + return nil, fmt.Errorf("address is empty from %s", pipeline.GetName()) + } + _, port, err := net.SplitHostPort(address) + if err != nil { + return nil, fmt.Errorf("failed to parse address %s: %w", address, err) + } + settings = &Source{ + Name: k, + Type: VectorType, + Options: map[string]any{ + "address": address, + }, + } portN, err := parsePort(port) if err != nil { return nil, fmt.Errorf("failed to parse port %s: %w", port, err) } - protocol := extractProtocol(v.Options) err = cfg.internal.addServicePort(&ServicePort{ - Port: portN, - Protocol: protocol, - Namespace: pipeline.GetNamespace(), - SourceName: k, - PipelineName: pipeline.GetName(), - ServiceName: pipeline.GetAnnotations()[common.AnnotationServiceName], + IsKubernetesEvents: true, + Port: portN, + Protocol: corev1.ProtocolTCP, + Namespace: pipeline.GetNamespace(), + SourceName: k, + PipelineName: pipeline.GetName(), }) if err != nil { return nil, err } } + default: + { + if val, ok := v.Options["address"]; ok { + address, _ := val.(string) + if _, port, err := net.SplitHostPort(address); err == nil { + portN, err := parsePort(port) + if err != nil { + return nil, fmt.Errorf("failed to parse port %s: %w", port, err) + } + protocol := extractProtocol(v.Options) + err = cfg.internal.addServicePort(&ServicePort{ + Port: portN, + Protocol: protocol, + Namespace: pipeline.GetNamespace(), + SourceName: k, + PipelineName: pipeline.GetName(), + }) + if err != nil { + return nil, err + } + } + } + } } v.Name = addPrefix(pipeline.GetNamespace(), pipeline.GetName(), k) diff --git a/internal/config/types.go b/internal/config/types.go index fa94d539..2e541d0e 100644 --- a/internal/config/types.go +++ b/internal/config/types.go @@ -72,12 +72,13 @@ type pipelineConfig_ struct { } type ServicePort struct { - PipelineName string - SourceName string - Namespace string - Port int32 - Protocol corev1.Protocol - ServiceName string + IsKubernetesEvents bool + PipelineName string + SourceName string + Namespace string + Port int32 + Protocol corev1.Protocol + ServiceName string } type internalConfig struct { diff --git a/internal/config/vector_source_types.go b/internal/config/vector_source_types.go index 281ef7be..1a88b9d9 100644 --- a/internal/config/vector_source_types.go +++ b/internal/config/vector_source_types.go @@ -39,6 +39,7 @@ const ( SplunkHECType = "splunk_hec" StatsDType = "statsd" VectorType = "vector" + kubernetesEventsType = "kubernetes_events" ) var aggregatorTypes = map[string]struct{}{ @@ -64,6 +65,7 @@ var aggregatorTypes = map[string]struct{}{ SplunkHECType: {}, StatsDType: {}, VectorType: {}, + kubernetesEventsType: {}, } var agentTypes = map[string]struct{}{ diff --git a/internal/controller/pipeline_controller.go b/internal/controller/pipeline_controller.go index c5b37ec6..df809ed8 100644 --- a/internal/controller/pipeline_controller.go +++ b/internal/controller/pipeline_controller.go @@ -21,6 +21,7 @@ import ( "errors" "fmt" "github.com/kaasops/vector-operator/internal/config/configcheck" + "github.com/kaasops/vector-operator/internal/k8sevents" "github.com/kaasops/vector-operator/internal/vector/aggregator" "github.com/kaasops/vector-operator/internal/vector/vectoragent" "golang.org/x/sync/errgroup" @@ -48,6 +49,7 @@ type PipelineReconciler struct { ConfigCheckTimeout time.Duration VectorAgentEventCh chan event.GenericEvent VectorAggregatorsEventCh chan event.GenericEvent + EventsCollector *k8sevents.EventsCollector } var ( @@ -159,7 +161,7 @@ func (r *PipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c for _, vector := range vectorAggregators { eg.Go(func() error { - vaCtrl := aggregator.NewController(vector, r.Client, r.Clientset) + vaCtrl := aggregator.NewController(vector, r.Client, r.Clientset, r.EventsCollector) cfg, err := config.BuildAggregatorConfig(config.VectorConfigParams{ ApiEnabled: vaCtrl.VectorAggregator.Spec.Api.Enabled, PlaygroundEnabled: vaCtrl.VectorAggregator.Spec.Api.Playground, diff --git a/internal/controller/vectoraggregator_controller.go b/internal/controller/vectoraggregator_controller.go index 3e3e4a3b..bfdbd918 100644 --- a/internal/controller/vectoraggregator_controller.go +++ b/internal/controller/vectoraggregator_controller.go @@ -21,6 +21,7 @@ import ( "errors" "github.com/kaasops/vector-operator/internal/config" "github.com/kaasops/vector-operator/internal/config/configcheck" + "github.com/kaasops/vector-operator/internal/k8sevents" "github.com/kaasops/vector-operator/internal/pipeline" "github.com/kaasops/vector-operator/internal/utils/hash" "github.com/kaasops/vector-operator/internal/utils/k8s" @@ -54,6 +55,7 @@ type VectorAggregatorReconciler struct { client.Client Scheme *runtime.Scheme + EventsCollector *k8sevents.EventsCollector Clientset *kubernetes.Clientset ConfigCheckTimeout time.Duration EventChan chan event.GenericEvent @@ -178,7 +180,7 @@ func listVectorAggregators(ctx context.Context, client client.Client) (vectors [ func (r *VectorAggregatorReconciler) createOrUpdateVectorAggregator(ctx context.Context, client client.Client, clientset *kubernetes.Clientset, v *v1alpha1.VectorAggregator) (ctrl.Result, error) { log := log.FromContext(ctx).WithValues("VectorAggregator", v.Name) - vaCtrl := aggregator.NewController(v, client, clientset) + vaCtrl := aggregator.NewController(v, client, clientset, r.EventsCollector) pipelines, err := pipeline.GetValidPipelines(ctx, vaCtrl.Client, pipeline.FilterPipelines{ Scope: pipeline.NamespacedPipeline, @@ -265,7 +267,7 @@ func (r *VectorAggregatorReconciler) reconcileVectorAggregators(ctx context.Cont } func (r *VectorAggregatorReconciler) deleteVectorAggregator(ctx context.Context, v *v1alpha1.VectorAggregator) error { - return aggregator.NewController(v, r.Client, r.Clientset).DeleteVectorAggregator(ctx) + return aggregator.NewController(v, r.Client, r.Clientset, r.EventsCollector).DeleteVectorAggregator(ctx) } func setAggregatorTypeMetaIfNeeded(cr *v1alpha1.VectorAggregator) { diff --git a/internal/k8sevents/event.go b/internal/k8sevents/event.go new file mode 100644 index 00000000..947f56ea --- /dev/null +++ b/internal/k8sevents/event.go @@ -0,0 +1,54 @@ +package k8sevents + +import ( + "github.com/kaasops/vector-operator/internal/vector/gen" + corev1 "k8s.io/api/core/v1" + "time" +) + +func k8sEventToVectorLog(ev *corev1.Event) *gen.Log { + return &gen.Log{ + Value: &gen.Value{ + Kind: &gen.Value_Map{ + Map: &gen.ValueMap{Fields: map[string]*gen.Value{ + "event": {Kind: &gen.Value_Map{ + Map: &gen.ValueMap{Fields: map[string]*gen.Value{ + "uid": valueFromString(string(ev.UID)), + "message": valueFromString(ev.Message), + "reason": valueFromString(ev.Reason), + "action": valueFromString(ev.Action), + "creationTimestamp": valueFromString(ev.CreationTimestamp.Format(time.RFC3339)), + "firstTimestamp": valueFromString(ev.FirstTimestamp.Format(time.RFC3339)), + "lastTimestamp": valueFromString(ev.LastTimestamp.Format(time.RFC3339)), + "name": valueFromString(ev.Name), + "namespace": valueFromString(ev.Namespace), + "source": {Kind: &gen.Value_Map{ + Map: &gen.ValueMap{Fields: map[string]*gen.Value{ + "host": valueFromString(ev.Source.Host), + "component": valueFromString(ev.Source.Component), + }}, + }}, + "involvedObject": {Kind: &gen.Value_Map{ + Map: &gen.ValueMap{Fields: map[string]*gen.Value{ + "uid": valueFromString(string(ev.InvolvedObject.UID)), + "kind": valueFromString(ev.InvolvedObject.Kind), + "name": valueFromString(ev.InvolvedObject.Name), + "namespace": valueFromString(ev.InvolvedObject.Namespace), + "apiVersion": valueFromString(ev.InvolvedObject.APIVersion), + "resourceVersion": valueFromString(ev.InvolvedObject.ResourceVersion), + "fieldPath": valueFromString(ev.InvolvedObject.FieldPath), + }}, + }}, + }}, + }}, + }}, + }, + }, + } +} + +func valueFromString(s string) *gen.Value { + return &gen.Value{ + Kind: &gen.Value_RawBytes{RawBytes: []byte(s)}, + } +} diff --git a/internal/k8sevents/manager.go b/internal/k8sevents/manager.go new file mode 100644 index 00000000..4229150e --- /dev/null +++ b/internal/k8sevents/manager.go @@ -0,0 +1,73 @@ +package k8sevents + +import ( + "fmt" + corev1 "k8s.io/api/core/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "net" + "sync" + "time" +) + +type Logger interface { + Info(msg string, keysAndValues ...any) + Error(err error, msg string, keysAndValues ...any) +} + +type EventsCollector struct { + client rest.Interface + logger Logger + mx sync.Mutex + mp map[string]*watcher +} + +func NewEventsCollector(clientset *kubernetes.Clientset, logger Logger) *EventsCollector { + return &EventsCollector{ + mp: make(map[string]*watcher), + client: clientset.CoreV1().RESTClient(), + logger: logger, + } +} + +func (m *EventsCollector) RegisterSubscriber(svcName, svcNamespace, port, namespace string) { + host := fmt.Sprintf("%s.%s", svcName, svcNamespace) + addr := net.JoinHostPort(host, port) + c := newWatcher(addr, namespace, m.logger) + + m.mx.Lock() + if oldC, ok := m.mp[host]; ok { + if oldC.addr == addr && oldC.namespace == namespace { + m.mx.Unlock() + return + } + oldC.close() + } + m.mp[host] = c + m.mx.Unlock() + + c.watchEvents(m.client) +} + +func (m *EventsCollector) UnregisterSubscriber(svcName, svcNamespace string) { + host := fmt.Sprintf("%s.%s", svcName, svcNamespace) + m.mx.Lock() + defer m.mx.Unlock() + if v, ok := m.mp[host]; ok { + v.close() + delete(m.mp, host) + } +} + +func eventTimestamp(ev *corev1.Event) time.Time { + var ts time.Time + switch { + case ev.EventTime.Time != time.Time{}: + ts = ev.EventTime.Time + case ev.LastTimestamp.Time != time.Time{}: + ts = ev.LastTimestamp.Time + case ev.FirstTimestamp.Time != time.Time{}: + ts = ev.FirstTimestamp.Time + } + return ts +} diff --git a/internal/k8sevents/watcher.go b/internal/k8sevents/watcher.go new file mode 100644 index 00000000..e7fee726 --- /dev/null +++ b/internal/k8sevents/watcher.go @@ -0,0 +1,128 @@ +package k8sevents + +import ( + "context" + "github.com/kaasops/vector-operator/internal/vector/gen" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/fields" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/cache" + "time" +) + +type watcher struct { + addr string + namespace string + createdAt time.Time + stopCh chan struct{} + logger Logger +} + +func newWatcher(addr, namespace string, logger Logger) *watcher { + r := watcher{ + addr: addr, + createdAt: time.Now(), + logger: logger, + namespace: namespace, + } + return &r +} + +func (w *watcher) watchEvents(client rest.Interface) { + if w.stopCh != nil { + return + } + + w.stopCh = make(chan struct{}) + eventsCh := make(chan *corev1.Event) + + watchList := cache.NewListWatchFromClient(client, "events", w.namespace, fields.Everything()) + _, ctrl := cache.NewInformerWithOptions(cache.InformerOptions{ + ListerWatcher: watchList, + ObjectType: &corev1.Event{}, + ResyncPeriod: 0, + Handler: cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj any) { + event := obj.(*corev1.Event) + eventsCh <- event + }, + UpdateFunc: func(_, obj interface{}) { + event := obj.(*corev1.Event) + eventsCh <- event + }, + }, + }) + go func() { + var conn *grpc.ClientConn + var vectorClient gen.VectorClient + var err error + var event *corev1.Event + var sending bool + + for { + select { + case <-w.stopCh: + if conn != nil { + _ = conn.Close() + } + return + + default: + if !sending { + select { + case event = <-eventsCh: + if eventTimestamp(event).Before(w.createdAt) || event == nil { + continue + } + sending = true + case <-w.stopCh: + if conn != nil { + _ = conn.Close() + } + return + } + } + + if conn == nil { + for { + conn, err = grpc.NewClient(w.addr, + grpc.WithTransportCredentials(insecure.NewCredentials()), + ) + if err != nil { + w.logger.Error(err, "connect to address", "address", w.addr) + time.Sleep(5 * time.Second) + continue + } + vectorClient = gen.NewVectorClient(conn) + break + } + } + + _, err = vectorClient.PushEvents(context.Background(), &gen.PushEventsRequest{ + Events: []*gen.EventWrapper{{ + Event: &gen.EventWrapper_Log{ + Log: k8sEventToVectorLog(event), + }, + }}, + }) + if err != nil { + w.logger.Error(err, "send event", "address", w.addr) + _ = conn.Close() + conn = nil + time.Sleep(5 * time.Second) + continue + } + + sending = false + } + } + + }() + go ctrl.Run(w.stopCh) +} + +func (w *watcher) close() { + close(w.stopCh) +} diff --git a/internal/vector/aggregator/controller.go b/internal/vector/aggregator/controller.go index ca0d7c26..a68da92e 100644 --- a/internal/vector/aggregator/controller.go +++ b/internal/vector/aggregator/controller.go @@ -4,6 +4,7 @@ import ( "context" vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" "github.com/kaasops/vector-operator/internal/config" + "github.com/kaasops/vector-operator/internal/k8sevents" "github.com/kaasops/vector-operator/internal/utils/k8s" monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" corev1 "k8s.io/api/core/v1" @@ -22,13 +23,20 @@ type Controller struct { ConfigBytes []byte Config *config.VectorConfig ClientSet *kubernetes.Clientset + EventsCollector *k8sevents.EventsCollector } -func NewController(v *vectorv1alpha1.VectorAggregator, c client.Client, cs *kubernetes.Clientset) *Controller { +func NewController( + v *vectorv1alpha1.VectorAggregator, + c client.Client, + cs *kubernetes.Clientset, + evCollector *k8sevents.EventsCollector, +) *Controller { ctrl := &Controller{ Client: c, VectorAggregator: v, ClientSet: cs, + EventsCollector: evCollector, } ctrl.setDefault() return ctrl diff --git a/internal/vector/aggregator/service.go b/internal/vector/aggregator/service.go index 7f0bc350..a119e951 100644 --- a/internal/vector/aggregator/service.go +++ b/internal/vector/aggregator/service.go @@ -3,6 +3,7 @@ package aggregator import ( "context" "fmt" + "github.com/kaasops/vector-operator/internal/common" "github.com/kaasops/vector-operator/internal/utils/k8s" "github.com/stoewer/go-strcase" corev1 "k8s.io/api/core/v1" @@ -11,6 +12,7 @@ import ( "maps" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" + "strconv" ) func (ctrl *Controller) ensureVectorAggregatorService(ctx context.Context) error { @@ -29,8 +31,19 @@ func (ctrl *Controller) ensureVectorAggregatorService(ctx context.Context) error if err := k8s.CreateOrUpdateResource(ctx, svc, ctrl.Client); err != nil { return err } + if svc.Annotations[common.AnnotationK8sEventsPort] != "" { + ctrl.EventsCollector.RegisterSubscriber( + svc.Name, + svc.Namespace, + svc.Annotations[common.AnnotationK8sEventsPort], + svc.Annotations[common.AnnotationK8sEventsNamespace], + ) + } } for _, svc := range existing { + if svc.Annotations[common.AnnotationK8sEventsPort] != "" { + ctrl.EventsCollector.UnregisterSubscriber(svc.Name, svc.Namespace) + } if err := ctrl.Client.Delete(ctx, svc); err != nil { return err } @@ -53,6 +66,11 @@ func (ctrl *Controller) createVectorAggregatorServices() ([]*corev1.Service, err ports := make([]corev1.ServicePort, 0, len(list)) for _, sp := range list { + if sp.IsKubernetesEvents { + ann[common.AnnotationK8sEventsNamespace] = sp.Namespace + ann[common.AnnotationK8sEventsPort] = strconv.Itoa(int(sp.Port)) + } + ports = append(ports, corev1.ServicePort{ Name: strcase.KebabCase(sp.SourceName), Protocol: sp.Protocol, diff --git a/internal/vector/gen/event.pb.go b/internal/vector/gen/event.pb.go new file mode 100644 index 00000000..9d55d371 --- /dev/null +++ b/internal/vector/gen/event.pb.go @@ -0,0 +1,3516 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.34.2 +// protoc (unknown) +// source: event.proto + +package gen + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type ValueNull int32 + +const ( + ValueNull_NULL_VALUE ValueNull = 0 +) + +// Enum value maps for ValueNull. +var ( + ValueNull_name = map[int32]string{ + 0: "NULL_VALUE", + } + ValueNull_value = map[string]int32{ + "NULL_VALUE": 0, + } +) + +func (x ValueNull) Enum() *ValueNull { + p := new(ValueNull) + *p = x + return p +} + +func (x ValueNull) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ValueNull) Descriptor() protoreflect.EnumDescriptor { + return file_event_proto_enumTypes[0].Descriptor() +} + +func (ValueNull) Type() protoreflect.EnumType { + return &file_event_proto_enumTypes[0] +} + +func (x ValueNull) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use ValueNull.Descriptor instead. +func (ValueNull) EnumDescriptor() ([]byte, []int) { + return file_event_proto_rawDescGZIP(), []int{0} +} + +type StatisticKind int32 + +const ( + StatisticKind_Histogram StatisticKind = 0 + StatisticKind_Summary StatisticKind = 1 +) + +// Enum value maps for StatisticKind. +var ( + StatisticKind_name = map[int32]string{ + 0: "Histogram", + 1: "Summary", + } + StatisticKind_value = map[string]int32{ + "Histogram": 0, + "Summary": 1, + } +) + +func (x StatisticKind) Enum() *StatisticKind { + p := new(StatisticKind) + *p = x + return p +} + +func (x StatisticKind) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (StatisticKind) Descriptor() protoreflect.EnumDescriptor { + return file_event_proto_enumTypes[1].Descriptor() +} + +func (StatisticKind) Type() protoreflect.EnumType { + return &file_event_proto_enumTypes[1] +} + +func (x StatisticKind) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use StatisticKind.Descriptor instead. +func (StatisticKind) EnumDescriptor() ([]byte, []int) { + return file_event_proto_rawDescGZIP(), []int{1} +} + +type Metric_Kind int32 + +const ( + Metric_Incremental Metric_Kind = 0 + Metric_Absolute Metric_Kind = 1 +) + +// Enum value maps for Metric_Kind. +var ( + Metric_Kind_name = map[int32]string{ + 0: "Incremental", + 1: "Absolute", + } + Metric_Kind_value = map[string]int32{ + "Incremental": 0, + "Absolute": 1, + } +) + +func (x Metric_Kind) Enum() *Metric_Kind { + p := new(Metric_Kind) + *p = x + return p +} + +func (x Metric_Kind) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Metric_Kind) Descriptor() protoreflect.EnumDescriptor { + return file_event_proto_enumTypes[2].Descriptor() +} + +func (Metric_Kind) Type() protoreflect.EnumType { + return &file_event_proto_enumTypes[2] +} + +func (x Metric_Kind) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Metric_Kind.Descriptor instead. +func (Metric_Kind) EnumDescriptor() ([]byte, []int) { + return file_event_proto_rawDescGZIP(), []int{14, 0} +} + +type EventArray struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Events: + // + // *EventArray_Logs + // *EventArray_Metrics + // *EventArray_Traces + Events isEventArray_Events `protobuf_oneof:"events"` +} + +func (x *EventArray) Reset() { + *x = EventArray{} + if protoimpl.UnsafeEnabled { + mi := &file_event_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EventArray) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EventArray) ProtoMessage() {} + +func (x *EventArray) ProtoReflect() protoreflect.Message { + mi := &file_event_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EventArray.ProtoReflect.Descriptor instead. +func (*EventArray) Descriptor() ([]byte, []int) { + return file_event_proto_rawDescGZIP(), []int{0} +} + +func (m *EventArray) GetEvents() isEventArray_Events { + if m != nil { + return m.Events + } + return nil +} + +func (x *EventArray) GetLogs() *LogArray { + if x, ok := x.GetEvents().(*EventArray_Logs); ok { + return x.Logs + } + return nil +} + +func (x *EventArray) GetMetrics() *MetricArray { + if x, ok := x.GetEvents().(*EventArray_Metrics); ok { + return x.Metrics + } + return nil +} + +func (x *EventArray) GetTraces() *TraceArray { + if x, ok := x.GetEvents().(*EventArray_Traces); ok { + return x.Traces + } + return nil +} + +type isEventArray_Events interface { + isEventArray_Events() +} + +type EventArray_Logs struct { + Logs *LogArray `protobuf:"bytes,1,opt,name=logs,proto3,oneof"` +} + +type EventArray_Metrics struct { + Metrics *MetricArray `protobuf:"bytes,2,opt,name=metrics,proto3,oneof"` +} + +type EventArray_Traces struct { + Traces *TraceArray `protobuf:"bytes,3,opt,name=traces,proto3,oneof"` +} + +func (*EventArray_Logs) isEventArray_Events() {} + +func (*EventArray_Metrics) isEventArray_Events() {} + +func (*EventArray_Traces) isEventArray_Events() {} + +type LogArray struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Logs []*Log `protobuf:"bytes,1,rep,name=logs,proto3" json:"logs,omitempty"` +} + +func (x *LogArray) Reset() { + *x = LogArray{} + if protoimpl.UnsafeEnabled { + mi := &file_event_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LogArray) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LogArray) ProtoMessage() {} + +func (x *LogArray) ProtoReflect() protoreflect.Message { + mi := &file_event_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LogArray.ProtoReflect.Descriptor instead. +func (*LogArray) Descriptor() ([]byte, []int) { + return file_event_proto_rawDescGZIP(), []int{1} +} + +func (x *LogArray) GetLogs() []*Log { + if x != nil { + return x.Logs + } + return nil +} + +type MetricArray struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Metrics []*Metric `protobuf:"bytes,1,rep,name=metrics,proto3" json:"metrics,omitempty"` +} + +func (x *MetricArray) Reset() { + *x = MetricArray{} + if protoimpl.UnsafeEnabled { + mi := &file_event_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MetricArray) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MetricArray) ProtoMessage() {} + +func (x *MetricArray) ProtoReflect() protoreflect.Message { + mi := &file_event_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MetricArray.ProtoReflect.Descriptor instead. +func (*MetricArray) Descriptor() ([]byte, []int) { + return file_event_proto_rawDescGZIP(), []int{2} +} + +func (x *MetricArray) GetMetrics() []*Metric { + if x != nil { + return x.Metrics + } + return nil +} + +type TraceArray struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Traces []*Trace `protobuf:"bytes,1,rep,name=traces,proto3" json:"traces,omitempty"` +} + +func (x *TraceArray) Reset() { + *x = TraceArray{} + if protoimpl.UnsafeEnabled { + mi := &file_event_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TraceArray) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TraceArray) ProtoMessage() {} + +func (x *TraceArray) ProtoReflect() protoreflect.Message { + mi := &file_event_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TraceArray.ProtoReflect.Descriptor instead. +func (*TraceArray) Descriptor() ([]byte, []int) { + return file_event_proto_rawDescGZIP(), []int{3} +} + +func (x *TraceArray) GetTraces() []*Trace { + if x != nil { + return x.Traces + } + return nil +} + +type EventWrapper struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Event: + // + // *EventWrapper_Log + // *EventWrapper_Metric + // *EventWrapper_Trace + Event isEventWrapper_Event `protobuf_oneof:"event"` +} + +func (x *EventWrapper) Reset() { + *x = EventWrapper{} + if protoimpl.UnsafeEnabled { + mi := &file_event_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EventWrapper) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EventWrapper) ProtoMessage() {} + +func (x *EventWrapper) ProtoReflect() protoreflect.Message { + mi := &file_event_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EventWrapper.ProtoReflect.Descriptor instead. +func (*EventWrapper) Descriptor() ([]byte, []int) { + return file_event_proto_rawDescGZIP(), []int{4} +} + +func (m *EventWrapper) GetEvent() isEventWrapper_Event { + if m != nil { + return m.Event + } + return nil +} + +func (x *EventWrapper) GetLog() *Log { + if x, ok := x.GetEvent().(*EventWrapper_Log); ok { + return x.Log + } + return nil +} + +func (x *EventWrapper) GetMetric() *Metric { + if x, ok := x.GetEvent().(*EventWrapper_Metric); ok { + return x.Metric + } + return nil +} + +func (x *EventWrapper) GetTrace() *Trace { + if x, ok := x.GetEvent().(*EventWrapper_Trace); ok { + return x.Trace + } + return nil +} + +type isEventWrapper_Event interface { + isEventWrapper_Event() +} + +type EventWrapper_Log struct { + Log *Log `protobuf:"bytes,1,opt,name=log,proto3,oneof"` +} + +type EventWrapper_Metric struct { + Metric *Metric `protobuf:"bytes,2,opt,name=metric,proto3,oneof"` +} + +type EventWrapper_Trace struct { + Trace *Trace `protobuf:"bytes,3,opt,name=trace,proto3,oneof"` +} + +func (*EventWrapper_Log) isEventWrapper_Event() {} + +func (*EventWrapper_Metric) isEventWrapper_Event() {} + +func (*EventWrapper_Trace) isEventWrapper_Event() {} + +type Log struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Deprecated, use value instead + Fields map[string]*Value `protobuf:"bytes,1,rep,name=fields,proto3" json:"fields,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Value *Value `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` + // Deprecated: Marked as deprecated in event.proto. + Metadata *Value `protobuf:"bytes,3,opt,name=metadata,proto3" json:"metadata,omitempty"` + MetadataFull *Metadata `protobuf:"bytes,4,opt,name=metadata_full,json=metadataFull,proto3" json:"metadata_full,omitempty"` +} + +func (x *Log) Reset() { + *x = Log{} + if protoimpl.UnsafeEnabled { + mi := &file_event_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Log) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Log) ProtoMessage() {} + +func (x *Log) ProtoReflect() protoreflect.Message { + mi := &file_event_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Log.ProtoReflect.Descriptor instead. +func (*Log) Descriptor() ([]byte, []int) { + return file_event_proto_rawDescGZIP(), []int{5} +} + +func (x *Log) GetFields() map[string]*Value { + if x != nil { + return x.Fields + } + return nil +} + +func (x *Log) GetValue() *Value { + if x != nil { + return x.Value + } + return nil +} + +// Deprecated: Marked as deprecated in event.proto. +func (x *Log) GetMetadata() *Value { + if x != nil { + return x.Metadata + } + return nil +} + +func (x *Log) GetMetadataFull() *Metadata { + if x != nil { + return x.MetadataFull + } + return nil +} + +type Trace struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Fields map[string]*Value `protobuf:"bytes,1,rep,name=fields,proto3" json:"fields,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // Deprecated: Marked as deprecated in event.proto. + Metadata *Value `protobuf:"bytes,2,opt,name=metadata,proto3" json:"metadata,omitempty"` + MetadataFull *Metadata `protobuf:"bytes,3,opt,name=metadata_full,json=metadataFull,proto3" json:"metadata_full,omitempty"` +} + +func (x *Trace) Reset() { + *x = Trace{} + if protoimpl.UnsafeEnabled { + mi := &file_event_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Trace) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Trace) ProtoMessage() {} + +func (x *Trace) ProtoReflect() protoreflect.Message { + mi := &file_event_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Trace.ProtoReflect.Descriptor instead. +func (*Trace) Descriptor() ([]byte, []int) { + return file_event_proto_rawDescGZIP(), []int{6} +} + +func (x *Trace) GetFields() map[string]*Value { + if x != nil { + return x.Fields + } + return nil +} + +// Deprecated: Marked as deprecated in event.proto. +func (x *Trace) GetMetadata() *Value { + if x != nil { + return x.Metadata + } + return nil +} + +func (x *Trace) GetMetadataFull() *Metadata { + if x != nil { + return x.MetadataFull + } + return nil +} + +type ValueMap struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Fields map[string]*Value `protobuf:"bytes,1,rep,name=fields,proto3" json:"fields,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *ValueMap) Reset() { + *x = ValueMap{} + if protoimpl.UnsafeEnabled { + mi := &file_event_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ValueMap) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ValueMap) ProtoMessage() {} + +func (x *ValueMap) ProtoReflect() protoreflect.Message { + mi := &file_event_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ValueMap.ProtoReflect.Descriptor instead. +func (*ValueMap) Descriptor() ([]byte, []int) { + return file_event_proto_rawDescGZIP(), []int{7} +} + +func (x *ValueMap) GetFields() map[string]*Value { + if x != nil { + return x.Fields + } + return nil +} + +type ValueArray struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Items []*Value `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"` +} + +func (x *ValueArray) Reset() { + *x = ValueArray{} + if protoimpl.UnsafeEnabled { + mi := &file_event_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ValueArray) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ValueArray) ProtoMessage() {} + +func (x *ValueArray) ProtoReflect() protoreflect.Message { + mi := &file_event_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ValueArray.ProtoReflect.Descriptor instead. +func (*ValueArray) Descriptor() ([]byte, []int) { + return file_event_proto_rawDescGZIP(), []int{8} +} + +func (x *ValueArray) GetItems() []*Value { + if x != nil { + return x.Items + } + return nil +} + +type Value struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Kind: + // + // *Value_RawBytes + // *Value_Timestamp + // *Value_Integer + // *Value_Float + // *Value_Boolean + // *Value_Map + // *Value_Array + // *Value_Null + Kind isValue_Kind `protobuf_oneof:"kind"` +} + +func (x *Value) Reset() { + *x = Value{} + if protoimpl.UnsafeEnabled { + mi := &file_event_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Value) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Value) ProtoMessage() {} + +func (x *Value) ProtoReflect() protoreflect.Message { + mi := &file_event_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Value.ProtoReflect.Descriptor instead. +func (*Value) Descriptor() ([]byte, []int) { + return file_event_proto_rawDescGZIP(), []int{9} +} + +func (m *Value) GetKind() isValue_Kind { + if m != nil { + return m.Kind + } + return nil +} + +func (x *Value) GetRawBytes() []byte { + if x, ok := x.GetKind().(*Value_RawBytes); ok { + return x.RawBytes + } + return nil +} + +func (x *Value) GetTimestamp() *timestamppb.Timestamp { + if x, ok := x.GetKind().(*Value_Timestamp); ok { + return x.Timestamp + } + return nil +} + +func (x *Value) GetInteger() int64 { + if x, ok := x.GetKind().(*Value_Integer); ok { + return x.Integer + } + return 0 +} + +func (x *Value) GetFloat() float64 { + if x, ok := x.GetKind().(*Value_Float); ok { + return x.Float + } + return 0 +} + +func (x *Value) GetBoolean() bool { + if x, ok := x.GetKind().(*Value_Boolean); ok { + return x.Boolean + } + return false +} + +func (x *Value) GetMap() *ValueMap { + if x, ok := x.GetKind().(*Value_Map); ok { + return x.Map + } + return nil +} + +func (x *Value) GetArray() *ValueArray { + if x, ok := x.GetKind().(*Value_Array); ok { + return x.Array + } + return nil +} + +func (x *Value) GetNull() ValueNull { + if x, ok := x.GetKind().(*Value_Null); ok { + return x.Null + } + return ValueNull_NULL_VALUE +} + +type isValue_Kind interface { + isValue_Kind() +} + +type Value_RawBytes struct { + RawBytes []byte `protobuf:"bytes,1,opt,name=raw_bytes,json=rawBytes,proto3,oneof"` +} + +type Value_Timestamp struct { + Timestamp *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=timestamp,proto3,oneof"` +} + +type Value_Integer struct { + Integer int64 `protobuf:"varint,4,opt,name=integer,proto3,oneof"` +} + +type Value_Float struct { + Float float64 `protobuf:"fixed64,5,opt,name=float,proto3,oneof"` +} + +type Value_Boolean struct { + Boolean bool `protobuf:"varint,6,opt,name=boolean,proto3,oneof"` +} + +type Value_Map struct { + Map *ValueMap `protobuf:"bytes,7,opt,name=map,proto3,oneof"` +} + +type Value_Array struct { + Array *ValueArray `protobuf:"bytes,8,opt,name=array,proto3,oneof"` +} + +type Value_Null struct { + Null ValueNull `protobuf:"varint,9,opt,name=null,proto3,enum=event.ValueNull,oneof"` +} + +func (*Value_RawBytes) isValue_Kind() {} + +func (*Value_Timestamp) isValue_Kind() {} + +func (*Value_Integer) isValue_Kind() {} + +func (*Value_Float) isValue_Kind() {} + +func (*Value_Boolean) isValue_Kind() {} + +func (*Value_Map) isValue_Kind() {} + +func (*Value_Array) isValue_Kind() {} + +func (*Value_Null) isValue_Kind() {} + +type DatadogOriginMetadata struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + OriginProduct *uint32 `protobuf:"varint,1,opt,name=origin_product,json=originProduct,proto3,oneof" json:"origin_product,omitempty"` + OriginCategory *uint32 `protobuf:"varint,2,opt,name=origin_category,json=originCategory,proto3,oneof" json:"origin_category,omitempty"` + OriginService *uint32 `protobuf:"varint,3,opt,name=origin_service,json=originService,proto3,oneof" json:"origin_service,omitempty"` +} + +func (x *DatadogOriginMetadata) Reset() { + *x = DatadogOriginMetadata{} + if protoimpl.UnsafeEnabled { + mi := &file_event_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DatadogOriginMetadata) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DatadogOriginMetadata) ProtoMessage() {} + +func (x *DatadogOriginMetadata) ProtoReflect() protoreflect.Message { + mi := &file_event_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DatadogOriginMetadata.ProtoReflect.Descriptor instead. +func (*DatadogOriginMetadata) Descriptor() ([]byte, []int) { + return file_event_proto_rawDescGZIP(), []int{10} +} + +func (x *DatadogOriginMetadata) GetOriginProduct() uint32 { + if x != nil && x.OriginProduct != nil { + return *x.OriginProduct + } + return 0 +} + +func (x *DatadogOriginMetadata) GetOriginCategory() uint32 { + if x != nil && x.OriginCategory != nil { + return *x.OriginCategory + } + return 0 +} + +func (x *DatadogOriginMetadata) GetOriginService() uint32 { + if x != nil && x.OriginService != nil { + return *x.OriginService + } + return 0 +} + +type Secrets struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Entries map[string]string `protobuf:"bytes,1,rep,name=entries,proto3" json:"entries,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *Secrets) Reset() { + *x = Secrets{} + if protoimpl.UnsafeEnabled { + mi := &file_event_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Secrets) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Secrets) ProtoMessage() {} + +func (x *Secrets) ProtoReflect() protoreflect.Message { + mi := &file_event_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Secrets.ProtoReflect.Descriptor instead. +func (*Secrets) Descriptor() ([]byte, []int) { + return file_event_proto_rawDescGZIP(), []int{11} +} + +func (x *Secrets) GetEntries() map[string]string { + if x != nil { + return x.Entries + } + return nil +} + +type OutputId struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Component string `protobuf:"bytes,1,opt,name=component,proto3" json:"component,omitempty"` + Port *string `protobuf:"bytes,2,opt,name=port,proto3,oneof" json:"port,omitempty"` +} + +func (x *OutputId) Reset() { + *x = OutputId{} + if protoimpl.UnsafeEnabled { + mi := &file_event_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *OutputId) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*OutputId) ProtoMessage() {} + +func (x *OutputId) ProtoReflect() protoreflect.Message { + mi := &file_event_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use OutputId.ProtoReflect.Descriptor instead. +func (*OutputId) Descriptor() ([]byte, []int) { + return file_event_proto_rawDescGZIP(), []int{12} +} + +func (x *OutputId) GetComponent() string { + if x != nil { + return x.Component + } + return "" +} + +func (x *OutputId) GetPort() string { + if x != nil && x.Port != nil { + return *x.Port + } + return "" +} + +type Metadata struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Value *Value `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` + DatadogOriginMetadata *DatadogOriginMetadata `protobuf:"bytes,2,opt,name=datadog_origin_metadata,json=datadogOriginMetadata,proto3" json:"datadog_origin_metadata,omitempty"` + SourceId *string `protobuf:"bytes,3,opt,name=source_id,json=sourceId,proto3,oneof" json:"source_id,omitempty"` + SourceType *string `protobuf:"bytes,4,opt,name=source_type,json=sourceType,proto3,oneof" json:"source_type,omitempty"` + UpstreamId *OutputId `protobuf:"bytes,5,opt,name=upstream_id,json=upstreamId,proto3" json:"upstream_id,omitempty"` + Secrets *Secrets `protobuf:"bytes,6,opt,name=secrets,proto3" json:"secrets,omitempty"` +} + +func (x *Metadata) Reset() { + *x = Metadata{} + if protoimpl.UnsafeEnabled { + mi := &file_event_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Metadata) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Metadata) ProtoMessage() {} + +func (x *Metadata) ProtoReflect() protoreflect.Message { + mi := &file_event_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Metadata.ProtoReflect.Descriptor instead. +func (*Metadata) Descriptor() ([]byte, []int) { + return file_event_proto_rawDescGZIP(), []int{13} +} + +func (x *Metadata) GetValue() *Value { + if x != nil { + return x.Value + } + return nil +} + +func (x *Metadata) GetDatadogOriginMetadata() *DatadogOriginMetadata { + if x != nil { + return x.DatadogOriginMetadata + } + return nil +} + +func (x *Metadata) GetSourceId() string { + if x != nil && x.SourceId != nil { + return *x.SourceId + } + return "" +} + +func (x *Metadata) GetSourceType() string { + if x != nil && x.SourceType != nil { + return *x.SourceType + } + return "" +} + +func (x *Metadata) GetUpstreamId() *OutputId { + if x != nil { + return x.UpstreamId + } + return nil +} + +func (x *Metadata) GetSecrets() *Secrets { + if x != nil { + return x.Secrets + } + return nil +} + +type Metric struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Timestamp *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + TagsV1 map[string]string `protobuf:"bytes,3,rep,name=tags_v1,json=tagsV1,proto3" json:"tags_v1,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + TagsV2 map[string]*TagValues `protobuf:"bytes,20,rep,name=tags_v2,json=tagsV2,proto3" json:"tags_v2,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Kind Metric_Kind `protobuf:"varint,4,opt,name=kind,proto3,enum=event.Metric_Kind" json:"kind,omitempty"` + // Types that are assignable to Value: + // + // *Metric_Counter + // *Metric_Gauge + // *Metric_Set + // *Metric_Distribution1 + // *Metric_AggregatedHistogram1 + // *Metric_AggregatedSummary1 + // *Metric_Distribution2 + // *Metric_AggregatedHistogram2 + // *Metric_AggregatedSummary2 + // *Metric_Sketch + // *Metric_AggregatedHistogram3 + // *Metric_AggregatedSummary3 + Value isMetric_Value `protobuf_oneof:"value"` + Namespace string `protobuf:"bytes,11,opt,name=namespace,proto3" json:"namespace,omitempty"` + IntervalMs uint32 `protobuf:"varint,18,opt,name=interval_ms,json=intervalMs,proto3" json:"interval_ms,omitempty"` + // Deprecated: Marked as deprecated in event.proto. + Metadata *Value `protobuf:"bytes,19,opt,name=metadata,proto3" json:"metadata,omitempty"` + MetadataFull *Metadata `protobuf:"bytes,21,opt,name=metadata_full,json=metadataFull,proto3" json:"metadata_full,omitempty"` +} + +func (x *Metric) Reset() { + *x = Metric{} + if protoimpl.UnsafeEnabled { + mi := &file_event_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Metric) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Metric) ProtoMessage() {} + +func (x *Metric) ProtoReflect() protoreflect.Message { + mi := &file_event_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Metric.ProtoReflect.Descriptor instead. +func (*Metric) Descriptor() ([]byte, []int) { + return file_event_proto_rawDescGZIP(), []int{14} +} + +func (x *Metric) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Metric) GetTimestamp() *timestamppb.Timestamp { + if x != nil { + return x.Timestamp + } + return nil +} + +func (x *Metric) GetTagsV1() map[string]string { + if x != nil { + return x.TagsV1 + } + return nil +} + +func (x *Metric) GetTagsV2() map[string]*TagValues { + if x != nil { + return x.TagsV2 + } + return nil +} + +func (x *Metric) GetKind() Metric_Kind { + if x != nil { + return x.Kind + } + return Metric_Incremental +} + +func (m *Metric) GetValue() isMetric_Value { + if m != nil { + return m.Value + } + return nil +} + +func (x *Metric) GetCounter() *Counter { + if x, ok := x.GetValue().(*Metric_Counter); ok { + return x.Counter + } + return nil +} + +func (x *Metric) GetGauge() *Gauge { + if x, ok := x.GetValue().(*Metric_Gauge); ok { + return x.Gauge + } + return nil +} + +func (x *Metric) GetSet() *Set { + if x, ok := x.GetValue().(*Metric_Set); ok { + return x.Set + } + return nil +} + +func (x *Metric) GetDistribution1() *Distribution1 { + if x, ok := x.GetValue().(*Metric_Distribution1); ok { + return x.Distribution1 + } + return nil +} + +func (x *Metric) GetAggregatedHistogram1() *AggregatedHistogram1 { + if x, ok := x.GetValue().(*Metric_AggregatedHistogram1); ok { + return x.AggregatedHistogram1 + } + return nil +} + +func (x *Metric) GetAggregatedSummary1() *AggregatedSummary1 { + if x, ok := x.GetValue().(*Metric_AggregatedSummary1); ok { + return x.AggregatedSummary1 + } + return nil +} + +func (x *Metric) GetDistribution2() *Distribution2 { + if x, ok := x.GetValue().(*Metric_Distribution2); ok { + return x.Distribution2 + } + return nil +} + +func (x *Metric) GetAggregatedHistogram2() *AggregatedHistogram2 { + if x, ok := x.GetValue().(*Metric_AggregatedHistogram2); ok { + return x.AggregatedHistogram2 + } + return nil +} + +func (x *Metric) GetAggregatedSummary2() *AggregatedSummary2 { + if x, ok := x.GetValue().(*Metric_AggregatedSummary2); ok { + return x.AggregatedSummary2 + } + return nil +} + +func (x *Metric) GetSketch() *Sketch { + if x, ok := x.GetValue().(*Metric_Sketch); ok { + return x.Sketch + } + return nil +} + +func (x *Metric) GetAggregatedHistogram3() *AggregatedHistogram3 { + if x, ok := x.GetValue().(*Metric_AggregatedHistogram3); ok { + return x.AggregatedHistogram3 + } + return nil +} + +func (x *Metric) GetAggregatedSummary3() *AggregatedSummary3 { + if x, ok := x.GetValue().(*Metric_AggregatedSummary3); ok { + return x.AggregatedSummary3 + } + return nil +} + +func (x *Metric) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *Metric) GetIntervalMs() uint32 { + if x != nil { + return x.IntervalMs + } + return 0 +} + +// Deprecated: Marked as deprecated in event.proto. +func (x *Metric) GetMetadata() *Value { + if x != nil { + return x.Metadata + } + return nil +} + +func (x *Metric) GetMetadataFull() *Metadata { + if x != nil { + return x.MetadataFull + } + return nil +} + +type isMetric_Value interface { + isMetric_Value() +} + +type Metric_Counter struct { + Counter *Counter `protobuf:"bytes,5,opt,name=counter,proto3,oneof"` +} + +type Metric_Gauge struct { + Gauge *Gauge `protobuf:"bytes,6,opt,name=gauge,proto3,oneof"` +} + +type Metric_Set struct { + Set *Set `protobuf:"bytes,7,opt,name=set,proto3,oneof"` +} + +type Metric_Distribution1 struct { + Distribution1 *Distribution1 `protobuf:"bytes,8,opt,name=distribution1,proto3,oneof"` +} + +type Metric_AggregatedHistogram1 struct { + AggregatedHistogram1 *AggregatedHistogram1 `protobuf:"bytes,9,opt,name=aggregated_histogram1,json=aggregatedHistogram1,proto3,oneof"` +} + +type Metric_AggregatedSummary1 struct { + AggregatedSummary1 *AggregatedSummary1 `protobuf:"bytes,10,opt,name=aggregated_summary1,json=aggregatedSummary1,proto3,oneof"` +} + +type Metric_Distribution2 struct { + Distribution2 *Distribution2 `protobuf:"bytes,12,opt,name=distribution2,proto3,oneof"` +} + +type Metric_AggregatedHistogram2 struct { + AggregatedHistogram2 *AggregatedHistogram2 `protobuf:"bytes,13,opt,name=aggregated_histogram2,json=aggregatedHistogram2,proto3,oneof"` +} + +type Metric_AggregatedSummary2 struct { + AggregatedSummary2 *AggregatedSummary2 `protobuf:"bytes,14,opt,name=aggregated_summary2,json=aggregatedSummary2,proto3,oneof"` +} + +type Metric_Sketch struct { + Sketch *Sketch `protobuf:"bytes,15,opt,name=sketch,proto3,oneof"` +} + +type Metric_AggregatedHistogram3 struct { + AggregatedHistogram3 *AggregatedHistogram3 `protobuf:"bytes,16,opt,name=aggregated_histogram3,json=aggregatedHistogram3,proto3,oneof"` +} + +type Metric_AggregatedSummary3 struct { + AggregatedSummary3 *AggregatedSummary3 `protobuf:"bytes,17,opt,name=aggregated_summary3,json=aggregatedSummary3,proto3,oneof"` +} + +func (*Metric_Counter) isMetric_Value() {} + +func (*Metric_Gauge) isMetric_Value() {} + +func (*Metric_Set) isMetric_Value() {} + +func (*Metric_Distribution1) isMetric_Value() {} + +func (*Metric_AggregatedHistogram1) isMetric_Value() {} + +func (*Metric_AggregatedSummary1) isMetric_Value() {} + +func (*Metric_Distribution2) isMetric_Value() {} + +func (*Metric_AggregatedHistogram2) isMetric_Value() {} + +func (*Metric_AggregatedSummary2) isMetric_Value() {} + +func (*Metric_Sketch) isMetric_Value() {} + +func (*Metric_AggregatedHistogram3) isMetric_Value() {} + +func (*Metric_AggregatedSummary3) isMetric_Value() {} + +type TagValues struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Values []*TagValue `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty"` +} + +func (x *TagValues) Reset() { + *x = TagValues{} + if protoimpl.UnsafeEnabled { + mi := &file_event_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TagValues) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TagValues) ProtoMessage() {} + +func (x *TagValues) ProtoReflect() protoreflect.Message { + mi := &file_event_proto_msgTypes[15] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TagValues.ProtoReflect.Descriptor instead. +func (*TagValues) Descriptor() ([]byte, []int) { + return file_event_proto_rawDescGZIP(), []int{15} +} + +func (x *TagValues) GetValues() []*TagValue { + if x != nil { + return x.Values + } + return nil +} + +type TagValue struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Value *string `protobuf:"bytes,1,opt,name=value,proto3,oneof" json:"value,omitempty"` +} + +func (x *TagValue) Reset() { + *x = TagValue{} + if protoimpl.UnsafeEnabled { + mi := &file_event_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TagValue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TagValue) ProtoMessage() {} + +func (x *TagValue) ProtoReflect() protoreflect.Message { + mi := &file_event_proto_msgTypes[16] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TagValue.ProtoReflect.Descriptor instead. +func (*TagValue) Descriptor() ([]byte, []int) { + return file_event_proto_rawDescGZIP(), []int{16} +} + +func (x *TagValue) GetValue() string { + if x != nil && x.Value != nil { + return *x.Value + } + return "" +} + +type Counter struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Value float64 `protobuf:"fixed64,1,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *Counter) Reset() { + *x = Counter{} + if protoimpl.UnsafeEnabled { + mi := &file_event_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Counter) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Counter) ProtoMessage() {} + +func (x *Counter) ProtoReflect() protoreflect.Message { + mi := &file_event_proto_msgTypes[17] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Counter.ProtoReflect.Descriptor instead. +func (*Counter) Descriptor() ([]byte, []int) { + return file_event_proto_rawDescGZIP(), []int{17} +} + +func (x *Counter) GetValue() float64 { + if x != nil { + return x.Value + } + return 0 +} + +type Gauge struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Value float64 `protobuf:"fixed64,1,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *Gauge) Reset() { + *x = Gauge{} + if protoimpl.UnsafeEnabled { + mi := &file_event_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Gauge) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Gauge) ProtoMessage() {} + +func (x *Gauge) ProtoReflect() protoreflect.Message { + mi := &file_event_proto_msgTypes[18] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Gauge.ProtoReflect.Descriptor instead. +func (*Gauge) Descriptor() ([]byte, []int) { + return file_event_proto_rawDescGZIP(), []int{18} +} + +func (x *Gauge) GetValue() float64 { + if x != nil { + return x.Value + } + return 0 +} + +type Set struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Values []string `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty"` +} + +func (x *Set) Reset() { + *x = Set{} + if protoimpl.UnsafeEnabled { + mi := &file_event_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Set) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Set) ProtoMessage() {} + +func (x *Set) ProtoReflect() protoreflect.Message { + mi := &file_event_proto_msgTypes[19] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Set.ProtoReflect.Descriptor instead. +func (*Set) Descriptor() ([]byte, []int) { + return file_event_proto_rawDescGZIP(), []int{19} +} + +func (x *Set) GetValues() []string { + if x != nil { + return x.Values + } + return nil +} + +type Distribution1 struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Values []float64 `protobuf:"fixed64,1,rep,packed,name=values,proto3" json:"values,omitempty"` + SampleRates []uint32 `protobuf:"varint,2,rep,packed,name=sample_rates,json=sampleRates,proto3" json:"sample_rates,omitempty"` + Statistic StatisticKind `protobuf:"varint,3,opt,name=statistic,proto3,enum=event.StatisticKind" json:"statistic,omitempty"` +} + +func (x *Distribution1) Reset() { + *x = Distribution1{} + if protoimpl.UnsafeEnabled { + mi := &file_event_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Distribution1) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Distribution1) ProtoMessage() {} + +func (x *Distribution1) ProtoReflect() protoreflect.Message { + mi := &file_event_proto_msgTypes[20] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Distribution1.ProtoReflect.Descriptor instead. +func (*Distribution1) Descriptor() ([]byte, []int) { + return file_event_proto_rawDescGZIP(), []int{20} +} + +func (x *Distribution1) GetValues() []float64 { + if x != nil { + return x.Values + } + return nil +} + +func (x *Distribution1) GetSampleRates() []uint32 { + if x != nil { + return x.SampleRates + } + return nil +} + +func (x *Distribution1) GetStatistic() StatisticKind { + if x != nil { + return x.Statistic + } + return StatisticKind_Histogram +} + +type Distribution2 struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Samples []*DistributionSample `protobuf:"bytes,1,rep,name=samples,proto3" json:"samples,omitempty"` + Statistic StatisticKind `protobuf:"varint,2,opt,name=statistic,proto3,enum=event.StatisticKind" json:"statistic,omitempty"` +} + +func (x *Distribution2) Reset() { + *x = Distribution2{} + if protoimpl.UnsafeEnabled { + mi := &file_event_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Distribution2) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Distribution2) ProtoMessage() {} + +func (x *Distribution2) ProtoReflect() protoreflect.Message { + mi := &file_event_proto_msgTypes[21] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Distribution2.ProtoReflect.Descriptor instead. +func (*Distribution2) Descriptor() ([]byte, []int) { + return file_event_proto_rawDescGZIP(), []int{21} +} + +func (x *Distribution2) GetSamples() []*DistributionSample { + if x != nil { + return x.Samples + } + return nil +} + +func (x *Distribution2) GetStatistic() StatisticKind { + if x != nil { + return x.Statistic + } + return StatisticKind_Histogram +} + +type DistributionSample struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Value float64 `protobuf:"fixed64,1,opt,name=value,proto3" json:"value,omitempty"` + Rate uint32 `protobuf:"varint,2,opt,name=rate,proto3" json:"rate,omitempty"` +} + +func (x *DistributionSample) Reset() { + *x = DistributionSample{} + if protoimpl.UnsafeEnabled { + mi := &file_event_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DistributionSample) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DistributionSample) ProtoMessage() {} + +func (x *DistributionSample) ProtoReflect() protoreflect.Message { + mi := &file_event_proto_msgTypes[22] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DistributionSample.ProtoReflect.Descriptor instead. +func (*DistributionSample) Descriptor() ([]byte, []int) { + return file_event_proto_rawDescGZIP(), []int{22} +} + +func (x *DistributionSample) GetValue() float64 { + if x != nil { + return x.Value + } + return 0 +} + +func (x *DistributionSample) GetRate() uint32 { + if x != nil { + return x.Rate + } + return 0 +} + +type AggregatedHistogram1 struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Buckets []float64 `protobuf:"fixed64,1,rep,packed,name=buckets,proto3" json:"buckets,omitempty"` + Counts []uint32 `protobuf:"varint,2,rep,packed,name=counts,proto3" json:"counts,omitempty"` + Count uint32 `protobuf:"varint,3,opt,name=count,proto3" json:"count,omitempty"` + Sum float64 `protobuf:"fixed64,4,opt,name=sum,proto3" json:"sum,omitempty"` +} + +func (x *AggregatedHistogram1) Reset() { + *x = AggregatedHistogram1{} + if protoimpl.UnsafeEnabled { + mi := &file_event_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AggregatedHistogram1) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AggregatedHistogram1) ProtoMessage() {} + +func (x *AggregatedHistogram1) ProtoReflect() protoreflect.Message { + mi := &file_event_proto_msgTypes[23] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AggregatedHistogram1.ProtoReflect.Descriptor instead. +func (*AggregatedHistogram1) Descriptor() ([]byte, []int) { + return file_event_proto_rawDescGZIP(), []int{23} +} + +func (x *AggregatedHistogram1) GetBuckets() []float64 { + if x != nil { + return x.Buckets + } + return nil +} + +func (x *AggregatedHistogram1) GetCounts() []uint32 { + if x != nil { + return x.Counts + } + return nil +} + +func (x *AggregatedHistogram1) GetCount() uint32 { + if x != nil { + return x.Count + } + return 0 +} + +func (x *AggregatedHistogram1) GetSum() float64 { + if x != nil { + return x.Sum + } + return 0 +} + +type AggregatedHistogram2 struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Buckets []*HistogramBucket `protobuf:"bytes,1,rep,name=buckets,proto3" json:"buckets,omitempty"` + Count uint32 `protobuf:"varint,2,opt,name=count,proto3" json:"count,omitempty"` + Sum float64 `protobuf:"fixed64,3,opt,name=sum,proto3" json:"sum,omitempty"` +} + +func (x *AggregatedHistogram2) Reset() { + *x = AggregatedHistogram2{} + if protoimpl.UnsafeEnabled { + mi := &file_event_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AggregatedHistogram2) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AggregatedHistogram2) ProtoMessage() {} + +func (x *AggregatedHistogram2) ProtoReflect() protoreflect.Message { + mi := &file_event_proto_msgTypes[24] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AggregatedHistogram2.ProtoReflect.Descriptor instead. +func (*AggregatedHistogram2) Descriptor() ([]byte, []int) { + return file_event_proto_rawDescGZIP(), []int{24} +} + +func (x *AggregatedHistogram2) GetBuckets() []*HistogramBucket { + if x != nil { + return x.Buckets + } + return nil +} + +func (x *AggregatedHistogram2) GetCount() uint32 { + if x != nil { + return x.Count + } + return 0 +} + +func (x *AggregatedHistogram2) GetSum() float64 { + if x != nil { + return x.Sum + } + return 0 +} + +type AggregatedHistogram3 struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Buckets []*HistogramBucket3 `protobuf:"bytes,1,rep,name=buckets,proto3" json:"buckets,omitempty"` + Count uint64 `protobuf:"varint,2,opt,name=count,proto3" json:"count,omitempty"` + Sum float64 `protobuf:"fixed64,3,opt,name=sum,proto3" json:"sum,omitempty"` +} + +func (x *AggregatedHistogram3) Reset() { + *x = AggregatedHistogram3{} + if protoimpl.UnsafeEnabled { + mi := &file_event_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AggregatedHistogram3) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AggregatedHistogram3) ProtoMessage() {} + +func (x *AggregatedHistogram3) ProtoReflect() protoreflect.Message { + mi := &file_event_proto_msgTypes[25] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AggregatedHistogram3.ProtoReflect.Descriptor instead. +func (*AggregatedHistogram3) Descriptor() ([]byte, []int) { + return file_event_proto_rawDescGZIP(), []int{25} +} + +func (x *AggregatedHistogram3) GetBuckets() []*HistogramBucket3 { + if x != nil { + return x.Buckets + } + return nil +} + +func (x *AggregatedHistogram3) GetCount() uint64 { + if x != nil { + return x.Count + } + return 0 +} + +func (x *AggregatedHistogram3) GetSum() float64 { + if x != nil { + return x.Sum + } + return 0 +} + +type HistogramBucket struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + UpperLimit float64 `protobuf:"fixed64,1,opt,name=upper_limit,json=upperLimit,proto3" json:"upper_limit,omitempty"` + Count uint32 `protobuf:"varint,2,opt,name=count,proto3" json:"count,omitempty"` +} + +func (x *HistogramBucket) Reset() { + *x = HistogramBucket{} + if protoimpl.UnsafeEnabled { + mi := &file_event_proto_msgTypes[26] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HistogramBucket) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HistogramBucket) ProtoMessage() {} + +func (x *HistogramBucket) ProtoReflect() protoreflect.Message { + mi := &file_event_proto_msgTypes[26] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HistogramBucket.ProtoReflect.Descriptor instead. +func (*HistogramBucket) Descriptor() ([]byte, []int) { + return file_event_proto_rawDescGZIP(), []int{26} +} + +func (x *HistogramBucket) GetUpperLimit() float64 { + if x != nil { + return x.UpperLimit + } + return 0 +} + +func (x *HistogramBucket) GetCount() uint32 { + if x != nil { + return x.Count + } + return 0 +} + +type HistogramBucket3 struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + UpperLimit float64 `protobuf:"fixed64,1,opt,name=upper_limit,json=upperLimit,proto3" json:"upper_limit,omitempty"` + Count uint64 `protobuf:"varint,2,opt,name=count,proto3" json:"count,omitempty"` +} + +func (x *HistogramBucket3) Reset() { + *x = HistogramBucket3{} + if protoimpl.UnsafeEnabled { + mi := &file_event_proto_msgTypes[27] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HistogramBucket3) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HistogramBucket3) ProtoMessage() {} + +func (x *HistogramBucket3) ProtoReflect() protoreflect.Message { + mi := &file_event_proto_msgTypes[27] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HistogramBucket3.ProtoReflect.Descriptor instead. +func (*HistogramBucket3) Descriptor() ([]byte, []int) { + return file_event_proto_rawDescGZIP(), []int{27} +} + +func (x *HistogramBucket3) GetUpperLimit() float64 { + if x != nil { + return x.UpperLimit + } + return 0 +} + +func (x *HistogramBucket3) GetCount() uint64 { + if x != nil { + return x.Count + } + return 0 +} + +type AggregatedSummary1 struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Quantiles []float64 `protobuf:"fixed64,1,rep,packed,name=quantiles,proto3" json:"quantiles,omitempty"` + Values []float64 `protobuf:"fixed64,2,rep,packed,name=values,proto3" json:"values,omitempty"` + Count uint32 `protobuf:"varint,3,opt,name=count,proto3" json:"count,omitempty"` + Sum float64 `protobuf:"fixed64,4,opt,name=sum,proto3" json:"sum,omitempty"` +} + +func (x *AggregatedSummary1) Reset() { + *x = AggregatedSummary1{} + if protoimpl.UnsafeEnabled { + mi := &file_event_proto_msgTypes[28] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AggregatedSummary1) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AggregatedSummary1) ProtoMessage() {} + +func (x *AggregatedSummary1) ProtoReflect() protoreflect.Message { + mi := &file_event_proto_msgTypes[28] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AggregatedSummary1.ProtoReflect.Descriptor instead. +func (*AggregatedSummary1) Descriptor() ([]byte, []int) { + return file_event_proto_rawDescGZIP(), []int{28} +} + +func (x *AggregatedSummary1) GetQuantiles() []float64 { + if x != nil { + return x.Quantiles + } + return nil +} + +func (x *AggregatedSummary1) GetValues() []float64 { + if x != nil { + return x.Values + } + return nil +} + +func (x *AggregatedSummary1) GetCount() uint32 { + if x != nil { + return x.Count + } + return 0 +} + +func (x *AggregatedSummary1) GetSum() float64 { + if x != nil { + return x.Sum + } + return 0 +} + +type AggregatedSummary2 struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Quantiles []*SummaryQuantile `protobuf:"bytes,1,rep,name=quantiles,proto3" json:"quantiles,omitempty"` + Count uint32 `protobuf:"varint,2,opt,name=count,proto3" json:"count,omitempty"` + Sum float64 `protobuf:"fixed64,3,opt,name=sum,proto3" json:"sum,omitempty"` +} + +func (x *AggregatedSummary2) Reset() { + *x = AggregatedSummary2{} + if protoimpl.UnsafeEnabled { + mi := &file_event_proto_msgTypes[29] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AggregatedSummary2) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AggregatedSummary2) ProtoMessage() {} + +func (x *AggregatedSummary2) ProtoReflect() protoreflect.Message { + mi := &file_event_proto_msgTypes[29] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AggregatedSummary2.ProtoReflect.Descriptor instead. +func (*AggregatedSummary2) Descriptor() ([]byte, []int) { + return file_event_proto_rawDescGZIP(), []int{29} +} + +func (x *AggregatedSummary2) GetQuantiles() []*SummaryQuantile { + if x != nil { + return x.Quantiles + } + return nil +} + +func (x *AggregatedSummary2) GetCount() uint32 { + if x != nil { + return x.Count + } + return 0 +} + +func (x *AggregatedSummary2) GetSum() float64 { + if x != nil { + return x.Sum + } + return 0 +} + +type AggregatedSummary3 struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Quantiles []*SummaryQuantile `protobuf:"bytes,1,rep,name=quantiles,proto3" json:"quantiles,omitempty"` + Count uint64 `protobuf:"varint,2,opt,name=count,proto3" json:"count,omitempty"` + Sum float64 `protobuf:"fixed64,3,opt,name=sum,proto3" json:"sum,omitempty"` +} + +func (x *AggregatedSummary3) Reset() { + *x = AggregatedSummary3{} + if protoimpl.UnsafeEnabled { + mi := &file_event_proto_msgTypes[30] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AggregatedSummary3) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AggregatedSummary3) ProtoMessage() {} + +func (x *AggregatedSummary3) ProtoReflect() protoreflect.Message { + mi := &file_event_proto_msgTypes[30] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AggregatedSummary3.ProtoReflect.Descriptor instead. +func (*AggregatedSummary3) Descriptor() ([]byte, []int) { + return file_event_proto_rawDescGZIP(), []int{30} +} + +func (x *AggregatedSummary3) GetQuantiles() []*SummaryQuantile { + if x != nil { + return x.Quantiles + } + return nil +} + +func (x *AggregatedSummary3) GetCount() uint64 { + if x != nil { + return x.Count + } + return 0 +} + +func (x *AggregatedSummary3) GetSum() float64 { + if x != nil { + return x.Sum + } + return 0 +} + +type SummaryQuantile struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Quantile float64 `protobuf:"fixed64,1,opt,name=quantile,proto3" json:"quantile,omitempty"` + Value float64 `protobuf:"fixed64,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *SummaryQuantile) Reset() { + *x = SummaryQuantile{} + if protoimpl.UnsafeEnabled { + mi := &file_event_proto_msgTypes[31] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SummaryQuantile) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SummaryQuantile) ProtoMessage() {} + +func (x *SummaryQuantile) ProtoReflect() protoreflect.Message { + mi := &file_event_proto_msgTypes[31] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SummaryQuantile.ProtoReflect.Descriptor instead. +func (*SummaryQuantile) Descriptor() ([]byte, []int) { + return file_event_proto_rawDescGZIP(), []int{31} +} + +func (x *SummaryQuantile) GetQuantile() float64 { + if x != nil { + return x.Quantile + } + return 0 +} + +func (x *SummaryQuantile) GetValue() float64 { + if x != nil { + return x.Value + } + return 0 +} + +type Sketch struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Sketch: + // + // *Sketch_AgentDdSketch + Sketch isSketch_Sketch `protobuf_oneof:"sketch"` +} + +func (x *Sketch) Reset() { + *x = Sketch{} + if protoimpl.UnsafeEnabled { + mi := &file_event_proto_msgTypes[32] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Sketch) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Sketch) ProtoMessage() {} + +func (x *Sketch) ProtoReflect() protoreflect.Message { + mi := &file_event_proto_msgTypes[32] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Sketch.ProtoReflect.Descriptor instead. +func (*Sketch) Descriptor() ([]byte, []int) { + return file_event_proto_rawDescGZIP(), []int{32} +} + +func (m *Sketch) GetSketch() isSketch_Sketch { + if m != nil { + return m.Sketch + } + return nil +} + +func (x *Sketch) GetAgentDdSketch() *Sketch_AgentDDSketch { + if x, ok := x.GetSketch().(*Sketch_AgentDdSketch); ok { + return x.AgentDdSketch + } + return nil +} + +type isSketch_Sketch interface { + isSketch_Sketch() +} + +type Sketch_AgentDdSketch struct { + AgentDdSketch *Sketch_AgentDDSketch `protobuf:"bytes,1,opt,name=agent_dd_sketch,json=agentDdSketch,proto3,oneof"` +} + +func (*Sketch_AgentDdSketch) isSketch_Sketch() {} + +type Sketch_AgentDDSketch struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Summary statistics for the samples in this sketch. + Count uint32 `protobuf:"varint,1,opt,name=count,proto3" json:"count,omitempty"` + Min float64 `protobuf:"fixed64,2,opt,name=min,proto3" json:"min,omitempty"` + Max float64 `protobuf:"fixed64,3,opt,name=max,proto3" json:"max,omitempty"` + Sum float64 `protobuf:"fixed64,4,opt,name=sum,proto3" json:"sum,omitempty"` + Avg float64 `protobuf:"fixed64,5,opt,name=avg,proto3" json:"avg,omitempty"` + // The bins (buckets) of this sketch, where `k` and `n` are unzipped pairs. + // `k` is the list of bin indexes that are populated, and `n` is the count of samples + // within the given bin. + K []int32 `protobuf:"zigzag32,6,rep,packed,name=k,proto3" json:"k,omitempty"` + N []uint32 `protobuf:"varint,7,rep,packed,name=n,proto3" json:"n,omitempty"` +} + +func (x *Sketch_AgentDDSketch) Reset() { + *x = Sketch_AgentDDSketch{} + if protoimpl.UnsafeEnabled { + mi := &file_event_proto_msgTypes[39] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Sketch_AgentDDSketch) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Sketch_AgentDDSketch) ProtoMessage() {} + +func (x *Sketch_AgentDDSketch) ProtoReflect() protoreflect.Message { + mi := &file_event_proto_msgTypes[39] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Sketch_AgentDDSketch.ProtoReflect.Descriptor instead. +func (*Sketch_AgentDDSketch) Descriptor() ([]byte, []int) { + return file_event_proto_rawDescGZIP(), []int{32, 0} +} + +func (x *Sketch_AgentDDSketch) GetCount() uint32 { + if x != nil { + return x.Count + } + return 0 +} + +func (x *Sketch_AgentDDSketch) GetMin() float64 { + if x != nil { + return x.Min + } + return 0 +} + +func (x *Sketch_AgentDDSketch) GetMax() float64 { + if x != nil { + return x.Max + } + return 0 +} + +func (x *Sketch_AgentDDSketch) GetSum() float64 { + if x != nil { + return x.Sum + } + return 0 +} + +func (x *Sketch_AgentDDSketch) GetAvg() float64 { + if x != nil { + return x.Avg + } + return 0 +} + +func (x *Sketch_AgentDDSketch) GetK() []int32 { + if x != nil { + return x.K + } + return nil +} + +func (x *Sketch_AgentDDSketch) GetN() []uint32 { + if x != nil { + return x.N + } + return nil +} + +var File_event_proto protoreflect.FileDescriptor + +var file_event_proto_rawDesc = []byte{ + 0x0a, 0x0b, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x65, + 0x76, 0x65, 0x6e, 0x74, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x9a, 0x01, 0x0a, 0x0a, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x41, + 0x72, 0x72, 0x61, 0x79, 0x12, 0x25, 0x0a, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x4c, 0x6f, 0x67, 0x41, 0x72, + 0x72, 0x61, 0x79, 0x48, 0x00, 0x52, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x12, 0x2e, 0x0a, 0x07, 0x6d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x65, + 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x41, 0x72, 0x72, 0x61, 0x79, + 0x48, 0x00, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x2b, 0x0a, 0x06, 0x74, + 0x72, 0x61, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x65, 0x76, + 0x65, 0x6e, 0x74, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x41, 0x72, 0x72, 0x61, 0x79, 0x48, 0x00, + 0x52, 0x06, 0x74, 0x72, 0x61, 0x63, 0x65, 0x73, 0x42, 0x08, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, + 0x74, 0x73, 0x22, 0x2a, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x41, 0x72, 0x72, 0x61, 0x79, 0x12, 0x1e, + 0x0a, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x65, + 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x22, 0x36, + 0x0a, 0x0b, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x41, 0x72, 0x72, 0x61, 0x79, 0x12, 0x27, 0x0a, + 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, + 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x07, 0x6d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x32, 0x0a, 0x0a, 0x54, 0x72, 0x61, 0x63, 0x65, 0x41, + 0x72, 0x72, 0x61, 0x79, 0x12, 0x24, 0x0a, 0x06, 0x74, 0x72, 0x61, 0x63, 0x65, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x54, 0x72, 0x61, + 0x63, 0x65, 0x52, 0x06, 0x74, 0x72, 0x61, 0x63, 0x65, 0x73, 0x22, 0x86, 0x01, 0x0a, 0x0c, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x03, 0x6c, + 0x6f, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, + 0x2e, 0x4c, 0x6f, 0x67, 0x48, 0x00, 0x52, 0x03, 0x6c, 0x6f, 0x67, 0x12, 0x27, 0x0a, 0x06, 0x6d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x65, 0x76, + 0x65, 0x6e, 0x74, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x48, 0x00, 0x52, 0x06, 0x6d, 0x65, + 0x74, 0x72, 0x69, 0x63, 0x12, 0x24, 0x0a, 0x05, 0x74, 0x72, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x54, 0x72, 0x61, 0x63, + 0x65, 0x48, 0x00, 0x52, 0x05, 0x74, 0x72, 0x61, 0x63, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, + 0x65, 0x6e, 0x74, 0x22, 0x86, 0x02, 0x0a, 0x03, 0x4c, 0x6f, 0x67, 0x12, 0x2e, 0x0a, 0x06, 0x66, + 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x65, 0x76, + 0x65, 0x6e, 0x74, 0x2e, 0x4c, 0x6f, 0x67, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x22, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x65, 0x76, 0x65, + 0x6e, 0x74, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, + 0x2c, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0c, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, + 0x02, 0x18, 0x01, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x34, 0x0a, + 0x0d, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x66, 0x75, 0x6c, 0x6c, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0c, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x46, + 0x75, 0x6c, 0x6c, 0x1a, 0x47, 0x0a, 0x0b, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xe6, 0x01, 0x0a, + 0x05, 0x54, 0x72, 0x61, 0x63, 0x65, 0x12, 0x30, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x54, + 0x72, 0x61, 0x63, 0x65, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x2c, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x65, 0x76, 0x65, + 0x6e, 0x74, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x02, 0x18, 0x01, 0x52, 0x08, 0x6d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x34, 0x0a, 0x0d, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x5f, 0x66, 0x75, 0x6c, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, + 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0c, + 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x46, 0x75, 0x6c, 0x6c, 0x1a, 0x47, 0x0a, 0x0b, + 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x22, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x65, + 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x88, 0x01, 0x0a, 0x08, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4d, + 0x61, 0x70, 0x12, 0x33, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x4d, 0x61, 0x70, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x1a, 0x47, 0x0a, 0x0b, 0x46, 0x69, 0x65, 0x6c, 0x64, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x22, 0x30, 0x0a, 0x0a, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x41, 0x72, 0x72, 0x61, 0x79, 0x12, 0x22, + 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, + 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x69, 0x74, 0x65, + 0x6d, 0x73, 0x22, 0xb8, 0x02, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1d, 0x0a, 0x09, + 0x72, 0x61, 0x77, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, + 0x00, 0x52, 0x08, 0x72, 0x61, 0x77, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x3a, 0x0a, 0x09, 0x74, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x00, 0x52, 0x09, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x1a, 0x0a, 0x07, 0x69, 0x6e, 0x74, 0x65, 0x67, + 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, 0x07, 0x69, 0x6e, 0x74, 0x65, + 0x67, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x05, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x01, 0x48, 0x00, 0x52, 0x05, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x12, 0x1a, 0x0a, 0x07, 0x62, + 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x07, + 0x62, 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, 0x12, 0x23, 0x0a, 0x03, 0x6d, 0x61, 0x70, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x4d, 0x61, 0x70, 0x48, 0x00, 0x52, 0x03, 0x6d, 0x61, 0x70, 0x12, 0x29, 0x0a, 0x05, + 0x61, 0x72, 0x72, 0x61, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x65, 0x76, + 0x65, 0x6e, 0x74, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x41, 0x72, 0x72, 0x61, 0x79, 0x48, 0x00, + 0x52, 0x05, 0x61, 0x72, 0x72, 0x61, 0x79, 0x12, 0x26, 0x0a, 0x04, 0x6e, 0x75, 0x6c, 0x6c, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x4e, 0x75, 0x6c, 0x6c, 0x48, 0x00, 0x52, 0x04, 0x6e, 0x75, 0x6c, 0x6c, 0x42, + 0x06, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, 0xd7, 0x01, + 0x0a, 0x15, 0x44, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x4d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2a, 0x0a, 0x0e, 0x6f, 0x72, 0x69, 0x67, 0x69, + 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x48, + 0x00, 0x52, 0x0d, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, + 0x88, 0x01, 0x01, 0x12, 0x2c, 0x0a, 0x0f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x5f, 0x63, 0x61, + 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x01, 0x52, 0x0e, + 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x88, 0x01, + 0x01, 0x12, 0x2a, 0x0a, 0x0e, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x5f, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x02, 0x52, 0x0d, 0x6f, 0x72, 0x69, + 0x67, 0x69, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x88, 0x01, 0x01, 0x42, 0x11, 0x0a, + 0x0f, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, + 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x5f, 0x63, 0x61, 0x74, 0x65, + 0x67, 0x6f, 0x72, 0x79, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x5f, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x22, 0x7c, 0x0a, 0x07, 0x53, 0x65, 0x63, 0x72, 0x65, + 0x74, 0x73, 0x12, 0x35, 0x0a, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x63, 0x72, + 0x65, 0x74, 0x73, 0x2e, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x52, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x1a, 0x3a, 0x0a, 0x0c, 0x45, 0x6e, 0x74, + 0x72, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x4a, 0x0a, 0x08, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x49, + 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x12, + 0x17, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, + 0x04, 0x70, 0x6f, 0x72, 0x74, 0x88, 0x01, 0x01, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x70, 0x6f, 0x72, + 0x74, 0x22, 0xc6, 0x02, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x22, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, + 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x12, 0x54, 0x0a, 0x17, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x5f, 0x6f, 0x72, + 0x69, 0x67, 0x69, 0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x44, 0x61, 0x74, 0x61, + 0x64, 0x6f, 0x67, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x52, 0x15, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x20, 0x0a, 0x09, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x08, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x24, 0x0a, 0x0b, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, + 0x01, 0x52, 0x0a, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x88, 0x01, 0x01, + 0x12, 0x30, 0x0a, 0x0b, 0x75, 0x70, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x4f, 0x75, + 0x74, 0x70, 0x75, 0x74, 0x49, 0x64, 0x52, 0x0a, 0x75, 0x70, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x49, 0x64, 0x12, 0x28, 0x0a, 0x07, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x63, 0x72, + 0x65, 0x74, 0x73, 0x52, 0x07, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x42, 0x0c, 0x0a, 0x0a, + 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x22, 0xbe, 0x0a, 0x0a, 0x06, 0x4d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x12, 0x32, 0x0a, 0x07, 0x74, 0x61, 0x67, 0x73, 0x5f, 0x76, 0x31, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x4d, 0x65, 0x74, + 0x72, 0x69, 0x63, 0x2e, 0x54, 0x61, 0x67, 0x73, 0x56, 0x31, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x06, 0x74, 0x61, 0x67, 0x73, 0x56, 0x31, 0x12, 0x32, 0x0a, 0x07, 0x74, 0x61, 0x67, 0x73, 0x5f, + 0x76, 0x32, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, + 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x54, 0x61, 0x67, 0x73, 0x56, 0x32, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x06, 0x74, 0x61, 0x67, 0x73, 0x56, 0x32, 0x12, 0x26, 0x0a, 0x04, 0x6b, + 0x69, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x65, 0x76, 0x65, 0x6e, + 0x74, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x04, 0x6b, + 0x69, 0x6e, 0x64, 0x12, 0x2a, 0x0a, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x75, + 0x6e, 0x74, 0x65, 0x72, 0x48, 0x00, 0x52, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x12, + 0x24, 0x0a, 0x05, 0x67, 0x61, 0x75, 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, + 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x47, 0x61, 0x75, 0x67, 0x65, 0x48, 0x00, 0x52, 0x05, + 0x67, 0x61, 0x75, 0x67, 0x65, 0x12, 0x1e, 0x0a, 0x03, 0x73, 0x65, 0x74, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x74, 0x48, 0x00, + 0x52, 0x03, 0x73, 0x65, 0x74, 0x12, 0x3c, 0x0a, 0x0d, 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x65, + 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x44, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, + 0x6e, 0x31, 0x48, 0x00, 0x52, 0x0d, 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, + 0x6f, 0x6e, 0x31, 0x12, 0x52, 0x0a, 0x15, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, + 0x64, 0x5f, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x31, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65, + 0x67, 0x61, 0x74, 0x65, 0x64, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x31, 0x48, + 0x00, 0x52, 0x14, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x48, 0x69, 0x73, + 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x31, 0x12, 0x4c, 0x0a, 0x13, 0x61, 0x67, 0x67, 0x72, 0x65, + 0x67, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x31, 0x18, 0x0a, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x67, 0x67, + 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x31, 0x48, + 0x00, 0x52, 0x12, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x53, 0x75, 0x6d, + 0x6d, 0x61, 0x72, 0x79, 0x31, 0x12, 0x3c, 0x0a, 0x0d, 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x32, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x65, + 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x44, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, + 0x6e, 0x32, 0x48, 0x00, 0x52, 0x0d, 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, + 0x6f, 0x6e, 0x32, 0x12, 0x52, 0x0a, 0x15, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, + 0x64, 0x5f, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x32, 0x18, 0x0d, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65, + 0x67, 0x61, 0x74, 0x65, 0x64, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x32, 0x48, + 0x00, 0x52, 0x14, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x48, 0x69, 0x73, + 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x32, 0x12, 0x4c, 0x0a, 0x13, 0x61, 0x67, 0x67, 0x72, 0x65, + 0x67, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x32, 0x18, 0x0e, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x67, 0x67, + 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x32, 0x48, + 0x00, 0x52, 0x12, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x53, 0x75, 0x6d, + 0x6d, 0x61, 0x72, 0x79, 0x32, 0x12, 0x27, 0x0a, 0x06, 0x73, 0x6b, 0x65, 0x74, 0x63, 0x68, 0x18, + 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x6b, + 0x65, 0x74, 0x63, 0x68, 0x48, 0x00, 0x52, 0x06, 0x73, 0x6b, 0x65, 0x74, 0x63, 0x68, 0x12, 0x52, + 0x0a, 0x15, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x68, 0x69, 0x73, + 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x33, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, + 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, + 0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x33, 0x48, 0x00, 0x52, 0x14, 0x61, 0x67, + 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, + 0x6d, 0x33, 0x12, 0x4c, 0x0a, 0x13, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, + 0x5f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x33, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x19, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, + 0x65, 0x64, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x33, 0x48, 0x00, 0x52, 0x12, 0x61, 0x67, + 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x33, + 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x0b, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1f, + 0x0a, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x6d, 0x73, 0x18, 0x12, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x4d, 0x73, 0x12, + 0x2c, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x13, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0c, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, + 0x02, 0x18, 0x01, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x34, 0x0a, + 0x0d, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x66, 0x75, 0x6c, 0x6c, 0x18, 0x15, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0c, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x46, + 0x75, 0x6c, 0x6c, 0x1a, 0x39, 0x0a, 0x0b, 0x54, 0x61, 0x67, 0x73, 0x56, 0x31, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4b, + 0x0a, 0x0b, 0x54, 0x61, 0x67, 0x73, 0x56, 0x32, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x26, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, + 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x54, 0x61, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x25, 0x0a, 0x04, 0x4b, + 0x69, 0x6e, 0x64, 0x12, 0x0f, 0x0a, 0x0b, 0x49, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x61, 0x6c, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x41, 0x62, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65, + 0x10, 0x01, 0x42, 0x07, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x34, 0x0a, 0x09, 0x54, + 0x61, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x27, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, + 0x2e, 0x54, 0x61, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x73, 0x22, 0x2f, 0x0a, 0x08, 0x54, 0x61, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x19, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x22, 0x1f, 0x0a, 0x07, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x22, 0x1d, 0x0a, 0x05, 0x47, 0x61, 0x75, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x22, 0x1d, 0x0a, 0x03, 0x53, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x73, 0x22, 0x7e, 0x0a, 0x0d, 0x44, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, + 0x6e, 0x31, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x01, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x61, + 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0d, + 0x52, 0x0b, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x61, 0x74, 0x65, 0x73, 0x12, 0x32, 0x0a, + 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x14, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, + 0x69, 0x63, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, + 0x63, 0x22, 0x78, 0x0a, 0x0d, 0x44, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, + 0x6e, 0x32, 0x12, 0x33, 0x0a, 0x07, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x44, 0x69, 0x73, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x07, + 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x73, 0x74, 0x69, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x65, 0x76, 0x65, + 0x6e, 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, 0x4b, 0x69, 0x6e, 0x64, + 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, 0x22, 0x3e, 0x0a, 0x12, 0x44, + 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x61, 0x6d, 0x70, 0x6c, + 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x61, 0x74, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x72, 0x61, 0x74, 0x65, 0x22, 0x70, 0x0a, 0x14, 0x41, + 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, + 0x61, 0x6d, 0x31, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x01, 0x52, 0x07, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x12, 0x16, 0x0a, + 0x06, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x06, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x73, + 0x75, 0x6d, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x73, 0x75, 0x6d, 0x22, 0x70, 0x0a, + 0x14, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x48, 0x69, 0x73, 0x74, 0x6f, + 0x67, 0x72, 0x61, 0x6d, 0x32, 0x12, 0x30, 0x0a, 0x07, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x48, + 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x07, + 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x10, 0x0a, + 0x03, 0x73, 0x75, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x73, 0x75, 0x6d, 0x22, + 0x71, 0x0a, 0x14, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x48, 0x69, 0x73, + 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x33, 0x12, 0x31, 0x0a, 0x07, 0x62, 0x75, 0x63, 0x6b, 0x65, + 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, + 0x2e, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, + 0x33, 0x52, 0x07, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x12, 0x10, 0x0a, 0x03, 0x73, 0x75, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x73, + 0x75, 0x6d, 0x22, 0x48, 0x0a, 0x0f, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x42, + 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x75, 0x70, 0x70, 0x65, 0x72, 0x5f, 0x6c, + 0x69, 0x6d, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x75, 0x70, 0x70, 0x65, + 0x72, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x49, 0x0a, 0x10, + 0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x33, + 0x12, 0x1f, 0x0a, 0x0b, 0x75, 0x70, 0x70, 0x65, 0x72, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x75, 0x70, 0x70, 0x65, 0x72, 0x4c, 0x69, 0x6d, 0x69, + 0x74, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x72, 0x0a, 0x12, 0x41, 0x67, 0x67, 0x72, 0x65, + 0x67, 0x61, 0x74, 0x65, 0x64, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x31, 0x12, 0x1c, 0x0a, + 0x09, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x01, + 0x52, 0x09, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x01, 0x52, 0x06, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x75, 0x6d, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x73, 0x75, 0x6d, 0x22, 0x72, 0x0a, 0x12, 0x41, + 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, + 0x32, 0x12, 0x34, 0x0a, 0x09, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x75, 0x6d, + 0x6d, 0x61, 0x72, 0x79, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x52, 0x09, 0x71, 0x75, + 0x61, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x10, 0x0a, + 0x03, 0x73, 0x75, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x73, 0x75, 0x6d, 0x22, + 0x72, 0x0a, 0x12, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x53, 0x75, 0x6d, + 0x6d, 0x61, 0x72, 0x79, 0x33, 0x12, 0x34, 0x0a, 0x09, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x6c, + 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, + 0x2e, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x6c, 0x65, + 0x52, 0x09, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x75, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, + 0x73, 0x75, 0x6d, 0x22, 0x43, 0x0a, 0x0f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x51, 0x75, + 0x61, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, + 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, + 0x6c, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xe5, 0x01, 0x0a, 0x06, 0x53, 0x6b, 0x65, + 0x74, 0x63, 0x68, 0x12, 0x45, 0x0a, 0x0f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x64, 0x64, 0x5f, + 0x73, 0x6b, 0x65, 0x74, 0x63, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, + 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x6b, 0x65, 0x74, 0x63, 0x68, 0x2e, 0x41, 0x67, 0x65, 0x6e, + 0x74, 0x44, 0x44, 0x53, 0x6b, 0x65, 0x74, 0x63, 0x68, 0x48, 0x00, 0x52, 0x0d, 0x61, 0x67, 0x65, + 0x6e, 0x74, 0x44, 0x64, 0x53, 0x6b, 0x65, 0x74, 0x63, 0x68, 0x1a, 0x89, 0x01, 0x0a, 0x0d, 0x41, + 0x67, 0x65, 0x6e, 0x74, 0x44, 0x44, 0x53, 0x6b, 0x65, 0x74, 0x63, 0x68, 0x12, 0x14, 0x0a, 0x05, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, + 0x03, 0x6d, 0x69, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x61, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x01, 0x52, 0x03, 0x6d, 0x61, 0x78, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x75, 0x6d, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x01, 0x52, 0x03, 0x73, 0x75, 0x6d, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x76, 0x67, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x61, 0x76, 0x67, 0x12, 0x0c, 0x0a, 0x01, 0x6b, 0x18, + 0x06, 0x20, 0x03, 0x28, 0x11, 0x52, 0x01, 0x6b, 0x12, 0x0c, 0x0a, 0x01, 0x6e, 0x18, 0x07, 0x20, + 0x03, 0x28, 0x0d, 0x52, 0x01, 0x6e, 0x42, 0x08, 0x0a, 0x06, 0x73, 0x6b, 0x65, 0x74, 0x63, 0x68, + 0x2a, 0x1b, 0x0a, 0x09, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4e, 0x75, 0x6c, 0x6c, 0x12, 0x0e, 0x0a, + 0x0a, 0x4e, 0x55, 0x4c, 0x4c, 0x5f, 0x56, 0x41, 0x4c, 0x55, 0x45, 0x10, 0x00, 0x2a, 0x2b, 0x0a, + 0x0d, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x0d, + 0x0a, 0x09, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x10, 0x00, 0x12, 0x0b, 0x0a, + 0x07, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x10, 0x01, 0x42, 0x83, 0x01, 0x0a, 0x09, 0x63, + 0x6f, 0x6d, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x0a, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x36, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x6b, 0x61, 0x61, 0x73, 0x6f, 0x70, 0x73, 0x2f, 0x76, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x2d, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x2f, 0x76, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2f, 0x67, 0x65, 0x6e, 0xa2, 0x02, + 0x03, 0x45, 0x58, 0x58, 0xaa, 0x02, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0xca, 0x02, 0x05, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0xe2, 0x02, 0x11, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x5c, 0x47, 0x50, 0x42, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_event_proto_rawDescOnce sync.Once + file_event_proto_rawDescData = file_event_proto_rawDesc +) + +func file_event_proto_rawDescGZIP() []byte { + file_event_proto_rawDescOnce.Do(func() { + file_event_proto_rawDescData = protoimpl.X.CompressGZIP(file_event_proto_rawDescData) + }) + return file_event_proto_rawDescData +} + +var file_event_proto_enumTypes = make([]protoimpl.EnumInfo, 3) +var file_event_proto_msgTypes = make([]protoimpl.MessageInfo, 40) +var file_event_proto_goTypes = []any{ + (ValueNull)(0), // 0: event.ValueNull + (StatisticKind)(0), // 1: event.StatisticKind + (Metric_Kind)(0), // 2: event.Metric.Kind + (*EventArray)(nil), // 3: event.EventArray + (*LogArray)(nil), // 4: event.LogArray + (*MetricArray)(nil), // 5: event.MetricArray + (*TraceArray)(nil), // 6: event.TraceArray + (*EventWrapper)(nil), // 7: event.EventWrapper + (*Log)(nil), // 8: event.Log + (*Trace)(nil), // 9: event.Trace + (*ValueMap)(nil), // 10: event.ValueMap + (*ValueArray)(nil), // 11: event.ValueArray + (*Value)(nil), // 12: event.Value + (*DatadogOriginMetadata)(nil), // 13: event.DatadogOriginMetadata + (*Secrets)(nil), // 14: event.Secrets + (*OutputId)(nil), // 15: event.OutputId + (*Metadata)(nil), // 16: event.Metadata + (*Metric)(nil), // 17: event.Metric + (*TagValues)(nil), // 18: event.TagValues + (*TagValue)(nil), // 19: event.TagValue + (*Counter)(nil), // 20: event.Counter + (*Gauge)(nil), // 21: event.Gauge + (*Set)(nil), // 22: event.Set + (*Distribution1)(nil), // 23: event.Distribution1 + (*Distribution2)(nil), // 24: event.Distribution2 + (*DistributionSample)(nil), // 25: event.DistributionSample + (*AggregatedHistogram1)(nil), // 26: event.AggregatedHistogram1 + (*AggregatedHistogram2)(nil), // 27: event.AggregatedHistogram2 + (*AggregatedHistogram3)(nil), // 28: event.AggregatedHistogram3 + (*HistogramBucket)(nil), // 29: event.HistogramBucket + (*HistogramBucket3)(nil), // 30: event.HistogramBucket3 + (*AggregatedSummary1)(nil), // 31: event.AggregatedSummary1 + (*AggregatedSummary2)(nil), // 32: event.AggregatedSummary2 + (*AggregatedSummary3)(nil), // 33: event.AggregatedSummary3 + (*SummaryQuantile)(nil), // 34: event.SummaryQuantile + (*Sketch)(nil), // 35: event.Sketch + nil, // 36: event.Log.FieldsEntry + nil, // 37: event.Trace.FieldsEntry + nil, // 38: event.ValueMap.FieldsEntry + nil, // 39: event.Secrets.EntriesEntry + nil, // 40: event.Metric.TagsV1Entry + nil, // 41: event.Metric.TagsV2Entry + (*Sketch_AgentDDSketch)(nil), // 42: event.Sketch.AgentDDSketch + (*timestamppb.Timestamp)(nil), // 43: google.protobuf.Timestamp +} +var file_event_proto_depIdxs = []int32{ + 4, // 0: event.EventArray.logs:type_name -> event.LogArray + 5, // 1: event.EventArray.metrics:type_name -> event.MetricArray + 6, // 2: event.EventArray.traces:type_name -> event.TraceArray + 8, // 3: event.LogArray.logs:type_name -> event.Log + 17, // 4: event.MetricArray.metrics:type_name -> event.Metric + 9, // 5: event.TraceArray.traces:type_name -> event.Trace + 8, // 6: event.EventWrapper.log:type_name -> event.Log + 17, // 7: event.EventWrapper.metric:type_name -> event.Metric + 9, // 8: event.EventWrapper.trace:type_name -> event.Trace + 36, // 9: event.Log.fields:type_name -> event.Log.FieldsEntry + 12, // 10: event.Log.value:type_name -> event.Value + 12, // 11: event.Log.metadata:type_name -> event.Value + 16, // 12: event.Log.metadata_full:type_name -> event.Metadata + 37, // 13: event.Trace.fields:type_name -> event.Trace.FieldsEntry + 12, // 14: event.Trace.metadata:type_name -> event.Value + 16, // 15: event.Trace.metadata_full:type_name -> event.Metadata + 38, // 16: event.ValueMap.fields:type_name -> event.ValueMap.FieldsEntry + 12, // 17: event.ValueArray.items:type_name -> event.Value + 43, // 18: event.Value.timestamp:type_name -> google.protobuf.Timestamp + 10, // 19: event.Value.map:type_name -> event.ValueMap + 11, // 20: event.Value.array:type_name -> event.ValueArray + 0, // 21: event.Value.null:type_name -> event.ValueNull + 39, // 22: event.Secrets.entries:type_name -> event.Secrets.EntriesEntry + 12, // 23: event.Metadata.value:type_name -> event.Value + 13, // 24: event.Metadata.datadog_origin_metadata:type_name -> event.DatadogOriginMetadata + 15, // 25: event.Metadata.upstream_id:type_name -> event.OutputId + 14, // 26: event.Metadata.secrets:type_name -> event.Secrets + 43, // 27: event.Metric.timestamp:type_name -> google.protobuf.Timestamp + 40, // 28: event.Metric.tags_v1:type_name -> event.Metric.TagsV1Entry + 41, // 29: event.Metric.tags_v2:type_name -> event.Metric.TagsV2Entry + 2, // 30: event.Metric.kind:type_name -> event.Metric.Kind + 20, // 31: event.Metric.counter:type_name -> event.Counter + 21, // 32: event.Metric.gauge:type_name -> event.Gauge + 22, // 33: event.Metric.set:type_name -> event.Set + 23, // 34: event.Metric.distribution1:type_name -> event.Distribution1 + 26, // 35: event.Metric.aggregated_histogram1:type_name -> event.AggregatedHistogram1 + 31, // 36: event.Metric.aggregated_summary1:type_name -> event.AggregatedSummary1 + 24, // 37: event.Metric.distribution2:type_name -> event.Distribution2 + 27, // 38: event.Metric.aggregated_histogram2:type_name -> event.AggregatedHistogram2 + 32, // 39: event.Metric.aggregated_summary2:type_name -> event.AggregatedSummary2 + 35, // 40: event.Metric.sketch:type_name -> event.Sketch + 28, // 41: event.Metric.aggregated_histogram3:type_name -> event.AggregatedHistogram3 + 33, // 42: event.Metric.aggregated_summary3:type_name -> event.AggregatedSummary3 + 12, // 43: event.Metric.metadata:type_name -> event.Value + 16, // 44: event.Metric.metadata_full:type_name -> event.Metadata + 19, // 45: event.TagValues.values:type_name -> event.TagValue + 1, // 46: event.Distribution1.statistic:type_name -> event.StatisticKind + 25, // 47: event.Distribution2.samples:type_name -> event.DistributionSample + 1, // 48: event.Distribution2.statistic:type_name -> event.StatisticKind + 29, // 49: event.AggregatedHistogram2.buckets:type_name -> event.HistogramBucket + 30, // 50: event.AggregatedHistogram3.buckets:type_name -> event.HistogramBucket3 + 34, // 51: event.AggregatedSummary2.quantiles:type_name -> event.SummaryQuantile + 34, // 52: event.AggregatedSummary3.quantiles:type_name -> event.SummaryQuantile + 42, // 53: event.Sketch.agent_dd_sketch:type_name -> event.Sketch.AgentDDSketch + 12, // 54: event.Log.FieldsEntry.value:type_name -> event.Value + 12, // 55: event.Trace.FieldsEntry.value:type_name -> event.Value + 12, // 56: event.ValueMap.FieldsEntry.value:type_name -> event.Value + 18, // 57: event.Metric.TagsV2Entry.value:type_name -> event.TagValues + 58, // [58:58] is the sub-list for method output_type + 58, // [58:58] is the sub-list for method input_type + 58, // [58:58] is the sub-list for extension type_name + 58, // [58:58] is the sub-list for extension extendee + 0, // [0:58] is the sub-list for field type_name +} + +func init() { file_event_proto_init() } +func file_event_proto_init() { + if File_event_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_event_proto_msgTypes[0].Exporter = func(v any, i int) any { + switch v := v.(*EventArray); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_event_proto_msgTypes[1].Exporter = func(v any, i int) any { + switch v := v.(*LogArray); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_event_proto_msgTypes[2].Exporter = func(v any, i int) any { + switch v := v.(*MetricArray); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_event_proto_msgTypes[3].Exporter = func(v any, i int) any { + switch v := v.(*TraceArray); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_event_proto_msgTypes[4].Exporter = func(v any, i int) any { + switch v := v.(*EventWrapper); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_event_proto_msgTypes[5].Exporter = func(v any, i int) any { + switch v := v.(*Log); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_event_proto_msgTypes[6].Exporter = func(v any, i int) any { + switch v := v.(*Trace); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_event_proto_msgTypes[7].Exporter = func(v any, i int) any { + switch v := v.(*ValueMap); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_event_proto_msgTypes[8].Exporter = func(v any, i int) any { + switch v := v.(*ValueArray); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_event_proto_msgTypes[9].Exporter = func(v any, i int) any { + switch v := v.(*Value); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_event_proto_msgTypes[10].Exporter = func(v any, i int) any { + switch v := v.(*DatadogOriginMetadata); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_event_proto_msgTypes[11].Exporter = func(v any, i int) any { + switch v := v.(*Secrets); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_event_proto_msgTypes[12].Exporter = func(v any, i int) any { + switch v := v.(*OutputId); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_event_proto_msgTypes[13].Exporter = func(v any, i int) any { + switch v := v.(*Metadata); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_event_proto_msgTypes[14].Exporter = func(v any, i int) any { + switch v := v.(*Metric); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_event_proto_msgTypes[15].Exporter = func(v any, i int) any { + switch v := v.(*TagValues); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_event_proto_msgTypes[16].Exporter = func(v any, i int) any { + switch v := v.(*TagValue); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_event_proto_msgTypes[17].Exporter = func(v any, i int) any { + switch v := v.(*Counter); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_event_proto_msgTypes[18].Exporter = func(v any, i int) any { + switch v := v.(*Gauge); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_event_proto_msgTypes[19].Exporter = func(v any, i int) any { + switch v := v.(*Set); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_event_proto_msgTypes[20].Exporter = func(v any, i int) any { + switch v := v.(*Distribution1); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_event_proto_msgTypes[21].Exporter = func(v any, i int) any { + switch v := v.(*Distribution2); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_event_proto_msgTypes[22].Exporter = func(v any, i int) any { + switch v := v.(*DistributionSample); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_event_proto_msgTypes[23].Exporter = func(v any, i int) any { + switch v := v.(*AggregatedHistogram1); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_event_proto_msgTypes[24].Exporter = func(v any, i int) any { + switch v := v.(*AggregatedHistogram2); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_event_proto_msgTypes[25].Exporter = func(v any, i int) any { + switch v := v.(*AggregatedHistogram3); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_event_proto_msgTypes[26].Exporter = func(v any, i int) any { + switch v := v.(*HistogramBucket); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_event_proto_msgTypes[27].Exporter = func(v any, i int) any { + switch v := v.(*HistogramBucket3); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_event_proto_msgTypes[28].Exporter = func(v any, i int) any { + switch v := v.(*AggregatedSummary1); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_event_proto_msgTypes[29].Exporter = func(v any, i int) any { + switch v := v.(*AggregatedSummary2); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_event_proto_msgTypes[30].Exporter = func(v any, i int) any { + switch v := v.(*AggregatedSummary3); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_event_proto_msgTypes[31].Exporter = func(v any, i int) any { + switch v := v.(*SummaryQuantile); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_event_proto_msgTypes[32].Exporter = func(v any, i int) any { + switch v := v.(*Sketch); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_event_proto_msgTypes[39].Exporter = func(v any, i int) any { + switch v := v.(*Sketch_AgentDDSketch); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_event_proto_msgTypes[0].OneofWrappers = []any{ + (*EventArray_Logs)(nil), + (*EventArray_Metrics)(nil), + (*EventArray_Traces)(nil), + } + file_event_proto_msgTypes[4].OneofWrappers = []any{ + (*EventWrapper_Log)(nil), + (*EventWrapper_Metric)(nil), + (*EventWrapper_Trace)(nil), + } + file_event_proto_msgTypes[9].OneofWrappers = []any{ + (*Value_RawBytes)(nil), + (*Value_Timestamp)(nil), + (*Value_Integer)(nil), + (*Value_Float)(nil), + (*Value_Boolean)(nil), + (*Value_Map)(nil), + (*Value_Array)(nil), + (*Value_Null)(nil), + } + file_event_proto_msgTypes[10].OneofWrappers = []any{} + file_event_proto_msgTypes[12].OneofWrappers = []any{} + file_event_proto_msgTypes[13].OneofWrappers = []any{} + file_event_proto_msgTypes[14].OneofWrappers = []any{ + (*Metric_Counter)(nil), + (*Metric_Gauge)(nil), + (*Metric_Set)(nil), + (*Metric_Distribution1)(nil), + (*Metric_AggregatedHistogram1)(nil), + (*Metric_AggregatedSummary1)(nil), + (*Metric_Distribution2)(nil), + (*Metric_AggregatedHistogram2)(nil), + (*Metric_AggregatedSummary2)(nil), + (*Metric_Sketch)(nil), + (*Metric_AggregatedHistogram3)(nil), + (*Metric_AggregatedSummary3)(nil), + } + file_event_proto_msgTypes[16].OneofWrappers = []any{} + file_event_proto_msgTypes[32].OneofWrappers = []any{ + (*Sketch_AgentDdSketch)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_event_proto_rawDesc, + NumEnums: 3, + NumMessages: 40, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_event_proto_goTypes, + DependencyIndexes: file_event_proto_depIdxs, + EnumInfos: file_event_proto_enumTypes, + MessageInfos: file_event_proto_msgTypes, + }.Build() + File_event_proto = out.File + file_event_proto_rawDesc = nil + file_event_proto_goTypes = nil + file_event_proto_depIdxs = nil +} diff --git a/internal/vector/gen/vector.pb.go b/internal/vector/gen/vector.pb.go new file mode 100644 index 00000000..4c49c5b6 --- /dev/null +++ b/internal/vector/gen/vector.pb.go @@ -0,0 +1,391 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.34.2 +// protoc (unknown) +// source: vector.proto + +package gen + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type ServingStatus int32 + +const ( + ServingStatus_SERVING ServingStatus = 0 + ServingStatus_NOT_SERVING ServingStatus = 1 +) + +// Enum value maps for ServingStatus. +var ( + ServingStatus_name = map[int32]string{ + 0: "SERVING", + 1: "NOT_SERVING", + } + ServingStatus_value = map[string]int32{ + "SERVING": 0, + "NOT_SERVING": 1, + } +) + +func (x ServingStatus) Enum() *ServingStatus { + p := new(ServingStatus) + *p = x + return p +} + +func (x ServingStatus) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ServingStatus) Descriptor() protoreflect.EnumDescriptor { + return file_vector_proto_enumTypes[0].Descriptor() +} + +func (ServingStatus) Type() protoreflect.EnumType { + return &file_vector_proto_enumTypes[0] +} + +func (x ServingStatus) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use ServingStatus.Descriptor instead. +func (ServingStatus) EnumDescriptor() ([]byte, []int) { + return file_vector_proto_rawDescGZIP(), []int{0} +} + +type PushEventsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Events []*EventWrapper `protobuf:"bytes,1,rep,name=events,proto3" json:"events,omitempty"` +} + +func (x *PushEventsRequest) Reset() { + *x = PushEventsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_vector_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PushEventsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PushEventsRequest) ProtoMessage() {} + +func (x *PushEventsRequest) ProtoReflect() protoreflect.Message { + mi := &file_vector_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PushEventsRequest.ProtoReflect.Descriptor instead. +func (*PushEventsRequest) Descriptor() ([]byte, []int) { + return file_vector_proto_rawDescGZIP(), []int{0} +} + +func (x *PushEventsRequest) GetEvents() []*EventWrapper { + if x != nil { + return x.Events + } + return nil +} + +type PushEventsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *PushEventsResponse) Reset() { + *x = PushEventsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_vector_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PushEventsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PushEventsResponse) ProtoMessage() {} + +func (x *PushEventsResponse) ProtoReflect() protoreflect.Message { + mi := &file_vector_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PushEventsResponse.ProtoReflect.Descriptor instead. +func (*PushEventsResponse) Descriptor() ([]byte, []int) { + return file_vector_proto_rawDescGZIP(), []int{1} +} + +type HealthCheckRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *HealthCheckRequest) Reset() { + *x = HealthCheckRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_vector_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HealthCheckRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HealthCheckRequest) ProtoMessage() {} + +func (x *HealthCheckRequest) ProtoReflect() protoreflect.Message { + mi := &file_vector_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HealthCheckRequest.ProtoReflect.Descriptor instead. +func (*HealthCheckRequest) Descriptor() ([]byte, []int) { + return file_vector_proto_rawDescGZIP(), []int{2} +} + +type HealthCheckResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Status ServingStatus `protobuf:"varint,1,opt,name=status,proto3,enum=vector.ServingStatus" json:"status,omitempty"` +} + +func (x *HealthCheckResponse) Reset() { + *x = HealthCheckResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_vector_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HealthCheckResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HealthCheckResponse) ProtoMessage() {} + +func (x *HealthCheckResponse) ProtoReflect() protoreflect.Message { + mi := &file_vector_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HealthCheckResponse.ProtoReflect.Descriptor instead. +func (*HealthCheckResponse) Descriptor() ([]byte, []int) { + return file_vector_proto_rawDescGZIP(), []int{3} +} + +func (x *HealthCheckResponse) GetStatus() ServingStatus { + if x != nil { + return x.Status + } + return ServingStatus_SERVING +} + +var File_vector_proto protoreflect.FileDescriptor + +var file_vector_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x76, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, + 0x76, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x1a, 0x0b, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x22, 0x40, 0x0a, 0x11, 0x50, 0x75, 0x73, 0x68, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, + 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, + 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x52, 0x06, 0x65, + 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x14, 0x0a, 0x12, 0x50, 0x75, 0x73, 0x68, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x14, 0x0a, 0x12, 0x48, + 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x22, 0x44, 0x0a, 0x13, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x76, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2a, 0x2d, 0x0a, 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x45, 0x52, 0x56, + 0x49, 0x4e, 0x47, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x4e, 0x4f, 0x54, 0x5f, 0x53, 0x45, 0x52, + 0x56, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x32, 0x97, 0x01, 0x0a, 0x06, 0x56, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x12, 0x45, 0x0a, 0x0a, 0x50, 0x75, 0x73, 0x68, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, + 0x19, 0x2e, 0x76, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x76, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x46, 0x0a, 0x0b, 0x48, 0x65, 0x61, 0x6c, + 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x1a, 0x2e, 0x76, 0x65, 0x63, 0x74, 0x6f, 0x72, + 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x76, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x48, 0x65, 0x61, + 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x42, 0x89, 0x01, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x42, + 0x0b, 0x56, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x36, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6b, 0x61, 0x61, 0x73, 0x6f, + 0x70, 0x73, 0x2f, 0x76, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2d, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, + 0x6f, 0x72, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x76, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x2f, 0x67, 0x65, 0x6e, 0xa2, 0x02, 0x03, 0x56, 0x58, 0x58, 0xaa, 0x02, 0x06, 0x56, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0xca, 0x02, 0x06, 0x56, 0x65, 0x63, 0x74, 0x6f, 0x72, 0xe2, 0x02, + 0x12, 0x56, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0xea, 0x02, 0x06, 0x56, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_vector_proto_rawDescOnce sync.Once + file_vector_proto_rawDescData = file_vector_proto_rawDesc +) + +func file_vector_proto_rawDescGZIP() []byte { + file_vector_proto_rawDescOnce.Do(func() { + file_vector_proto_rawDescData = protoimpl.X.CompressGZIP(file_vector_proto_rawDescData) + }) + return file_vector_proto_rawDescData +} + +var file_vector_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_vector_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_vector_proto_goTypes = []any{ + (ServingStatus)(0), // 0: vector.ServingStatus + (*PushEventsRequest)(nil), // 1: vector.PushEventsRequest + (*PushEventsResponse)(nil), // 2: vector.PushEventsResponse + (*HealthCheckRequest)(nil), // 3: vector.HealthCheckRequest + (*HealthCheckResponse)(nil), // 4: vector.HealthCheckResponse + (*EventWrapper)(nil), // 5: event.EventWrapper +} +var file_vector_proto_depIdxs = []int32{ + 5, // 0: vector.PushEventsRequest.events:type_name -> event.EventWrapper + 0, // 1: vector.HealthCheckResponse.status:type_name -> vector.ServingStatus + 1, // 2: vector.Vector.PushEvents:input_type -> vector.PushEventsRequest + 3, // 3: vector.Vector.HealthCheck:input_type -> vector.HealthCheckRequest + 2, // 4: vector.Vector.PushEvents:output_type -> vector.PushEventsResponse + 4, // 5: vector.Vector.HealthCheck:output_type -> vector.HealthCheckResponse + 4, // [4:6] is the sub-list for method output_type + 2, // [2:4] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_vector_proto_init() } +func file_vector_proto_init() { + if File_vector_proto != nil { + return + } + file_event_proto_init() + if !protoimpl.UnsafeEnabled { + file_vector_proto_msgTypes[0].Exporter = func(v any, i int) any { + switch v := v.(*PushEventsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vector_proto_msgTypes[1].Exporter = func(v any, i int) any { + switch v := v.(*PushEventsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vector_proto_msgTypes[2].Exporter = func(v any, i int) any { + switch v := v.(*HealthCheckRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vector_proto_msgTypes[3].Exporter = func(v any, i int) any { + switch v := v.(*HealthCheckResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_vector_proto_rawDesc, + NumEnums: 1, + NumMessages: 4, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_vector_proto_goTypes, + DependencyIndexes: file_vector_proto_depIdxs, + EnumInfos: file_vector_proto_enumTypes, + MessageInfos: file_vector_proto_msgTypes, + }.Build() + File_vector_proto = out.File + file_vector_proto_rawDesc = nil + file_vector_proto_goTypes = nil + file_vector_proto_depIdxs = nil +} diff --git a/internal/vector/gen/vector_grpc.pb.go b/internal/vector/gen/vector_grpc.pb.go new file mode 100644 index 00000000..d0e4691f --- /dev/null +++ b/internal/vector/gen/vector_grpc.pb.go @@ -0,0 +1,159 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.5.1 +// - protoc (unknown) +// source: vector.proto + +package gen + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + Vector_PushEvents_FullMethodName = "/vector.Vector/PushEvents" + Vector_HealthCheck_FullMethodName = "/vector.Vector/HealthCheck" +) + +// VectorClient is the client API for Vector service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type VectorClient interface { + PushEvents(ctx context.Context, in *PushEventsRequest, opts ...grpc.CallOption) (*PushEventsResponse, error) + HealthCheck(ctx context.Context, in *HealthCheckRequest, opts ...grpc.CallOption) (*HealthCheckResponse, error) +} + +type vectorClient struct { + cc grpc.ClientConnInterface +} + +func NewVectorClient(cc grpc.ClientConnInterface) VectorClient { + return &vectorClient{cc} +} + +func (c *vectorClient) PushEvents(ctx context.Context, in *PushEventsRequest, opts ...grpc.CallOption) (*PushEventsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(PushEventsResponse) + err := c.cc.Invoke(ctx, Vector_PushEvents_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vectorClient) HealthCheck(ctx context.Context, in *HealthCheckRequest, opts ...grpc.CallOption) (*HealthCheckResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(HealthCheckResponse) + err := c.cc.Invoke(ctx, Vector_HealthCheck_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// VectorServer is the server API for Vector service. +// All implementations must embed UnimplementedVectorServer +// for forward compatibility. +type VectorServer interface { + PushEvents(context.Context, *PushEventsRequest) (*PushEventsResponse, error) + HealthCheck(context.Context, *HealthCheckRequest) (*HealthCheckResponse, error) + mustEmbedUnimplementedVectorServer() +} + +// UnimplementedVectorServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedVectorServer struct{} + +func (UnimplementedVectorServer) PushEvents(context.Context, *PushEventsRequest) (*PushEventsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method PushEvents not implemented") +} +func (UnimplementedVectorServer) HealthCheck(context.Context, *HealthCheckRequest) (*HealthCheckResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method HealthCheck not implemented") +} +func (UnimplementedVectorServer) mustEmbedUnimplementedVectorServer() {} +func (UnimplementedVectorServer) testEmbeddedByValue() {} + +// UnsafeVectorServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to VectorServer will +// result in compilation errors. +type UnsafeVectorServer interface { + mustEmbedUnimplementedVectorServer() +} + +func RegisterVectorServer(s grpc.ServiceRegistrar, srv VectorServer) { + // If the following call pancis, it indicates UnimplementedVectorServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&Vector_ServiceDesc, srv) +} + +func _Vector_PushEvents_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PushEventsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VectorServer).PushEvents(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Vector_PushEvents_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VectorServer).PushEvents(ctx, req.(*PushEventsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vector_HealthCheck_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(HealthCheckRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VectorServer).HealthCheck(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Vector_HealthCheck_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VectorServer).HealthCheck(ctx, req.(*HealthCheckRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Vector_ServiceDesc is the grpc.ServiceDesc for Vector service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Vector_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "vector.Vector", + HandlerType: (*VectorServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "PushEvents", + Handler: _Vector_PushEvents_Handler, + }, + { + MethodName: "HealthCheck", + Handler: _Vector_HealthCheck_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "vector.proto", +} diff --git a/proto/vector/event.proto b/proto/vector/event.proto new file mode 100644 index 00000000..72443a82 --- /dev/null +++ b/proto/vector/event.proto @@ -0,0 +1,240 @@ +syntax = "proto3"; +package event; + +import "google/protobuf/timestamp.proto"; + +message EventArray { + oneof events { + LogArray logs = 1; + MetricArray metrics = 2; + TraceArray traces = 3; + } +} + +message LogArray { + repeated Log logs = 1; +} + +message MetricArray { + repeated Metric metrics = 1; +} + +message TraceArray { + repeated Trace traces = 1; +} + +message EventWrapper { + oneof event { + Log log = 1; + Metric metric = 2; + Trace trace = 3; + } +} + +message Log { + // Deprecated, use value instead + map fields = 1; + Value value = 2; + Value metadata = 3 [deprecated = true]; + Metadata metadata_full = 4; +} + +message Trace { + map fields = 1; + Value metadata = 2 [deprecated = true]; + Metadata metadata_full = 3; +} + +message ValueMap { + map fields = 1; +} + +message ValueArray { + repeated Value items = 1; +} + +enum ValueNull { + NULL_VALUE = 0; +} + +message Value { + reserved 3; + oneof kind { + bytes raw_bytes = 1; + google.protobuf.Timestamp timestamp = 2; + int64 integer = 4; + double float = 5; + bool boolean = 6; + ValueMap map = 7; + ValueArray array = 8; + ValueNull null = 9; + } +} + +message DatadogOriginMetadata { + optional uint32 origin_product = 1; + optional uint32 origin_category = 2; + optional uint32 origin_service = 3; +} + +message Secrets { + map entries = 1; +} + +message OutputId { + string component = 1; + optional string port = 2; +} + +message Metadata { + Value value = 1; + DatadogOriginMetadata datadog_origin_metadata = 2; + optional string source_id = 3; + optional string source_type = 4; + OutputId upstream_id = 5; + Secrets secrets = 6; +} + +message Metric { + string name = 1; + google.protobuf.Timestamp timestamp = 2; + map tags_v1 = 3; + map tags_v2 = 20; + enum Kind { + Incremental = 0; + Absolute = 1; + } + Kind kind = 4; + oneof value { + Counter counter = 5; + Gauge gauge = 6; + Set set = 7; + Distribution1 distribution1 = 8; + AggregatedHistogram1 aggregated_histogram1 = 9; + AggregatedSummary1 aggregated_summary1 = 10; + Distribution2 distribution2 = 12; + AggregatedHistogram2 aggregated_histogram2 = 13; + AggregatedSummary2 aggregated_summary2 = 14; + Sketch sketch = 15; + AggregatedHistogram3 aggregated_histogram3 = 16; + AggregatedSummary3 aggregated_summary3 = 17; + } + string namespace = 11; + uint32 interval_ms = 18; + Value metadata = 19 [deprecated = true]; + Metadata metadata_full = 21; +} + +message TagValues { + repeated TagValue values = 1; +} + +message TagValue { + optional string value = 1; +} + +message Counter { + double value = 1; +} + +message Gauge { + double value = 1; +} + +message Set { + repeated string values = 1; +} + +enum StatisticKind { + Histogram = 0; + Summary = 1; +} + +message Distribution1 { + repeated double values = 1; + repeated uint32 sample_rates = 2; + StatisticKind statistic = 3; +} + +message Distribution2 { + repeated DistributionSample samples = 1; + StatisticKind statistic = 2; +} + +message DistributionSample { + double value = 1; + uint32 rate = 2; +} + +message AggregatedHistogram1 { + repeated double buckets = 1; + repeated uint32 counts = 2; + uint32 count = 3; + double sum = 4; +} + +message AggregatedHistogram2 { + repeated HistogramBucket buckets = 1; + uint32 count = 2; + double sum = 3; +} + +message AggregatedHistogram3 { + repeated HistogramBucket3 buckets = 1; + uint64 count = 2; + double sum = 3; +} + +message HistogramBucket { + double upper_limit = 1; + uint32 count = 2; +} + +message HistogramBucket3 { + double upper_limit = 1; + uint64 count = 2; +} + +message AggregatedSummary1 { + repeated double quantiles = 1; + repeated double values = 2; + uint32 count = 3; + double sum = 4; +} + +message AggregatedSummary2 { + repeated SummaryQuantile quantiles = 1; + uint32 count = 2; + double sum = 3; +} + +message AggregatedSummary3 { + repeated SummaryQuantile quantiles = 1; + uint64 count = 2; + double sum = 3; +} + +message SummaryQuantile { + double quantile = 1; + double value = 2; +} + +message Sketch { + message AgentDDSketch { + // Summary statistics for the samples in this sketch. + uint32 count = 1; + double min = 2; + double max = 3; + double sum = 4; + double avg = 5; + // The bins (buckets) of this sketch, where `k` and `n` are unzipped pairs. + // `k` is the list of bin indexes that are populated, and `n` is the count of samples + // within the given bin. + repeated sint32 k = 6; + repeated uint32 n = 7; + } + + oneof sketch { + AgentDDSketch agent_dd_sketch = 1; + } +} \ No newline at end of file diff --git a/proto/vector/vector.proto b/proto/vector/vector.proto new file mode 100644 index 00000000..861024f3 --- /dev/null +++ b/proto/vector/vector.proto @@ -0,0 +1,27 @@ +syntax = "proto3"; +package vector; + +import "event.proto"; + +message PushEventsRequest { + repeated event.EventWrapper events = 1; +} + +message PushEventsResponse {} + +enum ServingStatus { + SERVING = 0; + NOT_SERVING = 1; +} + +message HealthCheckRequest {} + +message HealthCheckResponse { + ServingStatus status = 1; +} + +service Vector { + rpc PushEvents(PushEventsRequest) returns (PushEventsResponse) {} + + rpc HealthCheck(HealthCheckRequest) returns (HealthCheckResponse); +} \ No newline at end of file From 5ccb479018347f139a574d139b6f418c0a3482a5 Mon Sep 17 00:00:00 2001 From: aa1ex <115736127+aa1ex@users.noreply.github.com> Date: Fri, 4 Oct 2024 14:57:49 +0300 Subject: [PATCH 272/316] cluster vector aggregator (#152) add cluster vector aggregator Signed-off-by: Aleksandr Aleksandrov --- PROJECT | 9 + api/v1alpha1/clustervectoraggregator_types.go | 61 + api/v1alpha1/vector_common_types.go | 8 + api/v1alpha1/vectoraggregator_types.go | 6 +- api/v1alpha1/zz_generated.deepcopy.go | 119 +- cmd/main.go | 35 +- ...y.kaasops.io_clustervectoraggregators.yaml | 4968 +++++++++++++++++ ...vability.kaasops.io_vectoraggregators.yaml | 2 +- config/crd/kustomization.yaml | 2 + .../clustervectoraggregator_editor_role.yaml | 27 + .../clustervectoraggregator_viewer_role.yaml | 23 + config/rbac/kustomization.yaml | 2 + config/rbac/role.yaml | 3 + config/samples/kustomization.yaml | 1 + ...lity_v1alpha1_clustervectoraggregator.yaml | 9 + docs/design.md | 43 +- docs/specification.md | 2 +- go.mod | 4 +- internal/config/aggregator.go | 23 +- .../clustervectoraggregator_controller.go | 223 + ...clustervectoraggregator_controller_test.go | 89 + internal/controller/pipeline_controller.go | 146 +- .../controller/pipeline_controller_test.go | 1 + internal/controller/suite_test.go | 13 + .../controller/vectoraggregator_controller.go | 19 +- .../vectoraggregator_controller_test.go | 2 + internal/k8sevents/event.go | 1 + internal/k8sevents/manager.go | 61 +- internal/k8sevents/watcher.go | 13 + internal/vector/aggregator/config.go | 8 +- internal/vector/aggregator/controller.go | 122 +- internal/vector/aggregator/deployment.go | 72 +- internal/vector/aggregator/podmonitor.go | 4 +- internal/vector/aggregator/rbac.go | 6 +- internal/vector/aggregator/service.go | 13 +- 35 files changed, 5927 insertions(+), 213 deletions(-) create mode 100644 api/v1alpha1/clustervectoraggregator_types.go create mode 100644 config/crd/bases/observability.kaasops.io_clustervectoraggregators.yaml create mode 100644 config/rbac/clustervectoraggregator_editor_role.yaml create mode 100644 config/rbac/clustervectoraggregator_viewer_role.yaml create mode 100644 config/samples/observability_v1alpha1_clustervectoraggregator.yaml create mode 100644 internal/controller/clustervectoraggregator_controller.go create mode 100644 internal/controller/clustervectoraggregator_controller_test.go diff --git a/PROJECT b/PROJECT index 6675ee1b..2d7277a5 100644 --- a/PROJECT +++ b/PROJECT @@ -43,4 +43,13 @@ resources: kind: VectorAggregator path: github.com/kaasops/vector-operator/api/v1alpha1 version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: kaasops.io + group: observability + kind: ClusterVectorAggregator + path: github.com/kaasops/vector-operator/api/v1alpha1 + version: v1alpha1 version: "3" diff --git a/api/v1alpha1/clustervectoraggregator_types.go b/api/v1alpha1/clustervectoraggregator_types.go new file mode 100644 index 00000000..bf194a6e --- /dev/null +++ b/api/v1alpha1/clustervectoraggregator_types.go @@ -0,0 +1,61 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// ClusterVectorAggregatorSpec defines the desired state of ClusterVectorAggregator +type ClusterVectorAggregatorSpec struct { + VectorAggregatorCommon `json:",inline"` + // ResourceNamespace specifies the namespace where the related resources, such as Deployments and Services, will be deployed. + ResourceNamespace string `json:"resourceNamespace,omitempty"` +} + +// ClusterVectorAggregatorStatus defines the observed state of ClusterVectorAggregator +type ClusterVectorAggregatorStatus struct { + VectorCommonStatus `json:",inline"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:scope=Cluster +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" +// +kubebuilder:printcolumn:name="Valid",type="boolean",JSONPath=".status.configCheckResult" + +// ClusterVectorAggregator is the Schema for the clustervectoraggregators API +type ClusterVectorAggregator struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ClusterVectorAggregatorSpec `json:"spec,omitempty"` + Status ClusterVectorAggregatorStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// ClusterVectorAggregatorList contains a list of ClusterVectorAggregator +type ClusterVectorAggregatorList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ClusterVectorAggregator `json:"items"` +} + +func init() { + SchemeBuilder.Register(&ClusterVectorAggregator{}, &ClusterVectorAggregatorList{}) +} diff --git a/api/v1alpha1/vector_common_types.go b/api/v1alpha1/vector_common_types.go index 3544340d..dd2a3d98 100644 --- a/api/v1alpha1/vector_common_types.go +++ b/api/v1alpha1/vector_common_types.go @@ -131,3 +131,11 @@ type ConfigCheck struct { type VectorSelectorSpec struct { MatchLabels map[string]string `json:"matchLabels,omitempty"` } + +type VectorAggregatorCommon struct { + VectorCommon `json:",inline"` + Replicas int32 `json:"replicas,omitempty"` + // Selector defines a filter for the Vector Pipeline and Cluster Vector Pipeline by labels. + // If not specified, all pipelines will be selected. + Selector *VectorSelectorSpec `json:"selector,omitempty"` +} diff --git a/api/v1alpha1/vectoraggregator_types.go b/api/v1alpha1/vectoraggregator_types.go index 66711d52..cd8cd68a 100644 --- a/api/v1alpha1/vectoraggregator_types.go +++ b/api/v1alpha1/vectoraggregator_types.go @@ -26,11 +26,7 @@ import ( // VectorAggregatorSpec defines the desired state of VectorAggregator type VectorAggregatorSpec struct { - VectorCommon `json:",inline"` - Replicas int32 `json:"replicas,omitempty"` - // Defines a filter for the Vector Pipeline and Cluster Vector Pipeline by labels. - // If not specified, all pipelines will be selected. - Selector *VectorSelectorSpec `json:"selector,omitempty"` + VectorAggregatorCommon `json:",inline"` } // VectorAggregatorStatus defines the observed state of VectorAggregator diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 591c6cdd..f09fd3f2 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -40,6 +40,97 @@ func (in *ApiSpec) DeepCopy() *ApiSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterVectorAggregator) DeepCopyInto(out *ClusterVectorAggregator) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterVectorAggregator. +func (in *ClusterVectorAggregator) DeepCopy() *ClusterVectorAggregator { + if in == nil { + return nil + } + out := new(ClusterVectorAggregator) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterVectorAggregator) 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 *ClusterVectorAggregatorList) DeepCopyInto(out *ClusterVectorAggregatorList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ClusterVectorAggregator, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterVectorAggregatorList. +func (in *ClusterVectorAggregatorList) DeepCopy() *ClusterVectorAggregatorList { + if in == nil { + return nil + } + out := new(ClusterVectorAggregatorList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterVectorAggregatorList) 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 *ClusterVectorAggregatorSpec) DeepCopyInto(out *ClusterVectorAggregatorSpec) { + *out = *in + in.VectorAggregatorCommon.DeepCopyInto(&out.VectorAggregatorCommon) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterVectorAggregatorSpec. +func (in *ClusterVectorAggregatorSpec) DeepCopy() *ClusterVectorAggregatorSpec { + if in == nil { + return nil + } + out := new(ClusterVectorAggregatorSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterVectorAggregatorStatus) DeepCopyInto(out *ClusterVectorAggregatorStatus) { + *out = *in + in.VectorCommonStatus.DeepCopyInto(&out.VectorCommonStatus) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterVectorAggregatorStatus. +func (in *ClusterVectorAggregatorStatus) DeepCopy() *ClusterVectorAggregatorStatus { + if in == nil { + return nil + } + out := new(ClusterVectorAggregatorStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterVectorPipeline) DeepCopyInto(out *ClusterVectorPipeline) { *out = *in @@ -217,6 +308,27 @@ func (in *VectorAggregator) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VectorAggregatorCommon) DeepCopyInto(out *VectorAggregatorCommon) { + *out = *in + in.VectorCommon.DeepCopyInto(&out.VectorCommon) + if in.Selector != nil { + in, out := &in.Selector, &out.Selector + *out = new(VectorSelectorSpec) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VectorAggregatorCommon. +func (in *VectorAggregatorCommon) DeepCopy() *VectorAggregatorCommon { + if in == nil { + return nil + } + out := new(VectorAggregatorCommon) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VectorAggregatorList) DeepCopyInto(out *VectorAggregatorList) { *out = *in @@ -252,12 +364,7 @@ func (in *VectorAggregatorList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VectorAggregatorSpec) DeepCopyInto(out *VectorAggregatorSpec) { *out = *in - in.VectorCommon.DeepCopyInto(&out.VectorCommon) - if in.Selector != nil { - in, out := &in.Selector, &out.Selector - *out = new(VectorSelectorSpec) - (*in).DeepCopyInto(*out) - } + in.VectorAggregatorCommon.DeepCopyInto(&out.VectorAggregatorCommon) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VectorAggregatorSpec. diff --git a/cmd/main.go b/cmd/main.go index 733d237f..eb015ee5 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -21,10 +21,11 @@ import ( "crypto/tls" "flag" "fmt" - "github.com/kaasops/vector-operator/internal/k8sevents" "os" "time" + "github.com/kaasops/vector-operator/internal/k8sevents" + monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -208,17 +209,20 @@ func main() { defer close(vectorAgentsPipelineEventCh) vectorAggregatorsPipelineEventCh := make(chan event.GenericEvent, 10) defer close(vectorAggregatorsPipelineEventCh) + clusterVectorAggregatorsPipelineEventCh := make(chan event.GenericEvent, 10) + defer close(clusterVectorAggregatorsPipelineEventCh) evCollector := k8sevents.NewEventsCollector(clientset, ctrl.Log.WithName("kubernetes-events-collector")) if err = (&controller.PipelineReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Clientset: clientset, - ConfigCheckTimeout: configCheckTimeout, - VectorAgentEventCh: vectorAgentsPipelineEventCh, - VectorAggregatorsEventCh: vectorAggregatorsPipelineEventCh, - EventsCollector: evCollector, + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Clientset: clientset, + ConfigCheckTimeout: configCheckTimeout, + VectorAgentEventCh: vectorAgentsPipelineEventCh, + VectorAggregatorsEventCh: vectorAggregatorsPipelineEventCh, + ClusterVectorAggregatorsEventCh: clusterVectorAggregatorsPipelineEventCh, + EventsCollector: evCollector, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "VectorPipeline") os.Exit(1) @@ -239,10 +243,25 @@ func main() { os.Exit(1) } + clusterVectorAggregatorsEventCh := make(chan event.GenericEvent) + defer close(clusterVectorAggregatorsEventCh) + + if err = (&controller.ClusterVectorAggregatorReconciler{ + Client: mgr.GetClient(), + Clientset: clientset, + Scheme: mgr.GetScheme(), + ConfigCheckTimeout: configCheckTimeout, + EventChan: clusterVectorAggregatorsEventCh, + EventsCollector: evCollector, + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "ClusterVectorAggregator") + os.Exit(1) + } // +kubebuilder:scaffold:builder go reconcileWithDelay(context.Background(), vectorAgentsPipelineEventCh, vectorAgentEventCh, time.Second*10) go reconcileWithDelay(context.Background(), vectorAggregatorsPipelineEventCh, vectorAggregatorsEventCh, time.Second*10) + go reconcileWithDelay(context.Background(), clusterVectorAggregatorsPipelineEventCh, clusterVectorAggregatorsEventCh, time.Second*10) if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { setupLog.Error(err, "unable to set up health check") diff --git a/config/crd/bases/observability.kaasops.io_clustervectoraggregators.yaml b/config/crd/bases/observability.kaasops.io_clustervectoraggregators.yaml new file mode 100644 index 00000000..49463c7b --- /dev/null +++ b/config/crd/bases/observability.kaasops.io_clustervectoraggregators.yaml @@ -0,0 +1,4968 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: clustervectoraggregators.observability.kaasops.io +spec: + group: observability.kaasops.io + names: + kind: ClusterVectorAggregator + listKind: ClusterVectorAggregatorList + plural: clustervectoraggregators + singular: clustervectoraggregator + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.configCheckResult + name: Valid + type: boolean + name: v1alpha1 + schema: + openAPIV3Schema: + description: ClusterVectorAggregator is the Schema for the clustervectoraggregators + API + 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: + type: object + spec: + description: ClusterVectorAggregatorSpec defines the desired state of + ClusterVectorAggregator + properties: + affinity: + description: Affinity If specified, the pod's scheduling constraints. + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for the + pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. + items: + description: |- + An empty preferred scheduling term matches all objects with implicit weight 0 + (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated with the + corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + description: Weight associated with matching the corresponding + nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. + The terms are ORed. + items: + description: |- + A null or empty node selector term matches no objects. The requirements of + them are ANDed. + The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate + this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules (e.g. + avoid putting this pod in the same node, zone, etc. as some + other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the anti-affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the anti-affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the anti-affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + annotations: + additionalProperties: + type: string + description: Annotations is an unstructured key value map stored with + a resource that may be set by external tools to store and retrieve + arbitrary metadata. + type: object + api: + description: |- + Vector API params. Allows to interact with a running Vector instance. + https://vector.dev/docs/reference/api/ + properties: + enabled: + type: boolean + healthcheck: + description: |- + Enable ReadinessProbe and LivenessProbe via api /health endpoint. + If probe enabled via VectorAgent or VectorAggregator, this setting will be ignored for that probe. + type: boolean + playground: + type: boolean + type: object + compressConfigFile: + description: 'Compress config file to fix: metadata.annotations: Too + long: must have at most 262144 characters' + type: boolean + configCheck: + description: Control params for ConfigCheck pods + properties: + affinity: + description: Affinity If specified, the pod's scheduling constraints. + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for + the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. + items: + description: |- + An empty preferred scheduling term matches all objects with implicit weight 0 + (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated with + the corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + description: Weight associated with matching the + corresponding nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. + The terms are ORed. + items: + description: |- + A null or empty node selector term matches no objects. The requirements of + them are ANDed. + The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. + co-locate this pod in the same node, zone, etc. as some + other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules + (e.g. avoid putting this pod in the same node, zone, etc. + as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the anti-affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the anti-affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the anti-affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + annotations: + additionalProperties: + type: string + description: Annotations is an unstructured key value map stored + with a resource that may be set by external tools to store and + retrieve arbitrary metadata. + type: object + disabled: + type: boolean + image: + description: |- + Image - docker image settings for Vector Agent + if no specified operator uses default config version + type: string + resources: + description: |- + Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + if not specified - default setting will be used + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + tolerations: + description: Tolerations If specified, the pod's tolerations. + items: + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . + properties: + effect: + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + type: object + configReloaderImage: + type: string + configReloaderResources: + description: ResourceRequirements describes the compute resource requirements. + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + containerSecurityContext: + description: |- + SecurityContext holds security configuration that will be applied to a container. + Some fields are present in both SecurityContext and PodSecurityContext. + When both are set, the values in SecurityContext take precedence. + properties: + allowPrivilegeEscalation: + description: |- + AllowPrivilegeEscalation controls whether a process can gain more + privileges than its parent process. This bool directly controls if + the no_new_privs flag will be set on the container process. + AllowPrivilegeEscalation is true always when the container is: + 1) run as Privileged + 2) has CAP_SYS_ADMIN + Note that this field cannot be set when spec.os.name is windows. + type: boolean + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by this container. If set, this profile + overrides the pod's appArmorProfile. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object + capabilities: + description: |- + The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container runtime. + Note that this field cannot be set when spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities type + type: string + type: array + x-kubernetes-list-type: atomic + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities type + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + description: |- + Run container in privileged mode. + Processes in privileged containers are essentially equivalent to root on the host. + Defaults to false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: |- + procMount denotes the type of proc mount to use for the containers. + The default value is Default which uses the container runtime defaults for + readonly paths and masked paths. + This requires the ProcMountType feature flag to be enabled. + Note that this field cannot be set when spec.os.name is windows. + type: string + readOnlyRootFilesystem: + description: |- + Whether this container has a read-only root filesystem. + Default is false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: |- + The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies to + the container. + type: string + role: + description: Role is a SELinux role label that applies to + the container. + type: string + type: + description: Type is a SELinux type label that applies to + the container. + type: string + user: + description: User is a SELinux user label that applies to + the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by this container. If seccomp options are + provided at both the pod & container level, the container options + override the pod options. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + windowsOptions: + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options from the PodSecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the GMSA + credential spec to use. + type: string + hostProcess: + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + dataDir: + description: |- + The directory used for persisting Vector state, such as on-disk buffers, file checkpoints, and more. Please make sure the Vector project has write permissions to this directory. + https://vector.dev/docs/reference/configuration/global-options/#data_dir + type: string + env: + description: Env that will be added to Vector pod + items: + description: EnvVar represents an environment variable present in + a Container. + properties: + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + type: string + value: + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". + type: string + valueFrom: + description: Source for the environment variable's value. Cannot + be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. + properties: + apiVersion: + description: Version of the schema the FieldPath is + written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified + API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the exposed + resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + host_aliases: + description: |- + HostAliases provides mapping between ip and hostnames, + that would be propagated to pod, + cannot be used with HostNetwork. + items: + description: |- + HostAlias holds the mapping between IP and hostnames that will be injected as an entry in the + pod's hosts file. + properties: + hostnames: + description: Hostnames for the above IP address. + items: + type: string + type: array + x-kubernetes-list-type: atomic + ip: + description: IP address of the host file entry. + type: string + required: + - ip + type: object + type: array + hostNetwork: + description: HostNetwork controls whether the pod may use the node + network namespace + type: boolean + image: + description: |- + Image - docker image settings for Vector Agent + if no specified operator uses default config version + type: string + imagePullPolicy: + description: ImagePullPolicy of pods + type: string + imagePullSecrets: + description: |- + ImagePullSecrets An optional list of references to secrets in the same namespace + to use for pulling images from registries + see http://kubernetes.io/docs/user-guide/images#specifying-imagepullsecrets-on-a-pod + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + type: array + internalMetrics: + description: Enable internal metrics exporter + type: boolean + livenessProbe: + description: Periodic probe of container liveness. Container will + be restarted if the probe fails. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + properties: + port: + description: Port number of the gRPC service. Number must + be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows + repeated headers. + items: + description: HTTPHeader describes a custom header to be + used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + podSecurityContext: + description: |- + SecurityContext holds pod-level security attributes and common container settings. + This defaults to the default PodSecurityContext. + properties: + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by the containers in this pod. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object + fsGroup: + description: |- + A special supplemental group that applies to all containers in a pod. + Some volume types allow the Kubelet to change the ownership of that volume + to be owned by the pod: + + 1. The owning GID will be the FSGroup + 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) + 3. The permission bits are OR'd with rw-rw---- + + If unset, the Kubelet will not modify the ownership and permissions of any volume. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + fsGroupChangePolicy: + description: |- + fsGroupChangePolicy defines behavior of changing ownership and permission of the volume + before being exposed inside Pod. This field will only apply to + volume types which support fsGroup based ownership(and permissions). + It will have no effect on ephemeral volume types such as: secret, configmaps + and emptydir. + Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used. + Note that this field cannot be set when spec.os.name is windows. + type: string + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: |- + The SELinux context to be applied to all containers. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in SecurityContext. If set in + both SecurityContext and PodSecurityContext, the value specified in SecurityContext + takes precedence for that container. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies to + the container. + type: string + role: + description: Role is a SELinux role label that applies to + the container. + type: string + type: + description: Type is a SELinux type label that applies to + the container. + type: string + user: + description: User is a SELinux user label that applies to + the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by the containers in this pod. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + supplementalGroups: + description: |- + A list of groups applied to the first process run in each container, in + addition to the container's primary GID and fsGroup (if specified). If + the SupplementalGroupsPolicy feature is enabled, the + supplementalGroupsPolicy field determines whether these are in addition + to or instead of any group memberships defined in the container image. + If unspecified, no additional groups are added, though group memberships + defined in the container image may still be used, depending on the + supplementalGroupsPolicy field. + Note that this field cannot be set when spec.os.name is windows. + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + description: |- + Defines how supplemental groups of the first container processes are calculated. + Valid values are "Merge" and "Strict". If not specified, "Merge" is used. + (Alpha) Using the field requires the SupplementalGroupsPolicy feature gate to be enabled + and the container runtime must implement support for this feature. + Note that this field cannot be set when spec.os.name is windows. + type: string + sysctls: + description: |- + Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported + sysctls (by the container runtime) might fail to launch. + Note that this field cannot be set when spec.os.name is windows. + items: + description: Sysctl defines a kernel parameter to be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options within a container's SecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the GMSA + credential spec to use. + type: string + hostProcess: + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + podSecurityPolicyName: + description: |- + PodSecurityPolicyName - defines name for podSecurityPolicy + in case of empty value, prefixedName will be used. + type: string + priorityClassName: + description: PriorityClassName assigned to the Pods + type: string + readinessProbe: + description: Periodic probe of container service readiness. Container + will be removed from service endpoints if the probe fails. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + properties: + port: + description: Port number of the gRPC service. Number must + be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows + repeated headers. + items: + description: HTTPHeader describes a custom header to be + used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + replicas: + format: int32 + type: integer + resourceNamespace: + description: ResourceNamespace specifies the namespace where the related + resources, such as Deployments and Services, will be deployed. + type: string + resources: + description: |- + Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + if not specified - default setting will be used + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + runtimeClassName: + description: |- + RuntimeClassName - defines runtime class for kubernetes pod. + https://kubernetes.io/docs/concepts/containers/runtime-class/ + type: string + schedulerName: + description: SchedulerName - defines kubernetes scheduler name + type: string + selector: + description: |- + Selector defines a filter for the Vector Pipeline and Cluster Vector Pipeline by labels. + If not specified, all pipelines will be selected. + properties: + matchLabels: + additionalProperties: + type: string + type: object + type: object + tolerations: + description: Tolerations If specified, the pod's tolerations. + items: + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . + properties: + effect: + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + volumeMounts: + description: Pod volumes to mount into the container's filesystem. + items: + description: VolumeMount describes a mounting of a Volume within + a container. + properties: + mountPath: + description: |- + Path within the container at which the volume should be mounted. Must + not contain ':'. + type: string + mountPropagation: + description: |- + mountPropagation determines how mounts are propagated from the host + to container and the other way around. + When not set, MountPropagationNone is used. + This field is beta in 1.10. + When RecursiveReadOnly is set to IfPossible or to Enabled, MountPropagation must be None or unspecified + (which defaults to None). + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: |- + Mounted read-only if true, read-write otherwise (false or unspecified). + Defaults to false. + type: boolean + recursiveReadOnly: + description: |- + RecursiveReadOnly specifies whether read-only mounts should be handled + recursively. + + If ReadOnly is false, this field has no meaning and must be unspecified. + + If ReadOnly is true, and this field is set to Disabled, the mount is not made + recursively read-only. If this field is set to IfPossible, the mount is made + recursively read-only, if it is supported by the container runtime. If this + field is set to Enabled, the mount is made recursively read-only if it is + supported by the container runtime, otherwise the pod will not be started and + an error will be generated to indicate the reason. + + If this field is set to IfPossible or Enabled, MountPropagation must be set to + None (or be unspecified, which defaults to None). + + If this field is not specified, it is treated as an equivalent of Disabled. + type: string + subPath: + description: |- + Path within the volume from which the container's volume should be mounted. + Defaults to "" (volume's root). + type: string + subPathExpr: + description: |- + Expanded path within the volume from which the container's volume should be mounted. + Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. + Defaults to "" (volume's root). + SubPathExpr and SubPath are mutually exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + volumes: + description: List of volumes that can be mounted by containers belonging + to the pod. + items: + description: Volume represents a named volume in a pod that may + be accessed by any container in the pod. + properties: + awsElasticBlockStore: + description: |- + awsElasticBlockStore represents an AWS Disk resource that is attached to a + kubelet's host machine and then exposed to the pod. + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + properties: + fsType: + description: |- + fsType is the filesystem type of the volume that you want to mount. + Tip: Ensure that the filesystem type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + type: string + partition: + description: |- + partition is the partition in the volume that you want to mount. + If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition as "1". + Similarly, the volume partition for /dev/sda is "0" (or you can leave the property empty). + format: int32 + type: integer + readOnly: + description: |- + readOnly value true will force the readOnly setting in VolumeMounts. + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + type: boolean + volumeID: + description: |- + volumeID is unique ID of the persistent disk resource in AWS (Amazon EBS volume). + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + type: string + required: + - volumeID + type: object + azureDisk: + description: azureDisk represents an Azure Data Disk mount on + the host and bind mount to the pod. + properties: + cachingMode: + description: 'cachingMode is the Host Caching mode: None, + Read Only, Read Write.' + type: string + diskName: + description: diskName is the Name of the data disk in the + blob storage + type: string + diskURI: + description: diskURI is the URI of data disk in the blob + storage + type: string + fsType: + default: ext4 + description: |- + fsType is Filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + kind: + description: 'kind expected values are Shared: multiple + blob disks per storage account Dedicated: single blob + disk per storage account Managed: azure managed data + disk (only in managed availability set). defaults to shared' + type: string + readOnly: + default: false + description: |- + readOnly Defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + description: azureFile represents an Azure File Service mount + on the host and bind mount to the pod. + properties: + readOnly: + description: |- + readOnly defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + secretName: + description: secretName is the name of secret that contains + Azure Storage Account Name and Key + type: string + shareName: + description: shareName is the azure share Name + type: string + required: + - secretName + - shareName + type: object + cephfs: + description: cephFS represents a Ceph FS mount on the host that + shares a pod's lifetime + properties: + monitors: + description: |- + monitors is Required: Monitors is a collection of Ceph monitors + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it + items: + type: string + type: array + x-kubernetes-list-type: atomic + path: + description: 'path is Optional: Used as the mounted root, + rather than the full Ceph tree, default is /' + type: string + readOnly: + description: |- + readOnly is Optional: Defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it + type: boolean + secretFile: + description: |- + secretFile is Optional: SecretFile is the path to key ring for User, default is /etc/ceph/user.secret + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it + type: string + secretRef: + description: |- + secretRef is Optional: SecretRef is reference to the authentication secret for User, default is empty. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + user: + description: |- + user is optional: User is the rados user name, default is admin + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it + type: string + required: + - monitors + type: object + cinder: + description: |- + cinder represents a cinder volume attached and mounted on kubelets host machine. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md + properties: + fsType: + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md + type: string + readOnly: + description: |- + readOnly defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md + type: boolean + secretRef: + description: |- + secretRef is optional: points to a secret object containing parameters used to connect + to OpenStack. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + description: |- + volumeID used to identify the volume in cinder. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md + type: string + required: + - volumeID + type: object + configMap: + description: configMap represents a configMap that should populate + this volume + properties: + defaultMode: + description: |- + defaultMode is optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + ConfigMap will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: optional specify whether the ConfigMap or its + keys must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + description: csi (Container Storage Interface) represents ephemeral + storage that is handled by certain external CSI drivers (Beta + feature). + properties: + driver: + description: |- + driver is the name of the CSI driver that handles this volume. + Consult with your admin for the correct name as registered in the cluster. + type: string + fsType: + description: |- + fsType to mount. Ex. "ext4", "xfs", "ntfs". + If not provided, the empty value is passed to the associated CSI driver + which will determine the default filesystem to apply. + type: string + nodePublishSecretRef: + description: |- + nodePublishSecretRef is a reference to the secret object containing + sensitive information to pass to the CSI driver to complete the CSI + NodePublishVolume and NodeUnpublishVolume calls. + This field is optional, and may be empty if no secret is required. If the + secret object contains more than one secret, all secret references are passed. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + description: |- + readOnly specifies a read-only configuration for the volume. + Defaults to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: + type: string + description: |- + volumeAttributes stores driver-specific properties that are passed to the CSI + driver. Consult your driver's documentation for supported values. + type: object + required: + - driver + type: object + downwardAPI: + description: downwardAPI represents downward API about the pod + that should populate this volume + properties: + defaultMode: + description: |- + Optional: mode bits to use on created files by default. Must be a + Optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + items: + description: Items is a list of downward API volume file + items: + description: DownwardAPIVolumeFile represents information + to create the file containing the pod field + properties: + fieldRef: + description: 'Required: Selects a field of the pod: + only annotations, labels, name, namespace and uid + are supported.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + description: |- + Optional: mode bits used to set permissions on this file, must be an octal value + between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: 'Required: Path is the relative path + name of the file to be created. Must not be absolute + or contain the ''..'' path. Must be utf-8 encoded. + The first item of the relative path must not start + with ''..''' + type: string + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + emptyDir: + description: |- + emptyDir represents a temporary directory that shares a pod's lifetime. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir + properties: + medium: + description: |- + medium represents what type of storage medium should back this directory. + The default is "" which means to use the node's default medium. + Must be an empty string (default) or Memory. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + description: |- + sizeLimit is the total amount of local storage required for this EmptyDir volume. + The size limit is also applicable for memory medium. + The maximum usage on memory medium EmptyDir would be the minimum value between + the SizeLimit specified here and the sum of memory limits of all containers in a pod. + The default is nil which means that the limit is undefined. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + description: |- + ephemeral represents a volume that is handled by a cluster storage driver. + The volume's lifecycle is tied to the pod that defines it - it will be created before the pod starts, + and deleted when the pod is removed. + + Use this if: + a) the volume is only needed while the pod runs, + b) features of normal volumes like restoring from snapshot or capacity + tracking are needed, + c) the storage driver is specified through a storage class, and + d) the storage driver supports dynamic volume provisioning through + a PersistentVolumeClaim (see EphemeralVolumeSource for more + information on the connection between this volume type + and PersistentVolumeClaim). + + Use PersistentVolumeClaim or one of the vendor-specific + APIs for volumes that persist for longer than the lifecycle + of an individual pod. + + Use CSI for light-weight local ephemeral volumes if the CSI driver is meant to + be used that way - see the documentation of the driver for + more information. + + A pod can use both types of ephemeral volumes and + persistent volumes at the same time. + properties: + volumeClaimTemplate: + description: |- + Will be used to create a stand-alone PVC to provision the volume. + The pod in which this EphemeralVolumeSource is embedded will be the + owner of the PVC, i.e. the PVC will be deleted together with the + pod. The name of the PVC will be `-` where + `` is the name from the `PodSpec.Volumes` array + entry. Pod validation will reject the pod if the concatenated name + is not valid for a PVC (for example, too long). + + An existing PVC with that name that is not owned by the pod + will *not* be used for the pod to avoid using an unrelated + volume by mistake. Starting the pod is then blocked until + the unrelated PVC is removed. If such a pre-created PVC is + meant to be used by the pod, the PVC has to updated with an + owner reference to the pod once the pod exists. Normally + this should not be necessary, but it may be useful when + manually reconstructing a broken cluster. + + This field is read-only and no changes will be made by Kubernetes + to the PVC after it has been created. + + Required, must not be nil. + properties: + metadata: + description: |- + May contain labels and annotations that will be copied into the PVC + when creating it. No other fields are allowed and will be rejected during + validation. + type: object + spec: + description: |- + The specification for the PersistentVolumeClaim. The entire content is + copied unchanged into the PVC that gets created from this + template. The same fields as in a PersistentVolumeClaim + are also valid here. + properties: + accessModes: + description: |- + accessModes contains the desired access modes the volume should have. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1 + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + description: |- + dataSource field can be used to specify either: + * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) + * An existing PVC (PersistentVolumeClaim) + If the provisioner or an external controller can support the specified data source, + it will create a new volume based on the contents of the specified data source. + When the AnyVolumeDataSource feature gate is enabled, dataSource contents will be copied to dataSourceRef, + and dataSourceRef contents will be copied to dataSource when dataSourceRef.namespace is not specified. + If the namespace is specified, then dataSourceRef will not be copied to dataSource. + properties: + apiGroup: + description: |- + APIGroup is the group for the resource being referenced. + If APIGroup is not specified, the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource being + referenced + type: string + name: + description: Name is the name of resource being + referenced + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + description: |- + dataSourceRef specifies the object from which to populate the volume with data, if a non-empty + volume is desired. This may be any object from a non-empty API group (non + core object) or a PersistentVolumeClaim object. + When this field is specified, volume binding will only succeed if the type of + the specified object matches some installed volume populator or dynamic + provisioner. + This field will replace the functionality of the dataSource field and as such + if both fields are non-empty, they must have the same value. For backwards + compatibility, when namespace isn't specified in dataSourceRef, + both fields (dataSource and dataSourceRef) will be set to the same + value automatically if one of them is empty and the other is non-empty. + When namespace is specified in dataSourceRef, + dataSource isn't set to the same value and must be empty. + There are three important differences between dataSource and dataSourceRef: + * While dataSource only allows two specific types of objects, dataSourceRef + allows any non-core object, as well as PersistentVolumeClaim objects. + * While dataSource ignores disallowed values (dropping them), dataSourceRef + preserves all values, and generates an error if a disallowed value is + specified. + * While dataSource only allows local objects, dataSourceRef allows objects + in any namespaces. + (Beta) Using this field requires the AnyVolumeDataSource feature gate to be enabled. + (Alpha) Using the namespace field of dataSourceRef requires the CrossNamespaceVolumeDataSource feature gate to be enabled. + properties: + apiGroup: + description: |- + APIGroup is the group for the resource being referenced. + If APIGroup is not specified, the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource being + referenced + type: string + name: + description: Name is the name of resource being + referenced + type: string + namespace: + description: |- + Namespace is the namespace of resource being referenced + Note that when a namespace is specified, a gateway.networking.k8s.io/ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. + (Alpha) This field requires the CrossNamespaceVolumeDataSource feature gate to be enabled. + type: string + required: + - kind + - name + type: object + resources: + description: |- + resources represents the minimum resources the volume should have. + If RecoverVolumeExpansionFailure feature is enabled users are allowed to specify resource requirements + that are lower than previous value but must still be higher than capacity recorded in the + status field of the claim. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + selector: + description: selector is a label query over volumes + to consider for binding. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + description: |- + storageClassName is the name of the StorageClass required by the claim. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1 + type: string + volumeAttributesClassName: + description: |- + volumeAttributesClassName may be used to set the VolumeAttributesClass used by this claim. + If specified, the CSI driver will create or update the volume with the attributes defined + in the corresponding VolumeAttributesClass. This has a different purpose than storageClassName, + it can be changed after the claim is created. An empty string value means that no VolumeAttributesClass + will be applied to the claim but it's not allowed to reset this field to empty string once it is set. + If unspecified and the PersistentVolumeClaim is unbound, the default VolumeAttributesClass + will be set by the persistentvolume controller if it exists. + If the resource referred to by volumeAttributesClass does not exist, this PersistentVolumeClaim will be + set to a Pending state, as reflected by the modifyVolumeStatus field, until such as a resource + exists. + More info: https://kubernetes.io/docs/concepts/storage/volume-attributes-classes/ + (Beta) Using this field requires the VolumeAttributesClass feature gate to be enabled (off by default). + type: string + volumeMode: + description: |- + volumeMode defines what type of volume is required by the claim. + Value of Filesystem is implied when not included in claim spec. + type: string + volumeName: + description: volumeName is the binding reference + to the PersistentVolume backing this claim. + type: string + type: object + required: + - spec + type: object + type: object + fc: + description: fc represents a Fibre Channel resource that is + attached to a kubelet's host machine and then exposed to the + pod. + properties: + fsType: + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + lun: + description: 'lun is Optional: FC target lun number' + format: int32 + type: integer + readOnly: + description: |- + readOnly is Optional: Defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + targetWWNs: + description: 'targetWWNs is Optional: FC target worldwide + names (WWNs)' + items: + type: string + type: array + x-kubernetes-list-type: atomic + wwids: + description: |- + wwids Optional: FC volume world wide identifiers (wwids) + Either wwids or combination of targetWWNs and lun must be set, but not both simultaneously. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + flexVolume: + description: |- + flexVolume represents a generic volume resource that is + provisioned/attached using an exec based plugin. + properties: + driver: + description: driver is the name of the driver to use for + this volume. + type: string + fsType: + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". The default filesystem depends on FlexVolume script. + type: string + options: + additionalProperties: + type: string + description: 'options is Optional: this field holds extra + command options if any.' + type: object + readOnly: + description: |- + readOnly is Optional: defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: |- + secretRef is Optional: secretRef is reference to the secret object containing + sensitive information to pass to the plugin scripts. This may be + empty if no secret object is specified. If the secret object + contains more than one secret, all secrets are passed to the plugin + scripts. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + description: flocker represents a Flocker volume attached to + a kubelet's host machine. This depends on the Flocker control + service being running + properties: + datasetName: + description: |- + datasetName is Name of the dataset stored as metadata -> name on the dataset for Flocker + should be considered as deprecated + type: string + datasetUUID: + description: datasetUUID is the UUID of the dataset. This + is unique identifier of a Flocker dataset + type: string + type: object + gcePersistentDisk: + description: |- + gcePersistentDisk represents a GCE Disk resource that is attached to a + kubelet's host machine and then exposed to the pod. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + properties: + fsType: + description: |- + fsType is filesystem type of the volume that you want to mount. + Tip: Ensure that the filesystem type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + type: string + partition: + description: |- + partition is the partition in the volume that you want to mount. + If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition as "1". + Similarly, the volume partition for /dev/sda is "0" (or you can leave the property empty). + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + format: int32 + type: integer + pdName: + description: |- + pdName is unique name of the PD resource in GCE. Used to identify the disk in GCE. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + type: string + readOnly: + description: |- + readOnly here will force the ReadOnly setting in VolumeMounts. + Defaults to false. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + type: boolean + required: + - pdName + type: object + gitRepo: + description: |- + gitRepo represents a git repository at a particular revision. + DEPRECATED: GitRepo is deprecated. To provision a container with a git repo, mount an + EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir + into the Pod's container. + properties: + directory: + description: |- + directory is the target directory name. + Must not contain or start with '..'. If '.' is supplied, the volume directory will be the + git repository. Otherwise, if specified, the volume will contain the git repository in + the subdirectory with the given name. + type: string + repository: + description: repository is the URL + type: string + revision: + description: revision is the commit hash for the specified + revision. + type: string + required: + - repository + type: object + glusterfs: + description: |- + glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. + More info: https://examples.k8s.io/volumes/glusterfs/README.md + properties: + endpoints: + description: |- + endpoints is the endpoint name that details Glusterfs topology. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod + type: string + path: + description: |- + path is the Glusterfs volume path. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod + type: string + readOnly: + description: |- + readOnly here will force the Glusterfs volume to be mounted with read-only permissions. + Defaults to false. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod + type: boolean + required: + - endpoints + - path + type: object + hostPath: + description: |- + hostPath represents a pre-existing file or directory on the host + machine that is directly exposed to the container. This is generally + used for system agents or other privileged things that are allowed + to see the host machine. Most containers will NOT need this. + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + properties: + path: + description: |- + path of the directory on the host. + If the path is a symlink, it will follow the link to the real path. + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + type: string + type: + description: |- + type for HostPath Volume + Defaults to "" + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + type: string + required: + - path + type: object + image: + description: |- + image represents an OCI object (a container image or artifact) pulled and mounted on the kubelet's host machine. + The volume is resolved at pod startup depending on which PullPolicy value is provided: + + - Always: the kubelet always attempts to pull the reference. Container creation will fail If the pull fails. + - Never: the kubelet never pulls the reference and only uses a local image or artifact. Container creation will fail if the reference isn't present. + - IfNotPresent: the kubelet pulls if the reference isn't already present on disk. Container creation will fail if the reference isn't present and the pull fails. + + The volume gets re-resolved if the pod gets deleted and recreated, which means that new remote content will become available on pod recreation. + A failure to resolve or pull the image during pod startup will block containers from starting and may add significant latency. Failures will be retried using normal volume backoff and will be reported on the pod reason and message. + The types of objects that may be mounted by this volume are defined by the container runtime implementation on a host machine and at minimum must include all valid types supported by the container image field. + The OCI object gets mounted in a single directory (spec.containers[*].volumeMounts.mountPath) by merging the manifest layers in the same way as for container images. + The volume will be mounted read-only (ro) and non-executable files (noexec). + Sub path mounts for containers are not supported (spec.containers[*].volumeMounts.subpath). + The field spec.securityContext.fsGroupChangePolicy has no effect on this volume type. + properties: + pullPolicy: + description: |- + Policy for pulling OCI objects. Possible values are: + Always: the kubelet always attempts to pull the reference. Container creation will fail If the pull fails. + Never: the kubelet never pulls the reference and only uses a local image or artifact. Container creation will fail if the reference isn't present. + IfNotPresent: the kubelet pulls if the reference isn't already present on disk. Container creation will fail if the reference isn't present and the pull fails. + Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. + type: string + reference: + description: |- + Required: Image or artifact reference to be used. + Behaves in the same way as pod.spec.containers[*].image. + Pull secrets will be assembled in the same way as for the container image by looking up node credentials, SA image pull secrets, and pod spec image pull secrets. + More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config management to default or override + container images in workload controllers like Deployments and StatefulSets. + type: string + type: object + iscsi: + description: |- + iscsi represents an ISCSI Disk resource that is attached to a + kubelet's host machine and then exposed to the pod. + More info: https://examples.k8s.io/volumes/iscsi/README.md + properties: + chapAuthDiscovery: + description: chapAuthDiscovery defines whether support iSCSI + Discovery CHAP authentication + type: boolean + chapAuthSession: + description: chapAuthSession defines whether support iSCSI + Session CHAP authentication + type: boolean + fsType: + description: |- + fsType is the filesystem type of the volume that you want to mount. + Tip: Ensure that the filesystem type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi + type: string + initiatorName: + description: |- + initiatorName is the custom iSCSI Initiator Name. + If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface + : will be created for the connection. + type: string + iqn: + description: iqn is the target iSCSI Qualified Name. + type: string + iscsiInterface: + default: default + description: |- + iscsiInterface is the interface Name that uses an iSCSI transport. + Defaults to 'default' (tcp). + type: string + lun: + description: lun represents iSCSI Target Lun number. + format: int32 + type: integer + portals: + description: |- + portals is the iSCSI Target Portal List. The portal is either an IP or ip_addr:port if the port + is other than default (typically TCP ports 860 and 3260). + items: + type: string + type: array + x-kubernetes-list-type: atomic + readOnly: + description: |- + readOnly here will force the ReadOnly setting in VolumeMounts. + Defaults to false. + type: boolean + secretRef: + description: secretRef is the CHAP Secret for iSCSI target + and initiator authentication + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + description: |- + targetPortal is iSCSI Target Portal. The Portal is either an IP or ip_addr:port if the port + is other than default (typically TCP ports 860 and 3260). + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + description: |- + name of the volume. + Must be a DNS_LABEL and unique within the pod. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + nfs: + description: |- + nfs represents an NFS mount on the host that shares a pod's lifetime + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs + properties: + path: + description: |- + path that is exported by the NFS server. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs + type: string + readOnly: + description: |- + readOnly here will force the NFS export to be mounted with read-only permissions. + Defaults to false. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs + type: boolean + server: + description: |- + server is the hostname or IP address of the NFS server. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + description: |- + persistentVolumeClaimVolumeSource represents a reference to a + PersistentVolumeClaim in the same namespace. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims + properties: + claimName: + description: |- + claimName is the name of a PersistentVolumeClaim in the same namespace as the pod using this volume. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims + type: string + readOnly: + description: |- + readOnly Will force the ReadOnly setting in VolumeMounts. + Default false. + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + description: photonPersistentDisk represents a PhotonController + persistent disk attached and mounted on kubelets host machine + properties: + fsType: + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + pdID: + description: pdID is the ID that identifies Photon Controller + persistent disk + type: string + required: + - pdID + type: object + portworxVolume: + description: portworxVolume represents a portworx volume attached + and mounted on kubelets host machine + properties: + fsType: + description: |- + fSType represents the filesystem type to mount + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs". Implicitly inferred to be "ext4" if unspecified. + type: string + readOnly: + description: |- + readOnly defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + volumeID: + description: volumeID uniquely identifies a Portworx volume + type: string + required: + - volumeID + type: object + projected: + description: projected items for all in one resources secrets, + configmaps, and downward API + properties: + defaultMode: + description: |- + defaultMode are the mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + sources: + description: |- + sources is the list of volume projections. Each entry in this list + handles one source. + items: + description: |- + Projection that may be projected along with other supported volume types. + Exactly one of these fields must be set. + properties: + clusterTrustBundle: + description: |- + ClusterTrustBundle allows a pod to access the `.spec.trustBundle` field + of ClusterTrustBundle objects in an auto-updating file. + + Alpha, gated by the ClusterTrustBundleProjection feature gate. + + ClusterTrustBundle objects can either be selected by name, or by the + combination of signer name and a label selector. + + Kubelet performs aggressive normalization of the PEM contents written + into the pod filesystem. Esoteric PEM features such as inter-block + comments and block headers are stripped. Certificates are deduplicated. + The ordering of certificates within the file is arbitrary, and Kubelet + may change the order over time. + properties: + labelSelector: + description: |- + Select all ClusterTrustBundles that match this label selector. Only has + effect if signerName is set. Mutually-exclusive with name. If unset, + interpreted as "match nothing". If set but empty, interpreted as "match + everything". + properties: + matchExpressions: + description: matchExpressions is a list of + label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + name: + description: |- + Select a single ClusterTrustBundle by object name. Mutually-exclusive + with signerName and labelSelector. + type: string + optional: + description: |- + If true, don't block pod startup if the referenced ClusterTrustBundle(s) + aren't available. If using name, then the named ClusterTrustBundle is + allowed not to exist. If using signerName, then the combination of + signerName and labelSelector is allowed to match zero + ClusterTrustBundles. + type: boolean + path: + description: Relative path from the volume root + to write the bundle. + type: string + signerName: + description: |- + Select all ClusterTrustBundles that match this signer name. + Mutually-exclusive with name. The contents of all selected + ClusterTrustBundles will be unified and deduplicated. + type: string + required: + - path + type: object + configMap: + description: configMap information about the configMap + data to project + properties: + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + ConfigMap will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: optional specify whether the ConfigMap + or its keys must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + description: downwardAPI information about the downwardAPI + data to project + properties: + items: + description: Items is a list of DownwardAPIVolume + file + items: + description: DownwardAPIVolumeFile represents + information to create the file containing + the pod field + properties: + fieldRef: + description: 'Required: Selects a field + of the pod: only annotations, labels, + name, namespace and uid are supported.' + properties: + apiVersion: + description: Version of the schema the + FieldPath is written in terms of, + defaults to "v1". + type: string + fieldPath: + description: Path of the field to select + in the specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + description: |- + Optional: mode bits used to set permissions on this file, must be an octal value + between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: 'Required: Path is the relative + path name of the file to be created. Must + not be absolute or contain the ''..'' + path. Must be utf-8 encoded. The first + item of the relative path must not start + with ''..''' + type: string + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. + properties: + containerName: + description: 'Container name: required + for volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format + of the exposed resources, defaults + to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to + select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + secret: + description: secret information about the secret data + to project + properties: + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + Secret will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: optional field specify whether the + Secret or its key must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + description: serviceAccountToken is information about + the serviceAccountToken data to project + properties: + audience: + description: |- + audience is the intended audience of the token. A recipient of a token + must identify itself with an identifier specified in the audience of the + token, and otherwise should reject the token. The audience defaults to the + identifier of the apiserver. + type: string + expirationSeconds: + description: |- + expirationSeconds is the requested duration of validity of the service + account token. As the token approaches expiration, the kubelet volume + plugin will proactively rotate the service account token. The kubelet will + start trying to rotate the token if the token is older than 80 percent of + its time to live or if the token is older than 24 hours.Defaults to 1 hour + and must be at least 10 minutes. + format: int64 + type: integer + path: + description: |- + path is the path relative to the mount point of the file to project the + token into. + type: string + required: + - path + type: object + type: object + type: array + x-kubernetes-list-type: atomic + type: object + quobyte: + description: quobyte represents a Quobyte mount on the host + that shares a pod's lifetime + properties: + group: + description: |- + group to map volume access to + Default is no group + type: string + readOnly: + description: |- + readOnly here will force the Quobyte volume to be mounted with read-only permissions. + Defaults to false. + type: boolean + registry: + description: |- + registry represents a single or multiple Quobyte Registry services + specified as a string as host:port pair (multiple entries are separated with commas) + which acts as the central registry for volumes + type: string + tenant: + description: |- + tenant owning the given Quobyte volume in the Backend + Used with dynamically provisioned Quobyte volumes, value is set by the plugin + type: string + user: + description: |- + user to map volume access to + Defaults to serivceaccount user + type: string + volume: + description: volume is a string that references an already + created Quobyte volume by name. + type: string + required: + - registry + - volume + type: object + rbd: + description: |- + rbd represents a Rados Block Device mount on the host that shares a pod's lifetime. + More info: https://examples.k8s.io/volumes/rbd/README.md + properties: + fsType: + description: |- + fsType is the filesystem type of the volume that you want to mount. + Tip: Ensure that the filesystem type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd + type: string + image: + description: |- + image is the rados image name. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + type: string + keyring: + default: /etc/ceph/keyring + description: |- + keyring is the path to key ring for RBDUser. + Default is /etc/ceph/keyring. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + type: string + monitors: + description: |- + monitors is a collection of Ceph monitors. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + items: + type: string + type: array + x-kubernetes-list-type: atomic + pool: + default: rbd + description: |- + pool is the rados pool name. + Default is rbd. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + type: string + readOnly: + description: |- + readOnly here will force the ReadOnly setting in VolumeMounts. + Defaults to false. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + type: boolean + secretRef: + description: |- + secretRef is name of the authentication secret for RBDUser. If provided + overrides keyring. + Default is nil. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + user: + default: admin + description: |- + user is the rados user name. + Default is admin. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + type: string + required: + - image + - monitors + type: object + scaleIO: + description: scaleIO represents a ScaleIO persistent volume + attached and mounted on Kubernetes nodes. + properties: + fsType: + default: xfs + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". + Default is "xfs". + type: string + gateway: + description: gateway is the host address of the ScaleIO + API Gateway. + type: string + protectionDomain: + description: protectionDomain is the name of the ScaleIO + Protection Domain for the configured storage. + type: string + readOnly: + description: |- + readOnly Defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: |- + secretRef references to the secret for ScaleIO user and other + sensitive information. If this is not provided, Login operation will fail. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + description: sslEnabled Flag enable/disable SSL communication + with Gateway, default false + type: boolean + storageMode: + default: ThinProvisioned + description: |- + storageMode indicates whether the storage for a volume should be ThickProvisioned or ThinProvisioned. + Default is ThinProvisioned. + type: string + storagePool: + description: storagePool is the ScaleIO Storage Pool associated + with the protection domain. + type: string + system: + description: system is the name of the storage system as + configured in ScaleIO. + type: string + volumeName: + description: |- + volumeName is the name of a volume already created in the ScaleIO system + that is associated with this volume source. + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + description: |- + secret represents a secret that should populate this volume. + More info: https://kubernetes.io/docs/concepts/storage/volumes#secret + properties: + defaultMode: + description: |- + defaultMode is Optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values + for mode bits. Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + items: + description: |- + items If unspecified, each key-value pair in the Data field of the referenced + Secret will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + optional: + description: optional field specify whether the Secret or + its keys must be defined + type: boolean + secretName: + description: |- + secretName is the name of the secret in the pod's namespace to use. + More info: https://kubernetes.io/docs/concepts/storage/volumes#secret + type: string + type: object + storageos: + description: storageOS represents a StorageOS volume attached + and mounted on Kubernetes nodes. + properties: + fsType: + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + readOnly: + description: |- + readOnly defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: |- + secretRef specifies the secret to use for obtaining the StorageOS API + credentials. If not specified, default values will be attempted. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + description: |- + volumeName is the human-readable name of the StorageOS volume. Volume + names are only unique within a namespace. + type: string + volumeNamespace: + description: |- + volumeNamespace specifies the scope of the volume within StorageOS. If no + namespace is specified then the Pod's namespace will be used. This allows the + Kubernetes name scoping to be mirrored within StorageOS for tighter integration. + Set VolumeName to any name to override the default behaviour. + Set to "default" if you are not using namespaces within StorageOS. + Namespaces that do not pre-exist within StorageOS will be created. + type: string + type: object + vsphereVolume: + description: vsphereVolume represents a vSphere volume attached + and mounted on kubelets host machine + properties: + fsType: + description: |- + fsType is filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + storagePolicyID: + description: storagePolicyID is the storage Policy Based + Management (SPBM) profile ID associated with the StoragePolicyName. + type: string + storagePolicyName: + description: storagePolicyName is the storage Policy Based + Management (SPBM) profile name. + type: string + volumePath: + description: volumePath is the path that identifies vSphere + volume vmdk + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + type: object + status: + description: ClusterVectorAggregatorStatus defines the observed state + of ClusterVectorAggregator + properties: + LastAppliedConfigHash: + format: int32 + type: integer + configCheckResult: + type: boolean + reason: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/bases/observability.kaasops.io_vectoraggregators.yaml b/config/crd/bases/observability.kaasops.io_vectoraggregators.yaml index 08367ca6..f3d754d8 100644 --- a/config/crd/bases/observability.kaasops.io_vectoraggregators.yaml +++ b/config/crd/bases/observability.kaasops.io_vectoraggregators.yaml @@ -3068,7 +3068,7 @@ spec: type: string selector: description: |- - Defines a filter for the Vector Pipeline and Cluster Vector Pipeline by labels. + Selector defines a filter for the Vector Pipeline and Cluster Vector Pipeline by labels. If not specified, all pipelines will be selected. properties: matchLabels: diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 5352cb24..ee03cc60 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -6,6 +6,7 @@ resources: - bases/observability.kaasops.io_vectorpipelines.yaml - bases/observability.kaasops.io_clustervectorpipelines.yaml - bases/observability.kaasops.io_vectoraggregators.yaml +- bases/observability.kaasops.io_clustervectoraggregators.yaml # +kubebuilder:scaffold:crdkustomizeresource patches: @@ -19,6 +20,7 @@ patches: #- path: patches/cainjection_in_vectorpipelines.yaml #- path: patches/cainjection_in_clustervectorpipelines.yaml #- path: patches/cainjection_in_vectoraggregators.yaml +#- path: patches/cainjection_in_clustervectoraggregators.yaml # +kubebuilder:scaffold:crdkustomizecainjectionpatch # [WEBHOOK] To enable webhook, uncomment the following section diff --git a/config/rbac/clustervectoraggregator_editor_role.yaml b/config/rbac/clustervectoraggregator_editor_role.yaml new file mode 100644 index 00000000..2b2b6921 --- /dev/null +++ b/config/rbac/clustervectoraggregator_editor_role.yaml @@ -0,0 +1,27 @@ +# permissions for end users to edit clustervectoraggregators. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: vector-operator + app.kubernetes.io/managed-by: kustomize + name: clustervectoraggregator-editor-role +rules: +- apiGroups: + - observability.kaasops.io + resources: + - clustervectoraggregators + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - observability.kaasops.io + resources: + - clustervectoraggregators/status + verbs: + - get diff --git a/config/rbac/clustervectoraggregator_viewer_role.yaml b/config/rbac/clustervectoraggregator_viewer_role.yaml new file mode 100644 index 00000000..a56a4c27 --- /dev/null +++ b/config/rbac/clustervectoraggregator_viewer_role.yaml @@ -0,0 +1,23 @@ +# permissions for end users to view clustervectoraggregators. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: vector-operator + app.kubernetes.io/managed-by: kustomize + name: clustervectoraggregator-viewer-role +rules: +- apiGroups: + - observability.kaasops.io + resources: + - clustervectoraggregators + verbs: + - get + - list + - watch +- apiGroups: + - observability.kaasops.io + resources: + - clustervectoraggregators/status + verbs: + - get diff --git a/config/rbac/kustomization.yaml b/config/rbac/kustomization.yaml index 75b75cc3..df1894bb 100644 --- a/config/rbac/kustomization.yaml +++ b/config/rbac/kustomization.yaml @@ -22,6 +22,8 @@ resources: # default, aiding admins in cluster management. Those roles are # not used by the Project itself. You can comment the following lines # if you do not want those helpers be installed with your Project. +- clustervectoraggregator_editor_role.yaml +- clustervectoraggregator_viewer_role.yaml - vectoraggregator_editor_role.yaml - vectoraggregator_viewer_role.yaml - clustervectorpipeline_editor_role.yaml diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 62ca5634..b682bbad 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -51,6 +51,7 @@ rules: - apiGroups: - observability.kaasops.io resources: + - clustervectoraggregators - clustervectorpipelines - vectoraggregators - vectorpipelines @@ -66,6 +67,7 @@ rules: - apiGroups: - observability.kaasops.io resources: + - clustervectoraggregators/finalizers - clustervectorpipelines/finalizers - vectoraggregators/finalizers - vectorpipelines/finalizers @@ -75,6 +77,7 @@ rules: - apiGroups: - observability.kaasops.io resources: + - clustervectoraggregators/status - clustervectorpipelines/status - vectoraggregators/status - vectorpipelines/status diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index aa9d538a..825d6ef5 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -4,4 +4,5 @@ resources: - observability_v1alpha1_vectorpipeline.yaml - observability_v1alpha1_clustervectorpipeline.yaml - observability_v1alpha1_vectoraggregator.yaml +- observability_v1alpha1_clustervectoraggregator.yaml # +kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/samples/observability_v1alpha1_clustervectoraggregator.yaml b/config/samples/observability_v1alpha1_clustervectoraggregator.yaml new file mode 100644 index 00000000..151e4d3c --- /dev/null +++ b/config/samples/observability_v1alpha1_clustervectoraggregator.yaml @@ -0,0 +1,9 @@ +apiVersion: observability.kaasops.io/v1alpha1 +kind: ClusterVectorAggregator +metadata: + labels: + app.kubernetes.io/name: vector-operator + app.kubernetes.io/managed-by: kustomize + name: clustervectoraggregator-sample +spec: + # TODO(user): Add fields here diff --git a/docs/design.md b/docs/design.md index 907305a0..2959dec2 100644 --- a/docs/design.md +++ b/docs/design.md @@ -2,26 +2,26 @@ This document describes the design and interaction between the custom resource definitions (CRD) that the Vector Operator introduces. Operator introduces the following custom resources: -- [Vector](#Vector) +- [Vector](#vector) - [VectorPipeline](#vectorpipeline) - [ClusterVectorPipeline](#clustervectorpipeline) +- [VectorAggregator](#vectoraggregator) +- [ClusterVectorAggregator](#clustervectoraggregator) # Vector -The `Vector` CRD declaratively defines a Vector installation to run in a Kubernetes cluster. +The `Vector` CRD declaratively defines a Vector agent installation to run in a Kubernetes cluster. For each Vector resource, the Operator deploys a properly configured DaemonSet in the same namespace. For each Vector resource, the Operator adds: - DaemonSet with Vector -- Secret with Vector Configurtion file +- Secret with Vector Configuration file - Service. (For connect to Vector API, or scrape Vector metrics, if enabled) -- ServiceAccount/ClusterRole/RoleBinding for get access to Kubernetes API - +- ServiceAccount/ClusterRole/ClusterRoleBinding for get access to Kubernetes API ## Restrictions - Currently tested only ONE installation Vector on Kubernetes cluster ## Planned -- Add aggregator role in StatefullSet -- Add features for compress Vector configuration file. (Delete dublicates sources/Transforms/Sinks. Compress to gzip) +- Add features for compress Vector configuration file. (Delete duplicates sources/Transforms/Sinks. Compress to gzip) ## Specification Specification access to [this](https://github.com/kaasops/vector-operator/blob/main/docs/specification.md#vector-spec) page @@ -31,10 +31,12 @@ Specification access to [this](https://github.com/kaasops/vector-operator/blob/m The `VectorPipeline` is a namespace-scoped CRD. The `VectorPipeline` CRD defines Sources, Transforms and Sinks rules for Vector. All `VectorPipelines`, with validated configuration file, added to Vector configuration file. +Depending on the types of sources, it can take on the role of either an agent or an aggregator. ## Restrictions - For source available only [kubernetes_logs](https://vector.dev/docs/reference/configuration/sources/kubernetes_logs/) type - For source field `extra_namespace_label_selector` cannot be installed. The operator control this field and sets the namespace there, where VectorPipeline is defined. +- All source types must belong to the same role. ## Specification Specification access to [this](https://github.com/kaasops/vector-operator/blob/main/docs/specification.md#vectorpipelinespec-clustervectorpipelinespec) page @@ -43,8 +45,33 @@ Specification access to [this](https://github.com/kaasops/vector-operator/blob/m The `ClusterVectorPipeline` is a cluster-scoped CRD. The `ClusterVectorPipeline` CRD defines Sources, Transforms and Sinks rules for Vector. All `ClusterVectorPipelines`, with validated configuration file, added to Vector configuration file. +Depending on the types of sources, it can take on the role of either an agent or an aggregator. ClusterVectorPipelines works like VectorPipeline, but without restrictions. ## Specification -Specification access to [this](https://github.com/kaasops/vector-operator/blob/main/docs/specification.md#vectorpipelinespec-clustervectorpipelinespec) page \ No newline at end of file +Specification access to [this](https://github.com/kaasops/vector-operator/blob/main/docs/specification.md#vectorpipelinespec-clustervectorpipelinespec) page + + +# VectorAggregator +The `VectorAggregator` CRD declaratively defines a Vector aggregator installation to run in a Kubernetes cluster. +For each VectorAggregator resource, the Operator deploys a properly configured Deployment in the same namespace. +For each VectorAggregator resource, the Operator adds: +- Deployment with Vector +- Secret with Vector Configuration file +- Service. (For connect to Vector API, or scrape Vector metrics, if enabled) +- ServiceAccount/ClusterRole/ClusterRoleBinding for get access to Kubernetes API + +## Restrictions +- It only forms a configuration from VectorPipelines in the same namespace as the aggregator + +## Specification +Specification access to [this](https://github.com/kaasops/vector-operator/blob/main/docs/specification.md#vectoraggregator-spec) page + + +# ClusterVectorAggregator +The `ClusterVectorAggregator` is a cluster-scoped CRD that works like VectorAggregator but with ClusterVectorPipelines. + +## Restrictions +- It works only with ClusterVectorPipelines + diff --git a/docs/specification.md b/docs/specification.md index b83da489..afb728dd 100644 --- a/docs/specification.md +++ b/docs/specification.md @@ -10,7 +10,7 @@ # Vector Spec - + diff --git a/go.mod b/go.mod index d870d121..cb759173 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,8 @@ require ( github.com/stoewer/go-strcase v1.2.0 github.com/stretchr/testify v1.9.0 golang.org/x/sync v0.7.0 + google.golang.org/grpc v1.65.0 + google.golang.org/protobuf v1.34.2 gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.31.0 k8s.io/apimachinery v0.31.0 @@ -108,8 +110,6 @@ require ( gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect - google.golang.org/grpc v1.65.0 // indirect - google.golang.org/protobuf v1.34.2 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/internal/config/aggregator.go b/internal/config/aggregator.go index 266e4e85..98aba528 100644 --- a/internal/config/aggregator.go +++ b/internal/config/aggregator.go @@ -19,7 +19,10 @@ func BuildAggregatorConfig(params VectorConfigParams, pipelines ...pipeline.Pipe cfg.internal.servicePort = make(map[string]*ServicePort) + var kubernetesEventsPort int32 = 42000 + for _, pipeline := range pipelines { + kubernetesEventsAlreadyExists := false p := &PipelineConfig{} if err := UnmarshalJson(pipeline.GetSpec(), p); err != nil { return nil, fmt.Errorf("failed to unmarshal pipeline %s: %w", pipeline.GetName(), err) @@ -30,14 +33,11 @@ func BuildAggregatorConfig(params VectorConfigParams, pipelines ...pipeline.Pipe switch v.Type { case kubernetesEventsType: { - address := v.Options["address"].(string) - if address == "" { - return nil, fmt.Errorf("address is empty from %s", pipeline.GetName()) - } - _, port, err := net.SplitHostPort(address) - if err != nil { - return nil, fmt.Errorf("failed to parse address %s: %w", address, err) + if kubernetesEventsAlreadyExists { + return nil, fmt.Errorf("pipeline can only contain one source with the type kubernetes_events") } + kubernetesEventsAlreadyExists = true + address := net.JoinHostPort(net.IPv4zero.String(), strconv.Itoa(int(kubernetesEventsPort))) settings = &Source{ Name: k, Type: VectorType, @@ -45,13 +45,9 @@ func BuildAggregatorConfig(params VectorConfigParams, pipelines ...pipeline.Pipe "address": address, }, } - portN, err := parsePort(port) - if err != nil { - return nil, fmt.Errorf("failed to parse port %s: %w", port, err) - } - err = cfg.internal.addServicePort(&ServicePort{ + err := cfg.internal.addServicePort(&ServicePort{ IsKubernetesEvents: true, - Port: portN, + Port: kubernetesEventsPort, Protocol: corev1.ProtocolTCP, Namespace: pipeline.GetNamespace(), SourceName: k, @@ -60,6 +56,7 @@ func BuildAggregatorConfig(params VectorConfigParams, pipelines ...pipeline.Pipe if err != nil { return nil, err } + kubernetesEventsPort++ } default: { diff --git a/internal/controller/clustervectoraggregator_controller.go b/internal/controller/clustervectoraggregator_controller.go new file mode 100644 index 00000000..f949551b --- /dev/null +++ b/internal/controller/clustervectoraggregator_controller.go @@ -0,0 +1,223 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "context" + "errors" + "github.com/kaasops/vector-operator/internal/config" + "github.com/kaasops/vector-operator/internal/config/configcheck" + "github.com/kaasops/vector-operator/internal/k8sevents" + "github.com/kaasops/vector-operator/internal/pipeline" + "github.com/kaasops/vector-operator/internal/utils/hash" + "github.com/kaasops/vector-operator/internal/utils/k8s" + "github.com/kaasops/vector-operator/internal/vector/aggregator" + monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + api_errors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/source" + "time" + + v1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" +) + +// ClusterVectorAggregatorReconciler reconciles a ClusterVectorAggregator object +type ClusterVectorAggregatorReconciler struct { + client.Client + Scheme *runtime.Scheme + + EventsCollector *k8sevents.EventsCollector + Clientset *kubernetes.Clientset + ConfigCheckTimeout time.Duration + EventChan chan event.GenericEvent +} + +// +kubebuilder:rbac:groups=observability.kaasops.io,resources=clustervectoraggregators,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=observability.kaasops.io,resources=clustervectoraggregators/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=observability.kaasops.io,resources=clustervectoraggregators/finalizers,verbs=update + +// +kubebuilder:rbac:groups="",resources=serviceaccounts,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups="",resources=services,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups="",resources=pods,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups="",resources=pods/log,verbs=get;list +// +kubebuilder:rbac:groups="",resources=namespaces,verbs=list;watch +// +kubebuilder:rbac:groups="",resources=nodes,verbs=list;watch +// +kubebuilder:rbac:groups="",resources=events,verbs=list;watch +// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups="rbac.authorization.k8s.io",resources=clusterrolebindings,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups="rbac.authorization.k8s.io",resources=clusterroles,verbs=get;list;watch;create;update;patch;delete + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the ClusterVectorAggregator object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.0/pkg/reconcile +func (r *ClusterVectorAggregatorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + _ = log.FromContext(ctx) + + clusterAggregator := &v1alpha1.ClusterVectorAggregator{} + err := r.Get(ctx, req.NamespacedName, clusterAggregator) + if err != nil { + if api_errors.IsNotFound(err) { + r.EventsCollector.UnregisterByAggregatorID(req.String()) + return ctrl.Result{}, nil + } + return ctrl.Result{}, err + } + setClusterAggregatorTypeMetaIfNeeded(clusterAggregator) + + return r.createOrUpdateVectorAggregator(ctx, r.Client, r.Clientset, clusterAggregator) +} + +func (r *ClusterVectorAggregatorReconciler) createOrUpdateVectorAggregator(ctx context.Context, client client.Client, clientset *kubernetes.Clientset, v *v1alpha1.ClusterVectorAggregator) (ctrl.Result, error) { + log := log.FromContext(ctx).WithValues("VectorAggregator", v.Name) + vaCtrl := aggregator.NewController(v, client, clientset, r.EventsCollector) + if vaCtrl.Namespace == "" { + if err := vaCtrl.SetFailedStatus(ctx, "spec.resourceNamespace is empty"); err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{}, nil + } + + pipelines, err := pipeline.GetValidPipelines(ctx, vaCtrl.Client, pipeline.FilterPipelines{ + Scope: pipeline.ClusterPipelines, + Selector: v.Spec.Selector, + Role: v1alpha1.VectorPipelineRoleAggregator, + }) + if err != nil { + return ctrl.Result{}, err + } + + cfg, err := config.BuildAggregatorConfig(config.VectorConfigParams{ + ApiEnabled: vaCtrl.Spec.Api.Enabled, + PlaygroundEnabled: vaCtrl.Spec.Api.Playground, + InternalMetrics: vaCtrl.Spec.InternalMetrics, + }, pipelines...) + if err != nil { + if err := vaCtrl.SetFailedStatus(ctx, err.Error()); err != nil { + return ctrl.Result{}, err + } + log.Error(err, "Build config failed") + return ctrl.Result{}, nil + } + + byteCfg, err := cfg.MarshalJSON() + if err != nil { + return ctrl.Result{}, err + } + cfgHash := hash.Get(byteCfg) + + if !vaCtrl.Spec.ConfigCheck.Disabled { + if vaCtrl.Status.LastAppliedConfigHash == nil || *vaCtrl.Status.LastAppliedConfigHash != cfgHash { + reason, err := configcheck.New( + byteCfg, + vaCtrl.Client, + vaCtrl.ClientSet, + &vaCtrl.Spec.VectorCommon, + vaCtrl.Name, + vaCtrl.Namespace, + r.ConfigCheckTimeout, + configcheck.ConfigCheckInitiatorVector, + ).Run(ctx) + if err != nil { + if errors.Is(err, configcheck.ValidationError) { + if err := vaCtrl.SetFailedStatus(ctx, reason); err != nil { + return ctrl.Result{}, err + } + log.Error(err, "Invalid config") + return ctrl.Result{}, nil + } + return ctrl.Result{}, err + } + } + } + + vaCtrl.ConfigBytes = byteCfg + vaCtrl.Config = cfg + + if err := vaCtrl.EnsureVectorAggregator(ctx); err != nil { + return ctrl.Result{}, err + } + + if err := vaCtrl.SetSuccessStatus(ctx, &cfgHash); err != nil { + return ctrl.Result{}, err + } + + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *ClusterVectorAggregatorReconciler) SetupWithManager(mgr ctrl.Manager) error { + monitoringCRD, err := k8s.ResourceExists(r.Clientset.DiscoveryClient, monitorv1.SchemeGroupVersion.String(), monitorv1.PodMonitorsKind) + if err != nil { + return err + } + + builder := ctrl.NewControllerManagedBy(mgr). + For(&v1alpha1.ClusterVectorAggregator{}). + WatchesRawSource(source.Channel(r.EventChan, &handler.EnqueueRequestForObject{})). + Owns(&appsv1.Deployment{}). + Owns(&corev1.Service{}). + Owns(&corev1.Secret{}). + Owns(&corev1.ServiceAccount{}). + Owns(&rbacv1.ClusterRole{}). + Owns(&rbacv1.ClusterRoleBinding{}) + + if monitoringCRD { + builder.Owns(&monitorv1.PodMonitor{}) + } + + if err = builder.Complete(r); err != nil { + return err + } + return nil +} + +func listClusterVectorAggregators(ctx context.Context, client client.Client) (vectors []*v1alpha1.ClusterVectorAggregator, err error) { + vectorList := v1alpha1.ClusterVectorAggregatorList{} + err = client.List(ctx, &vectorList) + if err != nil { + return nil, err + } + for _, v := range vectorList.Items { + setClusterAggregatorTypeMetaIfNeeded(&v) + vectors = append(vectors, &v) + } + return vectors, nil +} + +func setClusterAggregatorTypeMetaIfNeeded(cr *v1alpha1.ClusterVectorAggregator) { + // https://github.com/kubernetes/kubernetes/issues/80609 + if cr.Kind == "" || cr.APIVersion == "" { + cr.Kind = "ClusterVectorAggregator" + cr.APIVersion = "observability.kaasops.io/v1alpha1" + } +} diff --git a/internal/controller/clustervectoraggregator_controller_test.go b/internal/controller/clustervectoraggregator_controller_test.go new file mode 100644 index 00000000..1df6f6b5 --- /dev/null +++ b/internal/controller/clustervectoraggregator_controller_test.go @@ -0,0 +1,89 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "context" + "sigs.k8s.io/controller-runtime/pkg/event" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + observabilityv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" +) + +var _ = Describe("ClusterVectorAggregator Controller", func() { + Context("When reconciling a resource", func() { + const resourceName = "test-resource" + + ctx := context.Background() + + typeNamespacedName := types.NamespacedName{ + Name: resourceName, + Namespace: "default", // TODO(user):Modify as needed + } + clustervectoraggregator := &observabilityv1alpha1.ClusterVectorAggregator{} + + BeforeEach(func() { + By("creating the custom resource for the Kind ClusterVectorAggregator") + err := k8sClient.Get(ctx, typeNamespacedName, clustervectoraggregator) + if err != nil && errors.IsNotFound(err) { + resource := &observabilityv1alpha1.ClusterVectorAggregator{ + ObjectMeta: metav1.ObjectMeta{ + Name: resourceName, + Namespace: "default", + }, + // TODO(user): Specify other spec details if needed. + } + Expect(k8sClient.Create(ctx, resource)).To(Succeed()) + } + }) + + AfterEach(func() { + // TODO(user): Cleanup logic after each test, like removing the resource instance. + resource := &observabilityv1alpha1.ClusterVectorAggregator{} + err := k8sClient.Get(ctx, typeNamespacedName, resource) + Expect(err).NotTo(HaveOccurred()) + + By("Cleanup the specific resource instance ClusterVectorAggregator") + Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) + }) + It("should successfully reconcile the resource", func() { + By("Reconciling the created resource") + controllerReconciler := &ClusterVectorAggregatorReconciler{ + Client: k8sClient, + Scheme: k8sClient.Scheme(), + EventsCollector: k8sEventsCollector, + EventChan: make(chan event.GenericEvent, 1), + ConfigCheckTimeout: configCheckTimeout, + Clientset: clientset, + } + + _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ + NamespacedName: typeNamespacedName, + }) + Expect(err).NotTo(HaveOccurred()) + // TODO(user): Add more specific assertions depending on your controller's reconciliation logic. + // Example: If you expect a certain status condition after reconciliation, verify it here. + }) + }) +}) diff --git a/internal/controller/pipeline_controller.go b/internal/controller/pipeline_controller.go index df809ed8..9bf94437 100644 --- a/internal/controller/pipeline_controller.go +++ b/internal/controller/pipeline_controller.go @@ -45,11 +45,12 @@ type PipelineReconciler struct { Scheme *runtime.Scheme // Temp. Wait this issue - https://github.com/kubernetes-sigs/controller-runtime/issues/452 - Clientset *kubernetes.Clientset - ConfigCheckTimeout time.Duration - VectorAgentEventCh chan event.GenericEvent - VectorAggregatorsEventCh chan event.GenericEvent - EventsCollector *k8sevents.EventsCollector + Clientset *kubernetes.Clientset + ConfigCheckTimeout time.Duration + VectorAgentEventCh chan event.GenericEvent + VectorAggregatorsEventCh chan event.GenericEvent + ClusterVectorAggregatorsEventCh chan event.GenericEvent + EventsCollector *k8sevents.EventsCollector } var ( @@ -79,8 +80,13 @@ func (r *PipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c log.Error(err, "Failed to get vector aggregators") return ctrl.Result{}, nil } + clusterVectorAggregators, err := listClusterVectorAggregators(ctx, r.Client) + if err != nil { + log.Error(err, "Failed to get cluster vector aggregators") + return ctrl.Result{}, nil + } - if len(vectorAgents) == 0 && len(vectorAggregators) == 0 { + if len(vectorAgents) == 0 && len(vectorAggregators) == 0 && len(clusterVectorAggregators) == 0 { log.Info("Vectors not found") return ctrl.Result{}, nil } @@ -93,6 +99,9 @@ func (r *PipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c for _, vector := range vectorAggregators { r.VectorAggregatorsEventCh <- event.GenericEvent{Object: vector} } + for _, vector := range clusterVectorAggregators { + r.ClusterVectorAggregatorsEventCh <- event.GenericEvent{Object: vector} + } return ctrl.Result{}, nil } @@ -159,46 +168,92 @@ func (r *PipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c } else { - for _, vector := range vectorAggregators { - eg.Go(func() error { - vaCtrl := aggregator.NewController(vector, r.Client, r.Clientset, r.EventsCollector) - cfg, err := config.BuildAggregatorConfig(config.VectorConfigParams{ - ApiEnabled: vaCtrl.VectorAggregator.Spec.Api.Enabled, - PlaygroundEnabled: vaCtrl.VectorAggregator.Spec.Api.Playground, - InternalMetrics: vaCtrl.VectorAggregator.Spec.InternalMetrics, - }, pipelineCR) - if err != nil { - return fmt.Errorf("aggregator %s/%s build config failed: %w: %w", vector.Namespace, vector.Name, ErrBuildConfigFailed, err) - } - if err != nil { + if pipelineCR.GetNamespace() != "" { + for _, vector := range vectorAggregators { + eg.Go(func() error { + vaCtrl := aggregator.NewController(vector, r.Client, r.Clientset, r.EventsCollector) + cfg, err := config.BuildAggregatorConfig(config.VectorConfigParams{ + ApiEnabled: vaCtrl.Spec.Api.Enabled, + PlaygroundEnabled: vaCtrl.Spec.Api.Playground, + InternalMetrics: vaCtrl.Spec.InternalMetrics, + }, pipelineCR) + if err != nil { + return fmt.Errorf("aggregator %s/%s build config failed: %w: %w", vector.Namespace, vector.Name, ErrBuildConfigFailed, err) + } + if err != nil { + return err + } + + byteConfig, err := cfg.MarshalJSON() + if err != nil { + return err + } + + vaCtrl.ConfigBytes = byteConfig + vaCtrl.Config = cfg + + configCheck := configcheck.New( + vaCtrl.ConfigBytes, + vaCtrl.Client, + vaCtrl.ClientSet, + &vaCtrl.Spec.VectorCommon, + vaCtrl.Name, + vaCtrl.Namespace, + r.ConfigCheckTimeout, + configcheck.ConfigCheckInitiatorPipieline, + ) + + reason, err := configCheck.Run(ctx) + if reason != "" { + return fmt.Errorf("aggregator %s/%s config check failed: %s", vector.Namespace, vector.Name, reason) + } return err - } - - byteConfig, err := cfg.MarshalJSON() - if err != nil { + }) + } + + } else { + + for _, vector := range clusterVectorAggregators { + eg.Go(func() error { + vaCtrl := aggregator.NewController(vector, r.Client, r.Clientset, r.EventsCollector) + cfg, err := config.BuildAggregatorConfig(config.VectorConfigParams{ + ApiEnabled: vaCtrl.Spec.Api.Enabled, + PlaygroundEnabled: vaCtrl.Spec.Api.Playground, + InternalMetrics: vaCtrl.Spec.InternalMetrics, + }, pipelineCR) + if err != nil { + return fmt.Errorf("cluster aggregator %s/%s build config failed: %w: %w", vector.Namespace, vector.Name, ErrBuildConfigFailed, err) + } + if err != nil { + return err + } + + byteConfig, err := cfg.MarshalJSON() + if err != nil { + return err + } + + vaCtrl.ConfigBytes = byteConfig + vaCtrl.Config = cfg + + configCheck := configcheck.New( + vaCtrl.ConfigBytes, + vaCtrl.Client, + vaCtrl.ClientSet, + &vaCtrl.Spec.VectorCommon, + vaCtrl.Name, + vaCtrl.Namespace, + r.ConfigCheckTimeout, + configcheck.ConfigCheckInitiatorPipieline, + ) + + reason, err := configCheck.Run(ctx) + if reason != "" { + return fmt.Errorf("cluster aggregator %s/%s config check failed: %s", vector.Namespace, vector.Name, reason) + } return err - } - - vaCtrl.ConfigBytes = byteConfig - vaCtrl.Config = cfg - - configCheck := configcheck.New( - vaCtrl.ConfigBytes, - vaCtrl.Client, - vaCtrl.ClientSet, - &vaCtrl.VectorAggregator.Spec.VectorCommon, - vaCtrl.VectorAggregator.Name, - vaCtrl.VectorAggregator.Namespace, - r.ConfigCheckTimeout, - configcheck.ConfigCheckInitiatorPipieline, - ) - - reason, err := configCheck.Run(ctx) - if reason != "" { - return fmt.Errorf("aggregator %s/%s config check failed: %s", vector.Namespace, vector.Name, reason) - } - return err - }) + }) + } } } @@ -221,6 +276,9 @@ func (r *PipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c for _, vector := range vectorAggregators { r.VectorAggregatorsEventCh <- event.GenericEvent{Object: vector} } + for _, vector := range clusterVectorAggregators { + r.ClusterVectorAggregatorsEventCh <- event.GenericEvent{Object: vector} + } log.Info("finish Reconcile Pipeline") return ctrl.Result{}, nil diff --git a/internal/controller/pipeline_controller_test.go b/internal/controller/pipeline_controller_test.go index d0e58d36..022cb74e 100644 --- a/internal/controller/pipeline_controller_test.go +++ b/internal/controller/pipeline_controller_test.go @@ -75,6 +75,7 @@ var _ = Describe("VectorPipeline Controller", func() { Clientset: clientset, ConfigCheckTimeout: configCheckTimeout, VectorAgentEventCh: make(chan event.GenericEvent, 1), + EventsCollector: k8sEventsCollector, } _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ diff --git a/internal/controller/suite_test.go b/internal/controller/suite_test.go index decb5ad0..3d9e8d48 100644 --- a/internal/controller/suite_test.go +++ b/internal/controller/suite_test.go @@ -19,6 +19,7 @@ package controller import ( "context" "fmt" + "github.com/kaasops/vector-operator/internal/k8sevents" "path/filepath" "runtime" "testing" @@ -38,6 +39,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log/zap" "github.com/kaasops/vector-operator/api/v1alpha1" + observabilityv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" // +kubebuilder:scaffold:imports ) @@ -51,6 +53,7 @@ var ctx context.Context var cancel context.CancelFunc var clientset *kubernetes.Clientset var configCheckTimeout time.Duration +var k8sEventsCollector *k8sevents.EventsCollector func TestControllers(t *testing.T) { RegisterFailHandler(Fail) @@ -87,6 +90,9 @@ var _ = BeforeSuite(func() { err = v1alpha1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) + err = observabilityv1alpha1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + // +kubebuilder:scaffold:scheme k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) @@ -98,6 +104,8 @@ var _ = BeforeSuite(func() { clientset, err = kubernetes.NewForConfig(cfg) Expect(err).NotTo(HaveOccurred()) Expect(clientset).NotTo(BeNil()) + + k8sEventsCollector = k8sevents.NewEventsCollector(clientset, &FakeLogger{}) }) var _ = AfterSuite(func() { @@ -106,3 +114,8 @@ var _ = AfterSuite(func() { err := testEnv.Stop() Expect(err).NotTo(HaveOccurred()) }) + +type FakeLogger struct{} + +func (l *FakeLogger) Info(msg string, keysAndValues ...any) {} +func (l *FakeLogger) Error(err error, msg string, keysAndValues ...any) {} diff --git a/internal/controller/vectoraggregator_controller.go b/internal/controller/vectoraggregator_controller.go index bfdbd918..20750fb4 100644 --- a/internal/controller/vectoraggregator_controller.go +++ b/internal/controller/vectoraggregator_controller.go @@ -113,6 +113,7 @@ func (r *VectorAggregatorReconciler) Reconcile(ctx context.Context, req ctrl.Req setAggregatorTypeMetaIfNeeded(vectorCR) if vectorCR.IsBeingDeleted() { + r.EventsCollector.UnregisterByAggregatorID(req.String()) if controllerutil.ContainsFinalizer(vectorCR, aggregatorFinalizerName) { if err := r.deleteVectorAggregator(ctx, vectorCR); err != nil { if !api_errors.IsNotFound(err) { @@ -186,16 +187,16 @@ func (r *VectorAggregatorReconciler) createOrUpdateVectorAggregator(ctx context. Scope: pipeline.NamespacedPipeline, Selector: v.Spec.Selector, Role: v1alpha1.VectorPipelineRoleAggregator, - Namespace: vaCtrl.VectorAggregator.Namespace, + Namespace: vaCtrl.Namespace, }) if err != nil { return ctrl.Result{}, err } cfg, err := config.BuildAggregatorConfig(config.VectorConfigParams{ - ApiEnabled: vaCtrl.VectorAggregator.Spec.Api.Enabled, - PlaygroundEnabled: vaCtrl.VectorAggregator.Spec.Api.Playground, - InternalMetrics: vaCtrl.VectorAggregator.Spec.InternalMetrics, + ApiEnabled: vaCtrl.Spec.Api.Enabled, + PlaygroundEnabled: vaCtrl.Spec.Api.Playground, + InternalMetrics: vaCtrl.Spec.InternalMetrics, }, pipelines...) if err != nil { if err := vaCtrl.SetFailedStatus(ctx, err.Error()); err != nil { @@ -211,15 +212,15 @@ func (r *VectorAggregatorReconciler) createOrUpdateVectorAggregator(ctx context. } cfgHash := hash.Get(byteCfg) - if !vaCtrl.VectorAggregator.Spec.ConfigCheck.Disabled { - if vaCtrl.VectorAggregator.Status.LastAppliedConfigHash == nil || *vaCtrl.VectorAggregator.Status.LastAppliedConfigHash != cfgHash { + if !vaCtrl.Spec.ConfigCheck.Disabled { + if vaCtrl.Status.LastAppliedConfigHash == nil || *vaCtrl.Status.LastAppliedConfigHash != cfgHash { reason, err := configcheck.New( byteCfg, vaCtrl.Client, vaCtrl.ClientSet, - &vaCtrl.VectorAggregator.Spec.VectorCommon, - vaCtrl.VectorAggregator.Name, - vaCtrl.VectorAggregator.Namespace, + &vaCtrl.Spec.VectorCommon, + vaCtrl.Name, + vaCtrl.Namespace, r.ConfigCheckTimeout, configcheck.ConfigCheckInitiatorVector, ).Run(ctx) diff --git a/internal/controller/vectoraggregator_controller_test.go b/internal/controller/vectoraggregator_controller_test.go index d65b3f0e..0df94d92 100644 --- a/internal/controller/vectoraggregator_controller_test.go +++ b/internal/controller/vectoraggregator_controller_test.go @@ -73,6 +73,7 @@ var _ = Describe("VectorAggregator Controller", func() { Clientset: clientset, ConfigCheckTimeout: time.Second * 10, EventChan: make(chan event.GenericEvent, 1), + EventsCollector: k8sEventsCollector, } // remove finalizer _, err = controllerReconciler.Reconcile(ctx, reconcile.Request{ @@ -88,6 +89,7 @@ var _ = Describe("VectorAggregator Controller", func() { Clientset: clientset, ConfigCheckTimeout: time.Second * 10, EventChan: make(chan event.GenericEvent, 1), + EventsCollector: k8sEventsCollector, } _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ NamespacedName: typeNamespacedName, diff --git a/internal/k8sevents/event.go b/internal/k8sevents/event.go index 947f56ea..284e7721 100644 --- a/internal/k8sevents/event.go +++ b/internal/k8sevents/event.go @@ -17,6 +17,7 @@ func k8sEventToVectorLog(ev *corev1.Event) *gen.Log { "message": valueFromString(ev.Message), "reason": valueFromString(ev.Reason), "action": valueFromString(ev.Action), + "type": valueFromString(ev.Type), "creationTimestamp": valueFromString(ev.CreationTimestamp.Format(time.RFC3339)), "firstTimestamp": valueFromString(ev.FirstTimestamp.Format(time.RFC3339)), "lastTimestamp": valueFromString(ev.LastTimestamp.Format(time.RFC3339)), diff --git a/internal/k8sevents/manager.go b/internal/k8sevents/manager.go index 4229150e..0f8f037e 100644 --- a/internal/k8sevents/manager.go +++ b/internal/k8sevents/manager.go @@ -2,12 +2,10 @@ package k8sevents import ( "fmt" - corev1 "k8s.io/api/core/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "net" "sync" - "time" ) type Logger interface { @@ -19,55 +17,68 @@ type EventsCollector struct { client rest.Interface logger Logger mx sync.Mutex - mp map[string]*watcher + mp map[string]map[string]*watcher } func NewEventsCollector(clientset *kubernetes.Clientset, logger Logger) *EventsCollector { return &EventsCollector{ - mp: make(map[string]*watcher), + mp: make(map[string]map[string]*watcher), client: clientset.CoreV1().RESTClient(), logger: logger, } } -func (m *EventsCollector) RegisterSubscriber(svcName, svcNamespace, port, namespace string) { +func (m *EventsCollector) RegisterSubscriber(aggregatorID, svcName, svcNamespace, port, namespace string) { host := fmt.Sprintf("%s.%s", svcName, svcNamespace) addr := net.JoinHostPort(host, port) c := newWatcher(addr, namespace, m.logger) m.mx.Lock() - if oldC, ok := m.mp[host]; ok { - if oldC.addr == addr && oldC.namespace == namespace { - m.mx.Unlock() - return + + if group, ok := m.mp[aggregatorID]; ok { + + if oldC, ok := group[host]; ok { + if oldC.addr == addr && oldC.namespace == namespace { + m.mx.Unlock() + return + } + oldC.close() } - oldC.close() + + group[host] = c + + } else { + group = make(map[string]*watcher) + group[host] = c + m.mp[aggregatorID] = group } - m.mp[host] = c + m.mx.Unlock() c.watchEvents(m.client) } -func (m *EventsCollector) UnregisterSubscriber(svcName, svcNamespace string) { +func (m *EventsCollector) UnregisterSubscriber(aggregatorID, svcName, svcNamespace string) { host := fmt.Sprintf("%s.%s", svcName, svcNamespace) m.mx.Lock() defer m.mx.Unlock() - if v, ok := m.mp[host]; ok { - v.close() - delete(m.mp, host) + + if group, ok := m.mp[aggregatorID]; ok { + if v, ok := group[host]; ok { + v.close() + delete(m.mp, host) + } } } -func eventTimestamp(ev *corev1.Event) time.Time { - var ts time.Time - switch { - case ev.EventTime.Time != time.Time{}: - ts = ev.EventTime.Time - case ev.LastTimestamp.Time != time.Time{}: - ts = ev.LastTimestamp.Time - case ev.FirstTimestamp.Time != time.Time{}: - ts = ev.FirstTimestamp.Time +func (m *EventsCollector) UnregisterByAggregatorID(aggregatorID string) { + m.mx.Lock() + defer m.mx.Unlock() + if group, ok := m.mp[aggregatorID]; ok { + for host, w := range group { + w.close() + delete(m.mp, host) + } + delete(m.mp, aggregatorID) } - return ts } diff --git a/internal/k8sevents/watcher.go b/internal/k8sevents/watcher.go index e7fee726..47549ff2 100644 --- a/internal/k8sevents/watcher.go +++ b/internal/k8sevents/watcher.go @@ -126,3 +126,16 @@ func (w *watcher) watchEvents(client rest.Interface) { func (w *watcher) close() { close(w.stopCh) } + +func eventTimestamp(ev *corev1.Event) time.Time { + var ts time.Time + switch { + case ev.EventTime.Time != time.Time{}: + ts = ev.EventTime.Time + case ev.LastTimestamp.Time != time.Time{}: + ts = ev.LastTimestamp.Time + case ev.FirstTimestamp.Time != time.Time{}: + ts = ev.FirstTimestamp.Time + } + return ts +} diff --git a/internal/vector/aggregator/config.go b/internal/vector/aggregator/config.go index d800860e..48f0fe1d 100644 --- a/internal/vector/aggregator/config.go +++ b/internal/vector/aggregator/config.go @@ -9,7 +9,7 @@ import ( ) func (ctrl *Controller) ensureVectorAggregatorConfig(ctx context.Context) error { - log := log.FromContext(ctx).WithValues("vector-aggregator-secret", ctrl.VectorAggregator.Name) + log := log.FromContext(ctx).WithValues(ctrl.prefix()+"vector-aggregator-secret", ctrl.Name) log.Info("start Reconcile Vector Aggregator Secret") vectorAggregatorSecret, err := ctrl.createVectorAggregatorConfig(ctx) if err != nil { @@ -19,19 +19,19 @@ func (ctrl *Controller) ensureVectorAggregatorConfig(ctx context.Context) error } func (ctrl *Controller) createVectorAggregatorConfig(ctx context.Context) (*corev1.Secret, error) { - log := log.FromContext(ctx).WithValues("vector-aggregator-config", ctrl.VectorAggregator.Name) + log := log.FromContext(ctx).WithValues(ctrl.prefix()+"vector-aggregator-config", ctrl.Name) labels := ctrl.labelsForVectorAggregator() annotations := ctrl.annotationsForVectorAggregator() data := ctrl.ConfigBytes - if ctrl.VectorAggregator.Spec.CompressConfigFile { + if ctrl.Spec.CompressConfigFile { data = compression.Compress(ctrl.ConfigBytes, log) } config := map[string][]byte{ "config.json": data, } secret := &corev1.Secret{ - ObjectMeta: ctrl.objectMetaVectorAggregator(labels, annotations, ctrl.VectorAggregator.Namespace), + ObjectMeta: ctrl.objectMetaVectorAggregator(labels, annotations, ctrl.Namespace), Data: config, } return secret, nil diff --git a/internal/vector/aggregator/controller.go b/internal/vector/aggregator/controller.go index a68da92e..f7db4be1 100644 --- a/internal/vector/aggregator/controller.go +++ b/internal/vector/aggregator/controller.go @@ -10,6 +10,7 @@ import ( corev1 "k8s.io/api/core/v1" resourcev1 "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/client-go/kubernetes" "k8s.io/utils/ptr" @@ -17,17 +18,29 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" ) +type Aggregator interface { + client.Object +} + type Controller struct { client.Client - VectorAggregator *vectorv1alpha1.VectorAggregator - ConfigBytes []byte - Config *config.VectorConfig - ClientSet *kubernetes.Clientset - EventsCollector *k8sevents.EventsCollector + id string + Name string + Namespace string + VectorAggregator Aggregator + APIVersion string + Kind string + Spec *vectorv1alpha1.VectorAggregatorCommon + Status *vectorv1alpha1.VectorCommonStatus + ConfigBytes []byte + Config *config.VectorConfig + ClientSet *kubernetes.Clientset + EventsCollector *k8sevents.EventsCollector + isClusterAggregator bool } func NewController( - v *vectorv1alpha1.VectorAggregator, + v Aggregator, c client.Client, cs *kubernetes.Clientset, evCollector *k8sevents.EventsCollector, @@ -38,12 +51,34 @@ func NewController( ClientSet: cs, EventsCollector: evCollector, } + + switch agg := v.(type) { + case *vectorv1alpha1.VectorAggregator: + ctrl.isClusterAggregator = false + ctrl.Spec = &agg.Spec.VectorAggregatorCommon + ctrl.Name = agg.Name + ctrl.Namespace = agg.Namespace + ctrl.Status = &agg.Status.VectorCommonStatus + ctrl.APIVersion = agg.APIVersion + ctrl.Kind = agg.Kind + ctrl.id = types.NamespacedName{Name: agg.Name, Namespace: agg.Namespace}.String() + case *vectorv1alpha1.ClusterVectorAggregator: + ctrl.isClusterAggregator = true + ctrl.Spec = &agg.Spec.VectorAggregatorCommon + ctrl.Name = agg.Name + ctrl.Namespace = agg.Spec.ResourceNamespace + ctrl.Status = &agg.Status.VectorCommonStatus + ctrl.APIVersion = agg.APIVersion + ctrl.Kind = agg.Kind + ctrl.id = types.NamespacedName{Name: agg.Name}.String() + } + ctrl.setDefault() return ctrl } func (ctrl *Controller) EnsureVectorAggregator(ctx context.Context) error { - log := log.FromContext(ctx).WithValues("vector-aggregator", ctrl.VectorAggregator.Name) + log := log.FromContext(ctx).WithValues(ctrl.prefix()+"vector-aggregator", ctrl.Name) log.Info("start Reconcile Vector Aggregator") monitoringCRD, err := k8s.ResourceExists(ctrl.ClientSet.Discovery(), monitorv1.SchemeGroupVersion.String(), monitorv1.PodMonitorsKind) @@ -63,7 +98,7 @@ func (ctrl *Controller) EnsureVectorAggregator(ctx context.Context) error { return err } - if ctrl.VectorAggregator.Spec.InternalMetrics && monitoringCRD { + if ctrl.Spec.InternalMetrics && monitoringCRD { if err := ctrl.ensureVectorAggregatorPodMonitor(ctx); err != nil { return err } @@ -86,29 +121,29 @@ func (ctrl *Controller) DeleteVectorAggregator(ctx context.Context) error { } func (ctrl *Controller) setDefault() { - if ctrl.VectorAggregator.Spec.Image == "" { - ctrl.VectorAggregator.Spec.Image = "timberio/vector:0.28.1-distroless-libc" + if ctrl.Spec.Image == "" { + ctrl.Spec.Image = "timberio/vector:0.28.1-distroless-libc" } - if ctrl.VectorAggregator.Spec.Resources.Requests == nil { - ctrl.VectorAggregator.Spec.Resources.Requests = corev1.ResourceList{ + if ctrl.Spec.Resources.Requests == nil { + ctrl.Spec.Resources.Requests = corev1.ResourceList{ corev1.ResourceMemory: resourcev1.MustParse("200Mi"), corev1.ResourceCPU: resourcev1.MustParse("100m"), } } - if ctrl.VectorAggregator.Spec.Resources.Limits == nil { - ctrl.VectorAggregator.Spec.Resources.Limits = corev1.ResourceList{ + if ctrl.Spec.Resources.Limits == nil { + ctrl.Spec.Resources.Limits = corev1.ResourceList{ corev1.ResourceMemory: resourcev1.MustParse("1024Mi"), corev1.ResourceCPU: resourcev1.MustParse("1000m"), } } - if ctrl.VectorAggregator.Spec.DataDir == "" { - ctrl.VectorAggregator.Spec.DataDir = "/var/lib/vector" + if ctrl.Spec.DataDir == "" { + ctrl.Spec.DataDir = "/var/lib/vector" } - if ctrl.VectorAggregator.Spec.Volumes == nil { - ctrl.VectorAggregator.Spec.Volumes = []corev1.Volume{ + if ctrl.Spec.Volumes == nil { + ctrl.Spec.Volumes = []corev1.Volume{ { Name: "var-log", VolumeSource: corev1.VolumeSource{ @@ -136,8 +171,8 @@ func (ctrl *Controller) setDefault() { } } - if ctrl.VectorAggregator.Spec.ReadinessProbe == nil && ctrl.VectorAggregator.Spec.Api.Enabled && ctrl.VectorAggregator.Spec.Api.Healthcheck { - ctrl.VectorAggregator.Spec.ReadinessProbe = &corev1.Probe{ + if ctrl.Spec.ReadinessProbe == nil && ctrl.Spec.Api.Enabled && ctrl.Spec.Api.Healthcheck { + ctrl.Spec.ReadinessProbe = &corev1.Probe{ ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Path: "/health", @@ -154,8 +189,8 @@ func (ctrl *Controller) setDefault() { FailureThreshold: 0, } } - if ctrl.VectorAggregator.Spec.LivenessProbe == nil && ctrl.VectorAggregator.Spec.Api.Enabled && ctrl.VectorAggregator.Spec.Api.Healthcheck { - ctrl.VectorAggregator.Spec.LivenessProbe = &corev1.Probe{ + if ctrl.Spec.LivenessProbe == nil && ctrl.Spec.Api.Enabled && ctrl.Spec.Api.Healthcheck { + ctrl.Spec.LivenessProbe = &corev1.Probe{ ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Path: "/health", @@ -173,8 +208,8 @@ func (ctrl *Controller) setDefault() { } } - if ctrl.VectorAggregator.Spec.VolumeMounts == nil { - ctrl.VectorAggregator.Spec.VolumeMounts = []corev1.VolumeMount{ + if ctrl.Spec.VolumeMounts == nil { + ctrl.Spec.VolumeMounts = []corev1.VolumeMount{ { Name: "var-log", MountPath: "/var/log/", @@ -189,17 +224,17 @@ func (ctrl *Controller) setDefault() { }, } } - if ctrl.VectorAggregator.Spec.CompressConfigFile && ctrl.VectorAggregator.Spec.ConfigReloaderImage == "" { - ctrl.VectorAggregator.Spec.ConfigReloaderImage = "docker.io/kaasops/config-reloader:v0.1.4" + if ctrl.Spec.CompressConfigFile && ctrl.Spec.ConfigReloaderImage == "" { + ctrl.Spec.ConfigReloaderImage = "docker.io/kaasops/config-reloader:v0.1.4" } - if ctrl.VectorAggregator.Spec.CompressConfigFile && ctrl.VectorAggregator.Spec.ConfigReloaderResources.Requests == nil { - ctrl.VectorAggregator.Spec.ConfigReloaderResources.Requests = corev1.ResourceList{ + if ctrl.Spec.CompressConfigFile && ctrl.Spec.ConfigReloaderResources.Requests == nil { + ctrl.Spec.ConfigReloaderResources.Requests = corev1.ResourceList{ corev1.ResourceMemory: resourcev1.MustParse("200Mi"), corev1.ResourceCPU: resourcev1.MustParse("100m"), } } - if ctrl.VectorAggregator.Spec.CompressConfigFile && ctrl.VectorAggregator.Spec.ConfigReloaderResources.Limits == nil { - ctrl.VectorAggregator.Spec.ConfigReloaderResources.Limits = corev1.ResourceList{ + if ctrl.Spec.CompressConfigFile && ctrl.Spec.ConfigReloaderResources.Limits == nil { + ctrl.Spec.ConfigReloaderResources.Limits = corev1.ResourceList{ corev1.ResourceMemory: resourcev1.MustParse("1024Mi"), corev1.ResourceCPU: resourcev1.MustParse("1000m"), } @@ -208,16 +243,16 @@ func (ctrl *Controller) setDefault() { func (ctrl *Controller) SetSuccessStatus(ctx context.Context, hash *uint32) error { var status = true - ctrl.VectorAggregator.Status.ConfigCheckResult = &status - ctrl.VectorAggregator.Status.Reason = nil - ctrl.VectorAggregator.Status.LastAppliedConfigHash = hash + ctrl.Status.ConfigCheckResult = &status + ctrl.Status.Reason = nil + ctrl.Status.LastAppliedConfigHash = hash return k8s.UpdateStatus(ctx, ctrl.VectorAggregator, ctrl.Client) } func (ctrl *Controller) SetFailedStatus(ctx context.Context, reason string) error { var status = false - ctrl.VectorAggregator.Status.ConfigCheckResult = &status - ctrl.VectorAggregator.Status.Reason = &reason + ctrl.Status.ConfigCheckResult = &status + ctrl.Status.Reason = &reason return k8s.UpdateStatus(ctx, ctrl.VectorAggregator, ctrl.Client) } @@ -226,12 +261,12 @@ func (ctrl *Controller) labelsForVectorAggregator() map[string]string { k8s.ManagedByLabelKey: "vector-operator", k8s.NameLabelKey: "vector", k8s.ComponentLabelKey: "Aggregator", - k8s.InstanceLabelKey: ctrl.VectorAggregator.Name, + k8s.InstanceLabelKey: ctrl.Name, } } func (ctrl *Controller) annotationsForVectorAggregator() map[string]string { - return ctrl.VectorAggregator.Spec.Annotations + return ctrl.Spec.Annotations } func (ctrl *Controller) objectMetaVectorAggregator(labels map[string]string, annotations map[string]string, namespace string) metav1.ObjectMeta { @@ -245,15 +280,15 @@ func (ctrl *Controller) objectMetaVectorAggregator(labels map[string]string, ann } func (ctrl *Controller) getNameVectorAggregator() string { - name := ctrl.VectorAggregator.Name + "-aggregator" + name := ctrl.Name + "-aggregator" return name } func (ctrl *Controller) getControllerReference() []metav1.OwnerReference { return []metav1.OwnerReference{ { - APIVersion: ctrl.VectorAggregator.APIVersion, - Kind: ctrl.VectorAggregator.Kind, + APIVersion: ctrl.APIVersion, + Kind: ctrl.Kind, Name: ctrl.VectorAggregator.GetName(), UID: ctrl.VectorAggregator.GetUID(), BlockOwnerDeletion: ptr.To(true), @@ -265,3 +300,10 @@ func (ctrl *Controller) getControllerReference() []metav1.OwnerReference { func (ctrl *Controller) GetServiceName() string { return ctrl.getNameVectorAggregator() } + +func (ctrl *Controller) prefix() string { + if ctrl.isClusterAggregator { + return "cluster-" + } + return "" +} diff --git a/internal/vector/aggregator/deployment.go b/internal/vector/aggregator/deployment.go index e434f21c..bdbb63ea 100644 --- a/internal/vector/aggregator/deployment.go +++ b/internal/vector/aggregator/deployment.go @@ -10,7 +10,7 @@ import ( ) func (ctrl *Controller) ensureVectorAggregatorDeployment(ctx context.Context) error { - log := log.FromContext(ctx).WithValues("vector-aggregator-deployment", ctrl.VectorAggregator.Name) + log := log.FromContext(ctx).WithValues(ctrl.prefix()+"vector-aggregator-deployment", ctrl.Name) log.Info("start Reconcile Vector Aggregator Deployment") return k8s.CreateOrUpdateResource(ctx, ctrl.createVectorAggregatorDeployment(), ctrl.Client) } @@ -22,33 +22,33 @@ func (ctrl *Controller) createVectorAggregatorDeployment() *appsv1.Deployment { var containers []corev1.Container containers = append(containers, *ctrl.VectorAggregatorContainer()) - if ctrl.VectorAggregator.Spec.CompressConfigFile { + if ctrl.Spec.CompressConfigFile { initContainers = append(initContainers, *ctrl.ConfigReloaderInitContainer()) } - if ctrl.VectorAggregator.Spec.CompressConfigFile { + if ctrl.Spec.CompressConfigFile { containers = append(containers, *ctrl.ConfigReloaderSidecarContainer()) } deployment := &appsv1.Deployment{ - ObjectMeta: ctrl.objectMetaVectorAggregator(labels, annotations, ctrl.VectorAggregator.Namespace), + ObjectMeta: ctrl.objectMetaVectorAggregator(labels, annotations, ctrl.Namespace), Spec: appsv1.DeploymentSpec{ Selector: &metav1.LabelSelector{MatchLabels: labels}, - Replicas: &ctrl.VectorAggregator.Spec.Replicas, + Replicas: &ctrl.Spec.Replicas, Template: corev1.PodTemplateSpec{ - ObjectMeta: ctrl.objectMetaVectorAggregator(labels, annotations, ctrl.VectorAggregator.Namespace), + ObjectMeta: ctrl.objectMetaVectorAggregator(labels, annotations, ctrl.Namespace), Spec: corev1.PodSpec{ ServiceAccountName: ctrl.getNameVectorAggregator(), Volumes: ctrl.generateVectorAggregatorVolume(), - SecurityContext: ctrl.VectorAggregator.Spec.SecurityContext, - ImagePullSecrets: ctrl.VectorAggregator.Spec.ImagePullSecrets, - Affinity: ctrl.VectorAggregator.Spec.Affinity, - RuntimeClassName: ctrl.VectorAggregator.Spec.RuntimeClassName, - SchedulerName: ctrl.VectorAggregator.Spec.SchedulerName, - Tolerations: ctrl.VectorAggregator.Spec.Tolerations, - PriorityClassName: ctrl.VectorAggregator.Spec.PodSecurityPolicyName, - HostNetwork: ctrl.VectorAggregator.Spec.HostNetwork, - HostAliases: ctrl.VectorAggregator.Spec.HostAliases, + SecurityContext: ctrl.Spec.SecurityContext, + ImagePullSecrets: ctrl.Spec.ImagePullSecrets, + Affinity: ctrl.Spec.Affinity, + RuntimeClassName: ctrl.Spec.RuntimeClassName, + SchedulerName: ctrl.Spec.SchedulerName, + Tolerations: ctrl.Spec.Tolerations, + PriorityClassName: ctrl.Spec.PodSecurityPolicyName, + HostNetwork: ctrl.Spec.HostNetwork, + HostAliases: ctrl.Spec.HostAliases, InitContainers: initContainers, Containers: containers, }, @@ -62,7 +62,7 @@ func (ctrl *Controller) createVectorAggregatorDeployment() *appsv1.Deployment { func (ctrl *Controller) VectorAggregatorContainer() *corev1.Container { return &corev1.Container{ Name: ctrl.getNameVectorAggregator(), - Image: ctrl.VectorAggregator.Spec.Image, + Image: ctrl.Spec.Image, Args: []string{"--config-dir", "/etc/vector", "--watch-config"}, Env: ctrl.generateVectorAggregatorEnvs(), Ports: []corev1.ContainerPort{ @@ -73,21 +73,21 @@ func (ctrl *Controller) VectorAggregatorContainer() *corev1.Container { }, }, VolumeMounts: ctrl.generateVectorAggregatorVolumeMounts(), - ReadinessProbe: ctrl.VectorAggregator.Spec.ReadinessProbe, - LivenessProbe: ctrl.VectorAggregator.Spec.LivenessProbe, - Resources: ctrl.VectorAggregator.Spec.Resources, - SecurityContext: ctrl.VectorAggregator.Spec.ContainerSecurityContext, - ImagePullPolicy: ctrl.VectorAggregator.Spec.ImagePullPolicy, + ReadinessProbe: ctrl.Spec.ReadinessProbe, + LivenessProbe: ctrl.Spec.LivenessProbe, + Resources: ctrl.Spec.Resources, + SecurityContext: ctrl.Spec.ContainerSecurityContext, + ImagePullPolicy: ctrl.Spec.ImagePullPolicy, } } func (ctrl *Controller) ConfigReloaderInitContainer() *corev1.Container { return &corev1.Container{ Name: "init-config-reloader", - Image: ctrl.VectorAggregator.Spec.ConfigReloaderImage, - ImagePullPolicy: ctrl.VectorAggregator.Spec.ImagePullPolicy, - Resources: ctrl.VectorAggregator.Spec.ConfigReloaderResources, - SecurityContext: ctrl.VectorAggregator.Spec.ContainerSecurityContext, + Image: ctrl.Spec.ConfigReloaderImage, + ImagePullPolicy: ctrl.Spec.ImagePullPolicy, + Resources: ctrl.Spec.ConfigReloaderResources, + SecurityContext: ctrl.Spec.ContainerSecurityContext, Args: []string{ "--init-mode=true", "--volume-dir-archive=/tmp/archive", @@ -109,10 +109,10 @@ func (ctrl *Controller) ConfigReloaderInitContainer() *corev1.Container { func (ctrl *Controller) ConfigReloaderSidecarContainer() *corev1.Container { return &corev1.Container{ Name: "config-reloader", - Image: ctrl.VectorAggregator.Spec.ConfigReloaderImage, - ImagePullPolicy: ctrl.VectorAggregator.Spec.ImagePullPolicy, - Resources: ctrl.VectorAggregator.Spec.ConfigReloaderResources, - SecurityContext: ctrl.VectorAggregator.Spec.ContainerSecurityContext, + Image: ctrl.Spec.ConfigReloaderImage, + ImagePullPolicy: ctrl.Spec.ImagePullPolicy, + Resources: ctrl.Spec.ConfigReloaderResources, + SecurityContext: ctrl.Spec.ContainerSecurityContext, Args: []string{ "--init-mode=false", "--volume-dir-archive=/tmp/archive", @@ -132,13 +132,13 @@ func (ctrl *Controller) ConfigReloaderSidecarContainer() *corev1.Container { } func (ctrl *Controller) generateVectorAggregatorVolume() []corev1.Volume { - volume := ctrl.VectorAggregator.Spec.Volumes + volume := ctrl.Spec.Volumes configVolumeSource := corev1.VolumeSource{ Secret: &corev1.SecretVolumeSource{ SecretName: ctrl.getNameVectorAggregator(), }, } - if ctrl.VectorAggregator.Spec.CompressConfigFile { + if ctrl.Spec.CompressConfigFile { configVolumeSource = corev1.VolumeSource{ EmptyDir: &corev1.EmptyDirVolumeSource{}, } @@ -153,7 +153,7 @@ func (ctrl *Controller) generateVectorAggregatorVolume() []corev1.Volume { Name: "data", VolumeSource: corev1.VolumeSource{ HostPath: &corev1.HostPathVolumeSource{ - Path: ctrl.VectorAggregator.Spec.DataDir, + Path: ctrl.Spec.DataDir, }, }, }, @@ -175,7 +175,7 @@ func (ctrl *Controller) generateVectorAggregatorVolume() []corev1.Volume { }, }...) - if ctrl.VectorAggregator.Spec.CompressConfigFile { + if ctrl.Spec.CompressConfigFile { volume = append(volume, corev1.Volume{ Name: "app-config-compress", VolumeSource: corev1.VolumeSource{ @@ -190,7 +190,7 @@ func (ctrl *Controller) generateVectorAggregatorVolume() []corev1.Volume { } func (ctrl *Controller) generateVectorAggregatorVolumeMounts() []corev1.VolumeMount { - volumeMount := ctrl.VectorAggregator.Spec.VolumeMounts + volumeMount := ctrl.Spec.VolumeMounts volumeMount = append(volumeMount, []corev1.VolumeMount{ { @@ -211,7 +211,7 @@ func (ctrl *Controller) generateVectorAggregatorVolumeMounts() []corev1.VolumeMo }, }...) - if ctrl.VectorAggregator.Spec.CompressConfigFile { + if ctrl.Spec.CompressConfigFile { volumeMount = append(volumeMount, []corev1.VolumeMount{ { Name: "app-config-compress", @@ -224,7 +224,7 @@ func (ctrl *Controller) generateVectorAggregatorVolumeMounts() []corev1.VolumeMo } func (ctrl *Controller) generateVectorAggregatorEnvs() []corev1.EnvVar { - envs := ctrl.VectorAggregator.Spec.Env + envs := ctrl.Spec.Env envs = append(envs, []corev1.EnvVar{ { diff --git a/internal/vector/aggregator/podmonitor.go b/internal/vector/aggregator/podmonitor.go index cc067d2f..48ec9ea4 100644 --- a/internal/vector/aggregator/podmonitor.go +++ b/internal/vector/aggregator/podmonitor.go @@ -9,7 +9,7 @@ import ( ) func (ctrl *Controller) ensureVectorAggregatorPodMonitor(ctx context.Context) error { - log := log.FromContext(ctx).WithValues("vector-aggregator-podmonitor", ctrl.VectorAggregator.Name) + log := log.FromContext(ctx).WithValues(ctrl.prefix()+"vector-aggregator-podmonitor", ctrl.Name) log.Info("start Reconcile Vector Aggregator PodMonitor") vectorAggregatorPodMonitor := ctrl.createVectorAggregatorPodMonitor() return k8s.CreateOrUpdateResource(ctx, vectorAggregatorPodMonitor, ctrl.Client) @@ -20,7 +20,7 @@ func (ctrl *Controller) createVectorAggregatorPodMonitor() *monitorv1.PodMonitor annotations := ctrl.annotationsForVectorAggregator() podmonitor := &monitorv1.PodMonitor{ - ObjectMeta: ctrl.objectMetaVectorAggregator(labels, annotations, ctrl.VectorAggregator.Namespace), + ObjectMeta: ctrl.objectMetaVectorAggregator(labels, annotations, ctrl.Namespace), Spec: monitorv1.PodMonitorSpec{ PodMetricsEndpoints: []monitorv1.PodMetricsEndpoint{ { diff --git a/internal/vector/aggregator/rbac.go b/internal/vector/aggregator/rbac.go index 3f52da6b..c5bf2a21 100644 --- a/internal/vector/aggregator/rbac.go +++ b/internal/vector/aggregator/rbac.go @@ -11,7 +11,7 @@ import ( const ApiPort = 8686 func (ctrl *Controller) ensureVectorAggregatorRBAC(ctx context.Context) error { - log := log.FromContext(ctx).WithValues("vector-aggregator-rbac", ctrl.VectorAggregator.Name) + log := log.FromContext(ctx).WithValues(ctrl.prefix()+"vector-aggregator-rbac", ctrl.Name) log.Info("start Reconcile Vector Aggregator RBAC") @@ -44,7 +44,7 @@ func (ctrl *Controller) createVectorAggregatorServiceAccount() *corev1.ServiceAc annotations := ctrl.annotationsForVectorAggregator() serviceAccount := &corev1.ServiceAccount{ - ObjectMeta: ctrl.objectMetaVectorAggregator(labels, annotations, ctrl.VectorAggregator.Namespace), + ObjectMeta: ctrl.objectMetaVectorAggregator(labels, annotations, ctrl.Namespace), } return serviceAccount @@ -83,7 +83,7 @@ func (ctrl *Controller) createVectorAggregatorClusterRoleBinding() *rbacv1.Clust { Kind: "ServiceAccount", Name: ctrl.getNameVectorAggregator(), - Namespace: ctrl.VectorAggregator.Namespace, + Namespace: ctrl.Namespace, }, }, } diff --git a/internal/vector/aggregator/service.go b/internal/vector/aggregator/service.go index a119e951..8bdfdf35 100644 --- a/internal/vector/aggregator/service.go +++ b/internal/vector/aggregator/service.go @@ -16,7 +16,7 @@ import ( ) func (ctrl *Controller) ensureVectorAggregatorService(ctx context.Context) error { - log := log.FromContext(ctx).WithValues("vector-aggregator-service", ctrl.VectorAggregator.Name) + log := log.FromContext(ctx).WithValues(ctrl.prefix()+"vector-aggregator-service", ctrl.Name) log.Info("start Reconcile Vector Aggregator Service") existing, err := ctrl.getExistingServices(ctx) if err != nil { @@ -33,6 +33,7 @@ func (ctrl *Controller) ensureVectorAggregatorService(ctx context.Context) error } if svc.Annotations[common.AnnotationK8sEventsPort] != "" { ctrl.EventsCollector.RegisterSubscriber( + ctrl.id, svc.Name, svc.Namespace, svc.Annotations[common.AnnotationK8sEventsPort], @@ -42,7 +43,7 @@ func (ctrl *Controller) ensureVectorAggregatorService(ctx context.Context) error } for _, svc := range existing { if svc.Annotations[common.AnnotationK8sEventsPort] != "" { - ctrl.EventsCollector.UnregisterSubscriber(svc.Name, svc.Namespace) + ctrl.EventsCollector.UnregisterSubscriber(ctrl.id, svc.Name, svc.Namespace) } if err := ctrl.Client.Delete(ctx, svc); err != nil { return err @@ -79,7 +80,7 @@ func (ctrl *Controller) createVectorAggregatorServices() ([]*corev1.Service, err }) } svc := &corev1.Service{ - ObjectMeta: ctrl.objectMetaVectorAggregator(labels, ann, ctrl.VectorAggregator.Namespace), + ObjectMeta: ctrl.objectMetaVectorAggregator(labels, ann, ctrl.Namespace), Spec: corev1.ServiceSpec{ Ports: ports, Selector: labels, @@ -93,9 +94,9 @@ func (ctrl *Controller) createVectorAggregatorServices() ([]*corev1.Service, err svcList = append(svcList, svc) } - if ctrl.VectorAggregator.Spec.Api.Enabled { + if ctrl.Spec.Api.Enabled { svcList = append(svcList, &corev1.Service{ - ObjectMeta: ctrl.objectMetaVectorAggregator(labels, annotations, ctrl.VectorAggregator.Namespace), + ObjectMeta: ctrl.objectMetaVectorAggregator(labels, annotations, ctrl.Namespace), Spec: corev1.ServiceSpec{ Ports: []corev1.ServicePort{ { @@ -116,7 +117,7 @@ func (ctrl *Controller) createVectorAggregatorServices() ([]*corev1.Service, err func (ctrl *Controller) getExistingServices(ctx context.Context) (map[string]*corev1.Service, error) { svcList := corev1.ServiceList{} opts := &client.ListOptions{ - Namespace: ctrl.VectorAggregator.Namespace, + Namespace: ctrl.Namespace, LabelSelector: labels.Set(ctrl.labelsForVectorAggregator()).AsSelector(), } err := ctrl.Client.List(ctx, &svcList, opts) From 4a2a94ad1bb69548b840904c7e8faff9deea5d82 Mon Sep 17 00:00:00 2001 From: aa1ex <115736127+aa1ex@users.noreply.github.com> Date: Fri, 4 Oct 2024 15:04:11 +0300 Subject: [PATCH 273/316] update ci (#153) add pre-v* to github action tags Signed-off-by: Aleksandr Aleksandrov --- .github/workflows/build-image.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-image.yaml b/.github/workflows/build-image.yaml index 13a9cca8..ff8b6c7e 100644 --- a/.github/workflows/build-image.yaml +++ b/.github/workflows/build-image.yaml @@ -5,6 +5,7 @@ on: push: tags: - 'v*' + - 'pre-v*' jobs: build-and-push-docker-image: name: Build and push Docker image From 17c73c442c56c87b33886b5bdb728d765d36066d Mon Sep 17 00:00:00 2001 From: zvlb Date: Mon, 7 Oct 2024 10:58:34 +0300 Subject: [PATCH 274/316] init helm chart for 0.1.0-rc Signed-off-by: zvlb --- helm/charts/vector-operator/Chart.yaml | 4 +- ...y.kaasops.io_clustervectoraggregators.yaml | 4968 +++++++++++++++++ ...ity.kaasops.io_clustervectorpipelines.yaml | 25 +- ...vability.kaasops.io_vectoraggregators.yaml | 4961 ++++++++++++++++ ...ervability.kaasops.io_vectorpipelines.yaml | 25 +- .../observability.kaasops.io_vectors.yaml | 4486 ++++++++------- .../templates/clusterrole.yaml | 5 + helm/index.yaml | 87 +- helm/packages/vector-operator-0.1.0-rc1.tgz | Bin 0 -> 99683 bytes 9 files changed, 12516 insertions(+), 2045 deletions(-) create mode 100644 helm/charts/vector-operator/crds/observability.kaasops.io_clustervectoraggregators.yaml create mode 100644 helm/charts/vector-operator/crds/observability.kaasops.io_vectoraggregators.yaml create mode 100644 helm/packages/vector-operator-0.1.0-rc1.tgz diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index 3aac2609..941d5d4a 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.40 +version: 0.1.0-rc1 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.0.40" +appVersion: "pre-v0.1.0-r1" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectoraggregators.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectoraggregators.yaml new file mode 100644 index 00000000..49463c7b --- /dev/null +++ b/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectoraggregators.yaml @@ -0,0 +1,4968 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: clustervectoraggregators.observability.kaasops.io +spec: + group: observability.kaasops.io + names: + kind: ClusterVectorAggregator + listKind: ClusterVectorAggregatorList + plural: clustervectoraggregators + singular: clustervectoraggregator + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.configCheckResult + name: Valid + type: boolean + name: v1alpha1 + schema: + openAPIV3Schema: + description: ClusterVectorAggregator is the Schema for the clustervectoraggregators + API + 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: + type: object + spec: + description: ClusterVectorAggregatorSpec defines the desired state of + ClusterVectorAggregator + properties: + affinity: + description: Affinity If specified, the pod's scheduling constraints. + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for the + pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. + items: + description: |- + An empty preferred scheduling term matches all objects with implicit weight 0 + (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated with the + corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + description: Weight associated with matching the corresponding + nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. + The terms are ORed. + items: + description: |- + A null or empty node selector term matches no objects. The requirements of + them are ANDed. + The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate + this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules (e.g. + avoid putting this pod in the same node, zone, etc. as some + other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the anti-affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the anti-affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the anti-affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + annotations: + additionalProperties: + type: string + description: Annotations is an unstructured key value map stored with + a resource that may be set by external tools to store and retrieve + arbitrary metadata. + type: object + api: + description: |- + Vector API params. Allows to interact with a running Vector instance. + https://vector.dev/docs/reference/api/ + properties: + enabled: + type: boolean + healthcheck: + description: |- + Enable ReadinessProbe and LivenessProbe via api /health endpoint. + If probe enabled via VectorAgent or VectorAggregator, this setting will be ignored for that probe. + type: boolean + playground: + type: boolean + type: object + compressConfigFile: + description: 'Compress config file to fix: metadata.annotations: Too + long: must have at most 262144 characters' + type: boolean + configCheck: + description: Control params for ConfigCheck pods + properties: + affinity: + description: Affinity If specified, the pod's scheduling constraints. + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for + the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. + items: + description: |- + An empty preferred scheduling term matches all objects with implicit weight 0 + (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated with + the corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + description: Weight associated with matching the + corresponding nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. + The terms are ORed. + items: + description: |- + A null or empty node selector term matches no objects. The requirements of + them are ANDed. + The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. + co-locate this pod in the same node, zone, etc. as some + other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules + (e.g. avoid putting this pod in the same node, zone, etc. + as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the anti-affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the anti-affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the anti-affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + annotations: + additionalProperties: + type: string + description: Annotations is an unstructured key value map stored + with a resource that may be set by external tools to store and + retrieve arbitrary metadata. + type: object + disabled: + type: boolean + image: + description: |- + Image - docker image settings for Vector Agent + if no specified operator uses default config version + type: string + resources: + description: |- + Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + if not specified - default setting will be used + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + tolerations: + description: Tolerations If specified, the pod's tolerations. + items: + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . + properties: + effect: + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + type: object + configReloaderImage: + type: string + configReloaderResources: + description: ResourceRequirements describes the compute resource requirements. + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + containerSecurityContext: + description: |- + SecurityContext holds security configuration that will be applied to a container. + Some fields are present in both SecurityContext and PodSecurityContext. + When both are set, the values in SecurityContext take precedence. + properties: + allowPrivilegeEscalation: + description: |- + AllowPrivilegeEscalation controls whether a process can gain more + privileges than its parent process. This bool directly controls if + the no_new_privs flag will be set on the container process. + AllowPrivilegeEscalation is true always when the container is: + 1) run as Privileged + 2) has CAP_SYS_ADMIN + Note that this field cannot be set when spec.os.name is windows. + type: boolean + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by this container. If set, this profile + overrides the pod's appArmorProfile. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object + capabilities: + description: |- + The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container runtime. + Note that this field cannot be set when spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities type + type: string + type: array + x-kubernetes-list-type: atomic + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities type + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + description: |- + Run container in privileged mode. + Processes in privileged containers are essentially equivalent to root on the host. + Defaults to false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: |- + procMount denotes the type of proc mount to use for the containers. + The default value is Default which uses the container runtime defaults for + readonly paths and masked paths. + This requires the ProcMountType feature flag to be enabled. + Note that this field cannot be set when spec.os.name is windows. + type: string + readOnlyRootFilesystem: + description: |- + Whether this container has a read-only root filesystem. + Default is false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: |- + The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies to + the container. + type: string + role: + description: Role is a SELinux role label that applies to + the container. + type: string + type: + description: Type is a SELinux type label that applies to + the container. + type: string + user: + description: User is a SELinux user label that applies to + the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by this container. If seccomp options are + provided at both the pod & container level, the container options + override the pod options. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + windowsOptions: + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options from the PodSecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the GMSA + credential spec to use. + type: string + hostProcess: + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + dataDir: + description: |- + The directory used for persisting Vector state, such as on-disk buffers, file checkpoints, and more. Please make sure the Vector project has write permissions to this directory. + https://vector.dev/docs/reference/configuration/global-options/#data_dir + type: string + env: + description: Env that will be added to Vector pod + items: + description: EnvVar represents an environment variable present in + a Container. + properties: + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + type: string + value: + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". + type: string + valueFrom: + description: Source for the environment variable's value. Cannot + be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. + properties: + apiVersion: + description: Version of the schema the FieldPath is + written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified + API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the exposed + resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + host_aliases: + description: |- + HostAliases provides mapping between ip and hostnames, + that would be propagated to pod, + cannot be used with HostNetwork. + items: + description: |- + HostAlias holds the mapping between IP and hostnames that will be injected as an entry in the + pod's hosts file. + properties: + hostnames: + description: Hostnames for the above IP address. + items: + type: string + type: array + x-kubernetes-list-type: atomic + ip: + description: IP address of the host file entry. + type: string + required: + - ip + type: object + type: array + hostNetwork: + description: HostNetwork controls whether the pod may use the node + network namespace + type: boolean + image: + description: |- + Image - docker image settings for Vector Agent + if no specified operator uses default config version + type: string + imagePullPolicy: + description: ImagePullPolicy of pods + type: string + imagePullSecrets: + description: |- + ImagePullSecrets An optional list of references to secrets in the same namespace + to use for pulling images from registries + see http://kubernetes.io/docs/user-guide/images#specifying-imagepullsecrets-on-a-pod + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + type: array + internalMetrics: + description: Enable internal metrics exporter + type: boolean + livenessProbe: + description: Periodic probe of container liveness. Container will + be restarted if the probe fails. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + properties: + port: + description: Port number of the gRPC service. Number must + be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows + repeated headers. + items: + description: HTTPHeader describes a custom header to be + used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + podSecurityContext: + description: |- + SecurityContext holds pod-level security attributes and common container settings. + This defaults to the default PodSecurityContext. + properties: + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by the containers in this pod. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object + fsGroup: + description: |- + A special supplemental group that applies to all containers in a pod. + Some volume types allow the Kubelet to change the ownership of that volume + to be owned by the pod: + + 1. The owning GID will be the FSGroup + 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) + 3. The permission bits are OR'd with rw-rw---- + + If unset, the Kubelet will not modify the ownership and permissions of any volume. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + fsGroupChangePolicy: + description: |- + fsGroupChangePolicy defines behavior of changing ownership and permission of the volume + before being exposed inside Pod. This field will only apply to + volume types which support fsGroup based ownership(and permissions). + It will have no effect on ephemeral volume types such as: secret, configmaps + and emptydir. + Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used. + Note that this field cannot be set when spec.os.name is windows. + type: string + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: |- + The SELinux context to be applied to all containers. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in SecurityContext. If set in + both SecurityContext and PodSecurityContext, the value specified in SecurityContext + takes precedence for that container. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies to + the container. + type: string + role: + description: Role is a SELinux role label that applies to + the container. + type: string + type: + description: Type is a SELinux type label that applies to + the container. + type: string + user: + description: User is a SELinux user label that applies to + the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by the containers in this pod. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + supplementalGroups: + description: |- + A list of groups applied to the first process run in each container, in + addition to the container's primary GID and fsGroup (if specified). If + the SupplementalGroupsPolicy feature is enabled, the + supplementalGroupsPolicy field determines whether these are in addition + to or instead of any group memberships defined in the container image. + If unspecified, no additional groups are added, though group memberships + defined in the container image may still be used, depending on the + supplementalGroupsPolicy field. + Note that this field cannot be set when spec.os.name is windows. + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + description: |- + Defines how supplemental groups of the first container processes are calculated. + Valid values are "Merge" and "Strict". If not specified, "Merge" is used. + (Alpha) Using the field requires the SupplementalGroupsPolicy feature gate to be enabled + and the container runtime must implement support for this feature. + Note that this field cannot be set when spec.os.name is windows. + type: string + sysctls: + description: |- + Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported + sysctls (by the container runtime) might fail to launch. + Note that this field cannot be set when spec.os.name is windows. + items: + description: Sysctl defines a kernel parameter to be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options within a container's SecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the GMSA + credential spec to use. + type: string + hostProcess: + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + podSecurityPolicyName: + description: |- + PodSecurityPolicyName - defines name for podSecurityPolicy + in case of empty value, prefixedName will be used. + type: string + priorityClassName: + description: PriorityClassName assigned to the Pods + type: string + readinessProbe: + description: Periodic probe of container service readiness. Container + will be removed from service endpoints if the probe fails. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + properties: + port: + description: Port number of the gRPC service. Number must + be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows + repeated headers. + items: + description: HTTPHeader describes a custom header to be + used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + replicas: + format: int32 + type: integer + resourceNamespace: + description: ResourceNamespace specifies the namespace where the related + resources, such as Deployments and Services, will be deployed. + type: string + resources: + description: |- + Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + if not specified - default setting will be used + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + runtimeClassName: + description: |- + RuntimeClassName - defines runtime class for kubernetes pod. + https://kubernetes.io/docs/concepts/containers/runtime-class/ + type: string + schedulerName: + description: SchedulerName - defines kubernetes scheduler name + type: string + selector: + description: |- + Selector defines a filter for the Vector Pipeline and Cluster Vector Pipeline by labels. + If not specified, all pipelines will be selected. + properties: + matchLabels: + additionalProperties: + type: string + type: object + type: object + tolerations: + description: Tolerations If specified, the pod's tolerations. + items: + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . + properties: + effect: + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + volumeMounts: + description: Pod volumes to mount into the container's filesystem. + items: + description: VolumeMount describes a mounting of a Volume within + a container. + properties: + mountPath: + description: |- + Path within the container at which the volume should be mounted. Must + not contain ':'. + type: string + mountPropagation: + description: |- + mountPropagation determines how mounts are propagated from the host + to container and the other way around. + When not set, MountPropagationNone is used. + This field is beta in 1.10. + When RecursiveReadOnly is set to IfPossible or to Enabled, MountPropagation must be None or unspecified + (which defaults to None). + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: |- + Mounted read-only if true, read-write otherwise (false or unspecified). + Defaults to false. + type: boolean + recursiveReadOnly: + description: |- + RecursiveReadOnly specifies whether read-only mounts should be handled + recursively. + + If ReadOnly is false, this field has no meaning and must be unspecified. + + If ReadOnly is true, and this field is set to Disabled, the mount is not made + recursively read-only. If this field is set to IfPossible, the mount is made + recursively read-only, if it is supported by the container runtime. If this + field is set to Enabled, the mount is made recursively read-only if it is + supported by the container runtime, otherwise the pod will not be started and + an error will be generated to indicate the reason. + + If this field is set to IfPossible or Enabled, MountPropagation must be set to + None (or be unspecified, which defaults to None). + + If this field is not specified, it is treated as an equivalent of Disabled. + type: string + subPath: + description: |- + Path within the volume from which the container's volume should be mounted. + Defaults to "" (volume's root). + type: string + subPathExpr: + description: |- + Expanded path within the volume from which the container's volume should be mounted. + Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. + Defaults to "" (volume's root). + SubPathExpr and SubPath are mutually exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + volumes: + description: List of volumes that can be mounted by containers belonging + to the pod. + items: + description: Volume represents a named volume in a pod that may + be accessed by any container in the pod. + properties: + awsElasticBlockStore: + description: |- + awsElasticBlockStore represents an AWS Disk resource that is attached to a + kubelet's host machine and then exposed to the pod. + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + properties: + fsType: + description: |- + fsType is the filesystem type of the volume that you want to mount. + Tip: Ensure that the filesystem type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + type: string + partition: + description: |- + partition is the partition in the volume that you want to mount. + If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition as "1". + Similarly, the volume partition for /dev/sda is "0" (or you can leave the property empty). + format: int32 + type: integer + readOnly: + description: |- + readOnly value true will force the readOnly setting in VolumeMounts. + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + type: boolean + volumeID: + description: |- + volumeID is unique ID of the persistent disk resource in AWS (Amazon EBS volume). + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + type: string + required: + - volumeID + type: object + azureDisk: + description: azureDisk represents an Azure Data Disk mount on + the host and bind mount to the pod. + properties: + cachingMode: + description: 'cachingMode is the Host Caching mode: None, + Read Only, Read Write.' + type: string + diskName: + description: diskName is the Name of the data disk in the + blob storage + type: string + diskURI: + description: diskURI is the URI of data disk in the blob + storage + type: string + fsType: + default: ext4 + description: |- + fsType is Filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + kind: + description: 'kind expected values are Shared: multiple + blob disks per storage account Dedicated: single blob + disk per storage account Managed: azure managed data + disk (only in managed availability set). defaults to shared' + type: string + readOnly: + default: false + description: |- + readOnly Defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + description: azureFile represents an Azure File Service mount + on the host and bind mount to the pod. + properties: + readOnly: + description: |- + readOnly defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + secretName: + description: secretName is the name of secret that contains + Azure Storage Account Name and Key + type: string + shareName: + description: shareName is the azure share Name + type: string + required: + - secretName + - shareName + type: object + cephfs: + description: cephFS represents a Ceph FS mount on the host that + shares a pod's lifetime + properties: + monitors: + description: |- + monitors is Required: Monitors is a collection of Ceph monitors + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it + items: + type: string + type: array + x-kubernetes-list-type: atomic + path: + description: 'path is Optional: Used as the mounted root, + rather than the full Ceph tree, default is /' + type: string + readOnly: + description: |- + readOnly is Optional: Defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it + type: boolean + secretFile: + description: |- + secretFile is Optional: SecretFile is the path to key ring for User, default is /etc/ceph/user.secret + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it + type: string + secretRef: + description: |- + secretRef is Optional: SecretRef is reference to the authentication secret for User, default is empty. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + user: + description: |- + user is optional: User is the rados user name, default is admin + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it + type: string + required: + - monitors + type: object + cinder: + description: |- + cinder represents a cinder volume attached and mounted on kubelets host machine. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md + properties: + fsType: + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md + type: string + readOnly: + description: |- + readOnly defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md + type: boolean + secretRef: + description: |- + secretRef is optional: points to a secret object containing parameters used to connect + to OpenStack. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + description: |- + volumeID used to identify the volume in cinder. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md + type: string + required: + - volumeID + type: object + configMap: + description: configMap represents a configMap that should populate + this volume + properties: + defaultMode: + description: |- + defaultMode is optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + ConfigMap will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: optional specify whether the ConfigMap or its + keys must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + description: csi (Container Storage Interface) represents ephemeral + storage that is handled by certain external CSI drivers (Beta + feature). + properties: + driver: + description: |- + driver is the name of the CSI driver that handles this volume. + Consult with your admin for the correct name as registered in the cluster. + type: string + fsType: + description: |- + fsType to mount. Ex. "ext4", "xfs", "ntfs". + If not provided, the empty value is passed to the associated CSI driver + which will determine the default filesystem to apply. + type: string + nodePublishSecretRef: + description: |- + nodePublishSecretRef is a reference to the secret object containing + sensitive information to pass to the CSI driver to complete the CSI + NodePublishVolume and NodeUnpublishVolume calls. + This field is optional, and may be empty if no secret is required. If the + secret object contains more than one secret, all secret references are passed. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + description: |- + readOnly specifies a read-only configuration for the volume. + Defaults to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: + type: string + description: |- + volumeAttributes stores driver-specific properties that are passed to the CSI + driver. Consult your driver's documentation for supported values. + type: object + required: + - driver + type: object + downwardAPI: + description: downwardAPI represents downward API about the pod + that should populate this volume + properties: + defaultMode: + description: |- + Optional: mode bits to use on created files by default. Must be a + Optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + items: + description: Items is a list of downward API volume file + items: + description: DownwardAPIVolumeFile represents information + to create the file containing the pod field + properties: + fieldRef: + description: 'Required: Selects a field of the pod: + only annotations, labels, name, namespace and uid + are supported.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + description: |- + Optional: mode bits used to set permissions on this file, must be an octal value + between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: 'Required: Path is the relative path + name of the file to be created. Must not be absolute + or contain the ''..'' path. Must be utf-8 encoded. + The first item of the relative path must not start + with ''..''' + type: string + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + emptyDir: + description: |- + emptyDir represents a temporary directory that shares a pod's lifetime. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir + properties: + medium: + description: |- + medium represents what type of storage medium should back this directory. + The default is "" which means to use the node's default medium. + Must be an empty string (default) or Memory. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + description: |- + sizeLimit is the total amount of local storage required for this EmptyDir volume. + The size limit is also applicable for memory medium. + The maximum usage on memory medium EmptyDir would be the minimum value between + the SizeLimit specified here and the sum of memory limits of all containers in a pod. + The default is nil which means that the limit is undefined. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + description: |- + ephemeral represents a volume that is handled by a cluster storage driver. + The volume's lifecycle is tied to the pod that defines it - it will be created before the pod starts, + and deleted when the pod is removed. + + Use this if: + a) the volume is only needed while the pod runs, + b) features of normal volumes like restoring from snapshot or capacity + tracking are needed, + c) the storage driver is specified through a storage class, and + d) the storage driver supports dynamic volume provisioning through + a PersistentVolumeClaim (see EphemeralVolumeSource for more + information on the connection between this volume type + and PersistentVolumeClaim). + + Use PersistentVolumeClaim or one of the vendor-specific + APIs for volumes that persist for longer than the lifecycle + of an individual pod. + + Use CSI for light-weight local ephemeral volumes if the CSI driver is meant to + be used that way - see the documentation of the driver for + more information. + + A pod can use both types of ephemeral volumes and + persistent volumes at the same time. + properties: + volumeClaimTemplate: + description: |- + Will be used to create a stand-alone PVC to provision the volume. + The pod in which this EphemeralVolumeSource is embedded will be the + owner of the PVC, i.e. the PVC will be deleted together with the + pod. The name of the PVC will be `-` where + `` is the name from the `PodSpec.Volumes` array + entry. Pod validation will reject the pod if the concatenated name + is not valid for a PVC (for example, too long). + + An existing PVC with that name that is not owned by the pod + will *not* be used for the pod to avoid using an unrelated + volume by mistake. Starting the pod is then blocked until + the unrelated PVC is removed. If such a pre-created PVC is + meant to be used by the pod, the PVC has to updated with an + owner reference to the pod once the pod exists. Normally + this should not be necessary, but it may be useful when + manually reconstructing a broken cluster. + + This field is read-only and no changes will be made by Kubernetes + to the PVC after it has been created. + + Required, must not be nil. + properties: + metadata: + description: |- + May contain labels and annotations that will be copied into the PVC + when creating it. No other fields are allowed and will be rejected during + validation. + type: object + spec: + description: |- + The specification for the PersistentVolumeClaim. The entire content is + copied unchanged into the PVC that gets created from this + template. The same fields as in a PersistentVolumeClaim + are also valid here. + properties: + accessModes: + description: |- + accessModes contains the desired access modes the volume should have. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1 + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + description: |- + dataSource field can be used to specify either: + * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) + * An existing PVC (PersistentVolumeClaim) + If the provisioner or an external controller can support the specified data source, + it will create a new volume based on the contents of the specified data source. + When the AnyVolumeDataSource feature gate is enabled, dataSource contents will be copied to dataSourceRef, + and dataSourceRef contents will be copied to dataSource when dataSourceRef.namespace is not specified. + If the namespace is specified, then dataSourceRef will not be copied to dataSource. + properties: + apiGroup: + description: |- + APIGroup is the group for the resource being referenced. + If APIGroup is not specified, the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource being + referenced + type: string + name: + description: Name is the name of resource being + referenced + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + description: |- + dataSourceRef specifies the object from which to populate the volume with data, if a non-empty + volume is desired. This may be any object from a non-empty API group (non + core object) or a PersistentVolumeClaim object. + When this field is specified, volume binding will only succeed if the type of + the specified object matches some installed volume populator or dynamic + provisioner. + This field will replace the functionality of the dataSource field and as such + if both fields are non-empty, they must have the same value. For backwards + compatibility, when namespace isn't specified in dataSourceRef, + both fields (dataSource and dataSourceRef) will be set to the same + value automatically if one of them is empty and the other is non-empty. + When namespace is specified in dataSourceRef, + dataSource isn't set to the same value and must be empty. + There are three important differences between dataSource and dataSourceRef: + * While dataSource only allows two specific types of objects, dataSourceRef + allows any non-core object, as well as PersistentVolumeClaim objects. + * While dataSource ignores disallowed values (dropping them), dataSourceRef + preserves all values, and generates an error if a disallowed value is + specified. + * While dataSource only allows local objects, dataSourceRef allows objects + in any namespaces. + (Beta) Using this field requires the AnyVolumeDataSource feature gate to be enabled. + (Alpha) Using the namespace field of dataSourceRef requires the CrossNamespaceVolumeDataSource feature gate to be enabled. + properties: + apiGroup: + description: |- + APIGroup is the group for the resource being referenced. + If APIGroup is not specified, the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource being + referenced + type: string + name: + description: Name is the name of resource being + referenced + type: string + namespace: + description: |- + Namespace is the namespace of resource being referenced + Note that when a namespace is specified, a gateway.networking.k8s.io/ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. + (Alpha) This field requires the CrossNamespaceVolumeDataSource feature gate to be enabled. + type: string + required: + - kind + - name + type: object + resources: + description: |- + resources represents the minimum resources the volume should have. + If RecoverVolumeExpansionFailure feature is enabled users are allowed to specify resource requirements + that are lower than previous value but must still be higher than capacity recorded in the + status field of the claim. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + selector: + description: selector is a label query over volumes + to consider for binding. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + description: |- + storageClassName is the name of the StorageClass required by the claim. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1 + type: string + volumeAttributesClassName: + description: |- + volumeAttributesClassName may be used to set the VolumeAttributesClass used by this claim. + If specified, the CSI driver will create or update the volume with the attributes defined + in the corresponding VolumeAttributesClass. This has a different purpose than storageClassName, + it can be changed after the claim is created. An empty string value means that no VolumeAttributesClass + will be applied to the claim but it's not allowed to reset this field to empty string once it is set. + If unspecified and the PersistentVolumeClaim is unbound, the default VolumeAttributesClass + will be set by the persistentvolume controller if it exists. + If the resource referred to by volumeAttributesClass does not exist, this PersistentVolumeClaim will be + set to a Pending state, as reflected by the modifyVolumeStatus field, until such as a resource + exists. + More info: https://kubernetes.io/docs/concepts/storage/volume-attributes-classes/ + (Beta) Using this field requires the VolumeAttributesClass feature gate to be enabled (off by default). + type: string + volumeMode: + description: |- + volumeMode defines what type of volume is required by the claim. + Value of Filesystem is implied when not included in claim spec. + type: string + volumeName: + description: volumeName is the binding reference + to the PersistentVolume backing this claim. + type: string + type: object + required: + - spec + type: object + type: object + fc: + description: fc represents a Fibre Channel resource that is + attached to a kubelet's host machine and then exposed to the + pod. + properties: + fsType: + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + lun: + description: 'lun is Optional: FC target lun number' + format: int32 + type: integer + readOnly: + description: |- + readOnly is Optional: Defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + targetWWNs: + description: 'targetWWNs is Optional: FC target worldwide + names (WWNs)' + items: + type: string + type: array + x-kubernetes-list-type: atomic + wwids: + description: |- + wwids Optional: FC volume world wide identifiers (wwids) + Either wwids or combination of targetWWNs and lun must be set, but not both simultaneously. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + flexVolume: + description: |- + flexVolume represents a generic volume resource that is + provisioned/attached using an exec based plugin. + properties: + driver: + description: driver is the name of the driver to use for + this volume. + type: string + fsType: + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". The default filesystem depends on FlexVolume script. + type: string + options: + additionalProperties: + type: string + description: 'options is Optional: this field holds extra + command options if any.' + type: object + readOnly: + description: |- + readOnly is Optional: defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: |- + secretRef is Optional: secretRef is reference to the secret object containing + sensitive information to pass to the plugin scripts. This may be + empty if no secret object is specified. If the secret object + contains more than one secret, all secrets are passed to the plugin + scripts. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + description: flocker represents a Flocker volume attached to + a kubelet's host machine. This depends on the Flocker control + service being running + properties: + datasetName: + description: |- + datasetName is Name of the dataset stored as metadata -> name on the dataset for Flocker + should be considered as deprecated + type: string + datasetUUID: + description: datasetUUID is the UUID of the dataset. This + is unique identifier of a Flocker dataset + type: string + type: object + gcePersistentDisk: + description: |- + gcePersistentDisk represents a GCE Disk resource that is attached to a + kubelet's host machine and then exposed to the pod. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + properties: + fsType: + description: |- + fsType is filesystem type of the volume that you want to mount. + Tip: Ensure that the filesystem type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + type: string + partition: + description: |- + partition is the partition in the volume that you want to mount. + If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition as "1". + Similarly, the volume partition for /dev/sda is "0" (or you can leave the property empty). + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + format: int32 + type: integer + pdName: + description: |- + pdName is unique name of the PD resource in GCE. Used to identify the disk in GCE. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + type: string + readOnly: + description: |- + readOnly here will force the ReadOnly setting in VolumeMounts. + Defaults to false. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + type: boolean + required: + - pdName + type: object + gitRepo: + description: |- + gitRepo represents a git repository at a particular revision. + DEPRECATED: GitRepo is deprecated. To provision a container with a git repo, mount an + EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir + into the Pod's container. + properties: + directory: + description: |- + directory is the target directory name. + Must not contain or start with '..'. If '.' is supplied, the volume directory will be the + git repository. Otherwise, if specified, the volume will contain the git repository in + the subdirectory with the given name. + type: string + repository: + description: repository is the URL + type: string + revision: + description: revision is the commit hash for the specified + revision. + type: string + required: + - repository + type: object + glusterfs: + description: |- + glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. + More info: https://examples.k8s.io/volumes/glusterfs/README.md + properties: + endpoints: + description: |- + endpoints is the endpoint name that details Glusterfs topology. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod + type: string + path: + description: |- + path is the Glusterfs volume path. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod + type: string + readOnly: + description: |- + readOnly here will force the Glusterfs volume to be mounted with read-only permissions. + Defaults to false. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod + type: boolean + required: + - endpoints + - path + type: object + hostPath: + description: |- + hostPath represents a pre-existing file or directory on the host + machine that is directly exposed to the container. This is generally + used for system agents or other privileged things that are allowed + to see the host machine. Most containers will NOT need this. + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + properties: + path: + description: |- + path of the directory on the host. + If the path is a symlink, it will follow the link to the real path. + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + type: string + type: + description: |- + type for HostPath Volume + Defaults to "" + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + type: string + required: + - path + type: object + image: + description: |- + image represents an OCI object (a container image or artifact) pulled and mounted on the kubelet's host machine. + The volume is resolved at pod startup depending on which PullPolicy value is provided: + + - Always: the kubelet always attempts to pull the reference. Container creation will fail If the pull fails. + - Never: the kubelet never pulls the reference and only uses a local image or artifact. Container creation will fail if the reference isn't present. + - IfNotPresent: the kubelet pulls if the reference isn't already present on disk. Container creation will fail if the reference isn't present and the pull fails. + + The volume gets re-resolved if the pod gets deleted and recreated, which means that new remote content will become available on pod recreation. + A failure to resolve or pull the image during pod startup will block containers from starting and may add significant latency. Failures will be retried using normal volume backoff and will be reported on the pod reason and message. + The types of objects that may be mounted by this volume are defined by the container runtime implementation on a host machine and at minimum must include all valid types supported by the container image field. + The OCI object gets mounted in a single directory (spec.containers[*].volumeMounts.mountPath) by merging the manifest layers in the same way as for container images. + The volume will be mounted read-only (ro) and non-executable files (noexec). + Sub path mounts for containers are not supported (spec.containers[*].volumeMounts.subpath). + The field spec.securityContext.fsGroupChangePolicy has no effect on this volume type. + properties: + pullPolicy: + description: |- + Policy for pulling OCI objects. Possible values are: + Always: the kubelet always attempts to pull the reference. Container creation will fail If the pull fails. + Never: the kubelet never pulls the reference and only uses a local image or artifact. Container creation will fail if the reference isn't present. + IfNotPresent: the kubelet pulls if the reference isn't already present on disk. Container creation will fail if the reference isn't present and the pull fails. + Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. + type: string + reference: + description: |- + Required: Image or artifact reference to be used. + Behaves in the same way as pod.spec.containers[*].image. + Pull secrets will be assembled in the same way as for the container image by looking up node credentials, SA image pull secrets, and pod spec image pull secrets. + More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config management to default or override + container images in workload controllers like Deployments and StatefulSets. + type: string + type: object + iscsi: + description: |- + iscsi represents an ISCSI Disk resource that is attached to a + kubelet's host machine and then exposed to the pod. + More info: https://examples.k8s.io/volumes/iscsi/README.md + properties: + chapAuthDiscovery: + description: chapAuthDiscovery defines whether support iSCSI + Discovery CHAP authentication + type: boolean + chapAuthSession: + description: chapAuthSession defines whether support iSCSI + Session CHAP authentication + type: boolean + fsType: + description: |- + fsType is the filesystem type of the volume that you want to mount. + Tip: Ensure that the filesystem type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi + type: string + initiatorName: + description: |- + initiatorName is the custom iSCSI Initiator Name. + If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface + : will be created for the connection. + type: string + iqn: + description: iqn is the target iSCSI Qualified Name. + type: string + iscsiInterface: + default: default + description: |- + iscsiInterface is the interface Name that uses an iSCSI transport. + Defaults to 'default' (tcp). + type: string + lun: + description: lun represents iSCSI Target Lun number. + format: int32 + type: integer + portals: + description: |- + portals is the iSCSI Target Portal List. The portal is either an IP or ip_addr:port if the port + is other than default (typically TCP ports 860 and 3260). + items: + type: string + type: array + x-kubernetes-list-type: atomic + readOnly: + description: |- + readOnly here will force the ReadOnly setting in VolumeMounts. + Defaults to false. + type: boolean + secretRef: + description: secretRef is the CHAP Secret for iSCSI target + and initiator authentication + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + description: |- + targetPortal is iSCSI Target Portal. The Portal is either an IP or ip_addr:port if the port + is other than default (typically TCP ports 860 and 3260). + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + description: |- + name of the volume. + Must be a DNS_LABEL and unique within the pod. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + nfs: + description: |- + nfs represents an NFS mount on the host that shares a pod's lifetime + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs + properties: + path: + description: |- + path that is exported by the NFS server. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs + type: string + readOnly: + description: |- + readOnly here will force the NFS export to be mounted with read-only permissions. + Defaults to false. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs + type: boolean + server: + description: |- + server is the hostname or IP address of the NFS server. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + description: |- + persistentVolumeClaimVolumeSource represents a reference to a + PersistentVolumeClaim in the same namespace. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims + properties: + claimName: + description: |- + claimName is the name of a PersistentVolumeClaim in the same namespace as the pod using this volume. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims + type: string + readOnly: + description: |- + readOnly Will force the ReadOnly setting in VolumeMounts. + Default false. + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + description: photonPersistentDisk represents a PhotonController + persistent disk attached and mounted on kubelets host machine + properties: + fsType: + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + pdID: + description: pdID is the ID that identifies Photon Controller + persistent disk + type: string + required: + - pdID + type: object + portworxVolume: + description: portworxVolume represents a portworx volume attached + and mounted on kubelets host machine + properties: + fsType: + description: |- + fSType represents the filesystem type to mount + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs". Implicitly inferred to be "ext4" if unspecified. + type: string + readOnly: + description: |- + readOnly defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + volumeID: + description: volumeID uniquely identifies a Portworx volume + type: string + required: + - volumeID + type: object + projected: + description: projected items for all in one resources secrets, + configmaps, and downward API + properties: + defaultMode: + description: |- + defaultMode are the mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + sources: + description: |- + sources is the list of volume projections. Each entry in this list + handles one source. + items: + description: |- + Projection that may be projected along with other supported volume types. + Exactly one of these fields must be set. + properties: + clusterTrustBundle: + description: |- + ClusterTrustBundle allows a pod to access the `.spec.trustBundle` field + of ClusterTrustBundle objects in an auto-updating file. + + Alpha, gated by the ClusterTrustBundleProjection feature gate. + + ClusterTrustBundle objects can either be selected by name, or by the + combination of signer name and a label selector. + + Kubelet performs aggressive normalization of the PEM contents written + into the pod filesystem. Esoteric PEM features such as inter-block + comments and block headers are stripped. Certificates are deduplicated. + The ordering of certificates within the file is arbitrary, and Kubelet + may change the order over time. + properties: + labelSelector: + description: |- + Select all ClusterTrustBundles that match this label selector. Only has + effect if signerName is set. Mutually-exclusive with name. If unset, + interpreted as "match nothing". If set but empty, interpreted as "match + everything". + properties: + matchExpressions: + description: matchExpressions is a list of + label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + name: + description: |- + Select a single ClusterTrustBundle by object name. Mutually-exclusive + with signerName and labelSelector. + type: string + optional: + description: |- + If true, don't block pod startup if the referenced ClusterTrustBundle(s) + aren't available. If using name, then the named ClusterTrustBundle is + allowed not to exist. If using signerName, then the combination of + signerName and labelSelector is allowed to match zero + ClusterTrustBundles. + type: boolean + path: + description: Relative path from the volume root + to write the bundle. + type: string + signerName: + description: |- + Select all ClusterTrustBundles that match this signer name. + Mutually-exclusive with name. The contents of all selected + ClusterTrustBundles will be unified and deduplicated. + type: string + required: + - path + type: object + configMap: + description: configMap information about the configMap + data to project + properties: + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + ConfigMap will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: optional specify whether the ConfigMap + or its keys must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + description: downwardAPI information about the downwardAPI + data to project + properties: + items: + description: Items is a list of DownwardAPIVolume + file + items: + description: DownwardAPIVolumeFile represents + information to create the file containing + the pod field + properties: + fieldRef: + description: 'Required: Selects a field + of the pod: only annotations, labels, + name, namespace and uid are supported.' + properties: + apiVersion: + description: Version of the schema the + FieldPath is written in terms of, + defaults to "v1". + type: string + fieldPath: + description: Path of the field to select + in the specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + description: |- + Optional: mode bits used to set permissions on this file, must be an octal value + between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: 'Required: Path is the relative + path name of the file to be created. Must + not be absolute or contain the ''..'' + path. Must be utf-8 encoded. The first + item of the relative path must not start + with ''..''' + type: string + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. + properties: + containerName: + description: 'Container name: required + for volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format + of the exposed resources, defaults + to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to + select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + secret: + description: secret information about the secret data + to project + properties: + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + Secret will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: optional field specify whether the + Secret or its key must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + description: serviceAccountToken is information about + the serviceAccountToken data to project + properties: + audience: + description: |- + audience is the intended audience of the token. A recipient of a token + must identify itself with an identifier specified in the audience of the + token, and otherwise should reject the token. The audience defaults to the + identifier of the apiserver. + type: string + expirationSeconds: + description: |- + expirationSeconds is the requested duration of validity of the service + account token. As the token approaches expiration, the kubelet volume + plugin will proactively rotate the service account token. The kubelet will + start trying to rotate the token if the token is older than 80 percent of + its time to live or if the token is older than 24 hours.Defaults to 1 hour + and must be at least 10 minutes. + format: int64 + type: integer + path: + description: |- + path is the path relative to the mount point of the file to project the + token into. + type: string + required: + - path + type: object + type: object + type: array + x-kubernetes-list-type: atomic + type: object + quobyte: + description: quobyte represents a Quobyte mount on the host + that shares a pod's lifetime + properties: + group: + description: |- + group to map volume access to + Default is no group + type: string + readOnly: + description: |- + readOnly here will force the Quobyte volume to be mounted with read-only permissions. + Defaults to false. + type: boolean + registry: + description: |- + registry represents a single or multiple Quobyte Registry services + specified as a string as host:port pair (multiple entries are separated with commas) + which acts as the central registry for volumes + type: string + tenant: + description: |- + tenant owning the given Quobyte volume in the Backend + Used with dynamically provisioned Quobyte volumes, value is set by the plugin + type: string + user: + description: |- + user to map volume access to + Defaults to serivceaccount user + type: string + volume: + description: volume is a string that references an already + created Quobyte volume by name. + type: string + required: + - registry + - volume + type: object + rbd: + description: |- + rbd represents a Rados Block Device mount on the host that shares a pod's lifetime. + More info: https://examples.k8s.io/volumes/rbd/README.md + properties: + fsType: + description: |- + fsType is the filesystem type of the volume that you want to mount. + Tip: Ensure that the filesystem type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd + type: string + image: + description: |- + image is the rados image name. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + type: string + keyring: + default: /etc/ceph/keyring + description: |- + keyring is the path to key ring for RBDUser. + Default is /etc/ceph/keyring. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + type: string + monitors: + description: |- + monitors is a collection of Ceph monitors. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + items: + type: string + type: array + x-kubernetes-list-type: atomic + pool: + default: rbd + description: |- + pool is the rados pool name. + Default is rbd. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + type: string + readOnly: + description: |- + readOnly here will force the ReadOnly setting in VolumeMounts. + Defaults to false. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + type: boolean + secretRef: + description: |- + secretRef is name of the authentication secret for RBDUser. If provided + overrides keyring. + Default is nil. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + user: + default: admin + description: |- + user is the rados user name. + Default is admin. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + type: string + required: + - image + - monitors + type: object + scaleIO: + description: scaleIO represents a ScaleIO persistent volume + attached and mounted on Kubernetes nodes. + properties: + fsType: + default: xfs + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". + Default is "xfs". + type: string + gateway: + description: gateway is the host address of the ScaleIO + API Gateway. + type: string + protectionDomain: + description: protectionDomain is the name of the ScaleIO + Protection Domain for the configured storage. + type: string + readOnly: + description: |- + readOnly Defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: |- + secretRef references to the secret for ScaleIO user and other + sensitive information. If this is not provided, Login operation will fail. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + description: sslEnabled Flag enable/disable SSL communication + with Gateway, default false + type: boolean + storageMode: + default: ThinProvisioned + description: |- + storageMode indicates whether the storage for a volume should be ThickProvisioned or ThinProvisioned. + Default is ThinProvisioned. + type: string + storagePool: + description: storagePool is the ScaleIO Storage Pool associated + with the protection domain. + type: string + system: + description: system is the name of the storage system as + configured in ScaleIO. + type: string + volumeName: + description: |- + volumeName is the name of a volume already created in the ScaleIO system + that is associated with this volume source. + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + description: |- + secret represents a secret that should populate this volume. + More info: https://kubernetes.io/docs/concepts/storage/volumes#secret + properties: + defaultMode: + description: |- + defaultMode is Optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values + for mode bits. Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + items: + description: |- + items If unspecified, each key-value pair in the Data field of the referenced + Secret will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + optional: + description: optional field specify whether the Secret or + its keys must be defined + type: boolean + secretName: + description: |- + secretName is the name of the secret in the pod's namespace to use. + More info: https://kubernetes.io/docs/concepts/storage/volumes#secret + type: string + type: object + storageos: + description: storageOS represents a StorageOS volume attached + and mounted on Kubernetes nodes. + properties: + fsType: + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + readOnly: + description: |- + readOnly defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: |- + secretRef specifies the secret to use for obtaining the StorageOS API + credentials. If not specified, default values will be attempted. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + description: |- + volumeName is the human-readable name of the StorageOS volume. Volume + names are only unique within a namespace. + type: string + volumeNamespace: + description: |- + volumeNamespace specifies the scope of the volume within StorageOS. If no + namespace is specified then the Pod's namespace will be used. This allows the + Kubernetes name scoping to be mirrored within StorageOS for tighter integration. + Set VolumeName to any name to override the default behaviour. + Set to "default" if you are not using namespaces within StorageOS. + Namespaces that do not pre-exist within StorageOS will be created. + type: string + type: object + vsphereVolume: + description: vsphereVolume represents a vSphere volume attached + and mounted on kubelets host machine + properties: + fsType: + description: |- + fsType is filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + storagePolicyID: + description: storagePolicyID is the storage Policy Based + Management (SPBM) profile ID associated with the StoragePolicyName. + type: string + storagePolicyName: + description: storagePolicyName is the storage Policy Based + Management (SPBM) profile name. + type: string + volumePath: + description: volumePath is the path that identifies vSphere + volume vmdk + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + type: object + status: + description: ClusterVectorAggregatorStatus defines the observed state + of ClusterVectorAggregator + properties: + LastAppliedConfigHash: + format: int32 + type: integer + configCheckResult: + type: boolean + reason: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectorpipelines.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectorpipelines.yaml index 760cca22..994403bd 100644 --- a/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectorpipelines.yaml +++ b/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectorpipelines.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.9.2 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.16.1 name: clustervectorpipelines.observability.kaasops.io spec: group: observability.kaasops.io @@ -24,6 +23,9 @@ spec: - jsonPath: .status.configCheckResult name: Valid type: boolean + - jsonPath: .status.role + name: Role + type: boolean name: v1alpha1 schema: openAPIV3Schema: @@ -31,14 +33,19 @@ spec: API 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' + 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' + 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: type: object @@ -65,6 +72,8 @@ spec: type: boolean reason: type: string + role: + type: string type: object type: object served: true diff --git a/helm/charts/vector-operator/crds/observability.kaasops.io_vectoraggregators.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_vectoraggregators.yaml new file mode 100644 index 00000000..f3d754d8 --- /dev/null +++ b/helm/charts/vector-operator/crds/observability.kaasops.io_vectoraggregators.yaml @@ -0,0 +1,4961 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: vectoraggregators.observability.kaasops.io +spec: + group: observability.kaasops.io + names: + kind: VectorAggregator + listKind: VectorAggregatorList + plural: vectoraggregators + singular: vectoraggregator + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.configCheckResult + name: Valid + type: boolean + name: v1alpha1 + schema: + openAPIV3Schema: + description: VectorAggregator is the Schema for the vectoraggregators API + 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: + type: object + spec: + description: VectorAggregatorSpec defines the desired state of VectorAggregator + properties: + affinity: + description: Affinity If specified, the pod's scheduling constraints. + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for the + pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. + items: + description: |- + An empty preferred scheduling term matches all objects with implicit weight 0 + (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated with the + corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + description: Weight associated with matching the corresponding + nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. + The terms are ORed. + items: + description: |- + A null or empty node selector term matches no objects. The requirements of + them are ANDed. + The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate + this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules (e.g. + avoid putting this pod in the same node, zone, etc. as some + other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the anti-affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the anti-affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the anti-affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + annotations: + additionalProperties: + type: string + description: Annotations is an unstructured key value map stored with + a resource that may be set by external tools to store and retrieve + arbitrary metadata. + type: object + api: + description: |- + Vector API params. Allows to interact with a running Vector instance. + https://vector.dev/docs/reference/api/ + properties: + enabled: + type: boolean + healthcheck: + description: |- + Enable ReadinessProbe and LivenessProbe via api /health endpoint. + If probe enabled via VectorAgent or VectorAggregator, this setting will be ignored for that probe. + type: boolean + playground: + type: boolean + type: object + compressConfigFile: + description: 'Compress config file to fix: metadata.annotations: Too + long: must have at most 262144 characters' + type: boolean + configCheck: + description: Control params for ConfigCheck pods + properties: + affinity: + description: Affinity If specified, the pod's scheduling constraints. + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for + the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. + items: + description: |- + An empty preferred scheduling term matches all objects with implicit weight 0 + (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated with + the corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + description: Weight associated with matching the + corresponding nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. + The terms are ORed. + items: + description: |- + A null or empty node selector term matches no objects. The requirements of + them are ANDed. + The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. + co-locate this pod in the same node, zone, etc. as some + other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules + (e.g. avoid putting this pod in the same node, zone, etc. + as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the anti-affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the anti-affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the anti-affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + annotations: + additionalProperties: + type: string + description: Annotations is an unstructured key value map stored + with a resource that may be set by external tools to store and + retrieve arbitrary metadata. + type: object + disabled: + type: boolean + image: + description: |- + Image - docker image settings for Vector Agent + if no specified operator uses default config version + type: string + resources: + description: |- + Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + if not specified - default setting will be used + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + tolerations: + description: Tolerations If specified, the pod's tolerations. + items: + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . + properties: + effect: + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + type: object + configReloaderImage: + type: string + configReloaderResources: + description: ResourceRequirements describes the compute resource requirements. + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + containerSecurityContext: + description: |- + SecurityContext holds security configuration that will be applied to a container. + Some fields are present in both SecurityContext and PodSecurityContext. + When both are set, the values in SecurityContext take precedence. + properties: + allowPrivilegeEscalation: + description: |- + AllowPrivilegeEscalation controls whether a process can gain more + privileges than its parent process. This bool directly controls if + the no_new_privs flag will be set on the container process. + AllowPrivilegeEscalation is true always when the container is: + 1) run as Privileged + 2) has CAP_SYS_ADMIN + Note that this field cannot be set when spec.os.name is windows. + type: boolean + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by this container. If set, this profile + overrides the pod's appArmorProfile. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object + capabilities: + description: |- + The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container runtime. + Note that this field cannot be set when spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities type + type: string + type: array + x-kubernetes-list-type: atomic + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities type + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + description: |- + Run container in privileged mode. + Processes in privileged containers are essentially equivalent to root on the host. + Defaults to false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: |- + procMount denotes the type of proc mount to use for the containers. + The default value is Default which uses the container runtime defaults for + readonly paths and masked paths. + This requires the ProcMountType feature flag to be enabled. + Note that this field cannot be set when spec.os.name is windows. + type: string + readOnlyRootFilesystem: + description: |- + Whether this container has a read-only root filesystem. + Default is false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: |- + The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies to + the container. + type: string + role: + description: Role is a SELinux role label that applies to + the container. + type: string + type: + description: Type is a SELinux type label that applies to + the container. + type: string + user: + description: User is a SELinux user label that applies to + the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by this container. If seccomp options are + provided at both the pod & container level, the container options + override the pod options. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + windowsOptions: + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options from the PodSecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the GMSA + credential spec to use. + type: string + hostProcess: + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + dataDir: + description: |- + The directory used for persisting Vector state, such as on-disk buffers, file checkpoints, and more. Please make sure the Vector project has write permissions to this directory. + https://vector.dev/docs/reference/configuration/global-options/#data_dir + type: string + env: + description: Env that will be added to Vector pod + items: + description: EnvVar represents an environment variable present in + a Container. + properties: + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + type: string + value: + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". + type: string + valueFrom: + description: Source for the environment variable's value. Cannot + be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. + properties: + apiVersion: + description: Version of the schema the FieldPath is + written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified + API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the exposed + resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + host_aliases: + description: |- + HostAliases provides mapping between ip and hostnames, + that would be propagated to pod, + cannot be used with HostNetwork. + items: + description: |- + HostAlias holds the mapping between IP and hostnames that will be injected as an entry in the + pod's hosts file. + properties: + hostnames: + description: Hostnames for the above IP address. + items: + type: string + type: array + x-kubernetes-list-type: atomic + ip: + description: IP address of the host file entry. + type: string + required: + - ip + type: object + type: array + hostNetwork: + description: HostNetwork controls whether the pod may use the node + network namespace + type: boolean + image: + description: |- + Image - docker image settings for Vector Agent + if no specified operator uses default config version + type: string + imagePullPolicy: + description: ImagePullPolicy of pods + type: string + imagePullSecrets: + description: |- + ImagePullSecrets An optional list of references to secrets in the same namespace + to use for pulling images from registries + see http://kubernetes.io/docs/user-guide/images#specifying-imagepullsecrets-on-a-pod + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + type: array + internalMetrics: + description: Enable internal metrics exporter + type: boolean + livenessProbe: + description: Periodic probe of container liveness. Container will + be restarted if the probe fails. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + properties: + port: + description: Port number of the gRPC service. Number must + be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows + repeated headers. + items: + description: HTTPHeader describes a custom header to be + used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + podSecurityContext: + description: |- + SecurityContext holds pod-level security attributes and common container settings. + This defaults to the default PodSecurityContext. + properties: + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by the containers in this pod. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object + fsGroup: + description: |- + A special supplemental group that applies to all containers in a pod. + Some volume types allow the Kubelet to change the ownership of that volume + to be owned by the pod: + + 1. The owning GID will be the FSGroup + 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) + 3. The permission bits are OR'd with rw-rw---- + + If unset, the Kubelet will not modify the ownership and permissions of any volume. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + fsGroupChangePolicy: + description: |- + fsGroupChangePolicy defines behavior of changing ownership and permission of the volume + before being exposed inside Pod. This field will only apply to + volume types which support fsGroup based ownership(and permissions). + It will have no effect on ephemeral volume types such as: secret, configmaps + and emptydir. + Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used. + Note that this field cannot be set when spec.os.name is windows. + type: string + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: |- + The SELinux context to be applied to all containers. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in SecurityContext. If set in + both SecurityContext and PodSecurityContext, the value specified in SecurityContext + takes precedence for that container. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies to + the container. + type: string + role: + description: Role is a SELinux role label that applies to + the container. + type: string + type: + description: Type is a SELinux type label that applies to + the container. + type: string + user: + description: User is a SELinux user label that applies to + the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by the containers in this pod. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + supplementalGroups: + description: |- + A list of groups applied to the first process run in each container, in + addition to the container's primary GID and fsGroup (if specified). If + the SupplementalGroupsPolicy feature is enabled, the + supplementalGroupsPolicy field determines whether these are in addition + to or instead of any group memberships defined in the container image. + If unspecified, no additional groups are added, though group memberships + defined in the container image may still be used, depending on the + supplementalGroupsPolicy field. + Note that this field cannot be set when spec.os.name is windows. + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + description: |- + Defines how supplemental groups of the first container processes are calculated. + Valid values are "Merge" and "Strict". If not specified, "Merge" is used. + (Alpha) Using the field requires the SupplementalGroupsPolicy feature gate to be enabled + and the container runtime must implement support for this feature. + Note that this field cannot be set when spec.os.name is windows. + type: string + sysctls: + description: |- + Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported + sysctls (by the container runtime) might fail to launch. + Note that this field cannot be set when spec.os.name is windows. + items: + description: Sysctl defines a kernel parameter to be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options within a container's SecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the GMSA + credential spec to use. + type: string + hostProcess: + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + podSecurityPolicyName: + description: |- + PodSecurityPolicyName - defines name for podSecurityPolicy + in case of empty value, prefixedName will be used. + type: string + priorityClassName: + description: PriorityClassName assigned to the Pods + type: string + readinessProbe: + description: Periodic probe of container service readiness. Container + will be removed from service endpoints if the probe fails. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + properties: + port: + description: Port number of the gRPC service. Number must + be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows + repeated headers. + items: + description: HTTPHeader describes a custom header to be + used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + replicas: + format: int32 + type: integer + resources: + description: |- + Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + if not specified - default setting will be used + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + runtimeClassName: + description: |- + RuntimeClassName - defines runtime class for kubernetes pod. + https://kubernetes.io/docs/concepts/containers/runtime-class/ + type: string + schedulerName: + description: SchedulerName - defines kubernetes scheduler name + type: string + selector: + description: |- + Selector defines a filter for the Vector Pipeline and Cluster Vector Pipeline by labels. + If not specified, all pipelines will be selected. + properties: + matchLabels: + additionalProperties: + type: string + type: object + type: object + tolerations: + description: Tolerations If specified, the pod's tolerations. + items: + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . + properties: + effect: + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + volumeMounts: + description: Pod volumes to mount into the container's filesystem. + items: + description: VolumeMount describes a mounting of a Volume within + a container. + properties: + mountPath: + description: |- + Path within the container at which the volume should be mounted. Must + not contain ':'. + type: string + mountPropagation: + description: |- + mountPropagation determines how mounts are propagated from the host + to container and the other way around. + When not set, MountPropagationNone is used. + This field is beta in 1.10. + When RecursiveReadOnly is set to IfPossible or to Enabled, MountPropagation must be None or unspecified + (which defaults to None). + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: |- + Mounted read-only if true, read-write otherwise (false or unspecified). + Defaults to false. + type: boolean + recursiveReadOnly: + description: |- + RecursiveReadOnly specifies whether read-only mounts should be handled + recursively. + + If ReadOnly is false, this field has no meaning and must be unspecified. + + If ReadOnly is true, and this field is set to Disabled, the mount is not made + recursively read-only. If this field is set to IfPossible, the mount is made + recursively read-only, if it is supported by the container runtime. If this + field is set to Enabled, the mount is made recursively read-only if it is + supported by the container runtime, otherwise the pod will not be started and + an error will be generated to indicate the reason. + + If this field is set to IfPossible or Enabled, MountPropagation must be set to + None (or be unspecified, which defaults to None). + + If this field is not specified, it is treated as an equivalent of Disabled. + type: string + subPath: + description: |- + Path within the volume from which the container's volume should be mounted. + Defaults to "" (volume's root). + type: string + subPathExpr: + description: |- + Expanded path within the volume from which the container's volume should be mounted. + Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. + Defaults to "" (volume's root). + SubPathExpr and SubPath are mutually exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + volumes: + description: List of volumes that can be mounted by containers belonging + to the pod. + items: + description: Volume represents a named volume in a pod that may + be accessed by any container in the pod. + properties: + awsElasticBlockStore: + description: |- + awsElasticBlockStore represents an AWS Disk resource that is attached to a + kubelet's host machine and then exposed to the pod. + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + properties: + fsType: + description: |- + fsType is the filesystem type of the volume that you want to mount. + Tip: Ensure that the filesystem type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + type: string + partition: + description: |- + partition is the partition in the volume that you want to mount. + If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition as "1". + Similarly, the volume partition for /dev/sda is "0" (or you can leave the property empty). + format: int32 + type: integer + readOnly: + description: |- + readOnly value true will force the readOnly setting in VolumeMounts. + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + type: boolean + volumeID: + description: |- + volumeID is unique ID of the persistent disk resource in AWS (Amazon EBS volume). + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + type: string + required: + - volumeID + type: object + azureDisk: + description: azureDisk represents an Azure Data Disk mount on + the host and bind mount to the pod. + properties: + cachingMode: + description: 'cachingMode is the Host Caching mode: None, + Read Only, Read Write.' + type: string + diskName: + description: diskName is the Name of the data disk in the + blob storage + type: string + diskURI: + description: diskURI is the URI of data disk in the blob + storage + type: string + fsType: + default: ext4 + description: |- + fsType is Filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + kind: + description: 'kind expected values are Shared: multiple + blob disks per storage account Dedicated: single blob + disk per storage account Managed: azure managed data + disk (only in managed availability set). defaults to shared' + type: string + readOnly: + default: false + description: |- + readOnly Defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + description: azureFile represents an Azure File Service mount + on the host and bind mount to the pod. + properties: + readOnly: + description: |- + readOnly defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + secretName: + description: secretName is the name of secret that contains + Azure Storage Account Name and Key + type: string + shareName: + description: shareName is the azure share Name + type: string + required: + - secretName + - shareName + type: object + cephfs: + description: cephFS represents a Ceph FS mount on the host that + shares a pod's lifetime + properties: + monitors: + description: |- + monitors is Required: Monitors is a collection of Ceph monitors + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it + items: + type: string + type: array + x-kubernetes-list-type: atomic + path: + description: 'path is Optional: Used as the mounted root, + rather than the full Ceph tree, default is /' + type: string + readOnly: + description: |- + readOnly is Optional: Defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it + type: boolean + secretFile: + description: |- + secretFile is Optional: SecretFile is the path to key ring for User, default is /etc/ceph/user.secret + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it + type: string + secretRef: + description: |- + secretRef is Optional: SecretRef is reference to the authentication secret for User, default is empty. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + user: + description: |- + user is optional: User is the rados user name, default is admin + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it + type: string + required: + - monitors + type: object + cinder: + description: |- + cinder represents a cinder volume attached and mounted on kubelets host machine. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md + properties: + fsType: + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md + type: string + readOnly: + description: |- + readOnly defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md + type: boolean + secretRef: + description: |- + secretRef is optional: points to a secret object containing parameters used to connect + to OpenStack. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + description: |- + volumeID used to identify the volume in cinder. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md + type: string + required: + - volumeID + type: object + configMap: + description: configMap represents a configMap that should populate + this volume + properties: + defaultMode: + description: |- + defaultMode is optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + ConfigMap will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: optional specify whether the ConfigMap or its + keys must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + description: csi (Container Storage Interface) represents ephemeral + storage that is handled by certain external CSI drivers (Beta + feature). + properties: + driver: + description: |- + driver is the name of the CSI driver that handles this volume. + Consult with your admin for the correct name as registered in the cluster. + type: string + fsType: + description: |- + fsType to mount. Ex. "ext4", "xfs", "ntfs". + If not provided, the empty value is passed to the associated CSI driver + which will determine the default filesystem to apply. + type: string + nodePublishSecretRef: + description: |- + nodePublishSecretRef is a reference to the secret object containing + sensitive information to pass to the CSI driver to complete the CSI + NodePublishVolume and NodeUnpublishVolume calls. + This field is optional, and may be empty if no secret is required. If the + secret object contains more than one secret, all secret references are passed. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + description: |- + readOnly specifies a read-only configuration for the volume. + Defaults to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: + type: string + description: |- + volumeAttributes stores driver-specific properties that are passed to the CSI + driver. Consult your driver's documentation for supported values. + type: object + required: + - driver + type: object + downwardAPI: + description: downwardAPI represents downward API about the pod + that should populate this volume + properties: + defaultMode: + description: |- + Optional: mode bits to use on created files by default. Must be a + Optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + items: + description: Items is a list of downward API volume file + items: + description: DownwardAPIVolumeFile represents information + to create the file containing the pod field + properties: + fieldRef: + description: 'Required: Selects a field of the pod: + only annotations, labels, name, namespace and uid + are supported.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + description: |- + Optional: mode bits used to set permissions on this file, must be an octal value + between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: 'Required: Path is the relative path + name of the file to be created. Must not be absolute + or contain the ''..'' path. Must be utf-8 encoded. + The first item of the relative path must not start + with ''..''' + type: string + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + emptyDir: + description: |- + emptyDir represents a temporary directory that shares a pod's lifetime. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir + properties: + medium: + description: |- + medium represents what type of storage medium should back this directory. + The default is "" which means to use the node's default medium. + Must be an empty string (default) or Memory. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + description: |- + sizeLimit is the total amount of local storage required for this EmptyDir volume. + The size limit is also applicable for memory medium. + The maximum usage on memory medium EmptyDir would be the minimum value between + the SizeLimit specified here and the sum of memory limits of all containers in a pod. + The default is nil which means that the limit is undefined. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + description: |- + ephemeral represents a volume that is handled by a cluster storage driver. + The volume's lifecycle is tied to the pod that defines it - it will be created before the pod starts, + and deleted when the pod is removed. + + Use this if: + a) the volume is only needed while the pod runs, + b) features of normal volumes like restoring from snapshot or capacity + tracking are needed, + c) the storage driver is specified through a storage class, and + d) the storage driver supports dynamic volume provisioning through + a PersistentVolumeClaim (see EphemeralVolumeSource for more + information on the connection between this volume type + and PersistentVolumeClaim). + + Use PersistentVolumeClaim or one of the vendor-specific + APIs for volumes that persist for longer than the lifecycle + of an individual pod. + + Use CSI for light-weight local ephemeral volumes if the CSI driver is meant to + be used that way - see the documentation of the driver for + more information. + + A pod can use both types of ephemeral volumes and + persistent volumes at the same time. + properties: + volumeClaimTemplate: + description: |- + Will be used to create a stand-alone PVC to provision the volume. + The pod in which this EphemeralVolumeSource is embedded will be the + owner of the PVC, i.e. the PVC will be deleted together with the + pod. The name of the PVC will be `-` where + `` is the name from the `PodSpec.Volumes` array + entry. Pod validation will reject the pod if the concatenated name + is not valid for a PVC (for example, too long). + + An existing PVC with that name that is not owned by the pod + will *not* be used for the pod to avoid using an unrelated + volume by mistake. Starting the pod is then blocked until + the unrelated PVC is removed. If such a pre-created PVC is + meant to be used by the pod, the PVC has to updated with an + owner reference to the pod once the pod exists. Normally + this should not be necessary, but it may be useful when + manually reconstructing a broken cluster. + + This field is read-only and no changes will be made by Kubernetes + to the PVC after it has been created. + + Required, must not be nil. + properties: + metadata: + description: |- + May contain labels and annotations that will be copied into the PVC + when creating it. No other fields are allowed and will be rejected during + validation. + type: object + spec: + description: |- + The specification for the PersistentVolumeClaim. The entire content is + copied unchanged into the PVC that gets created from this + template. The same fields as in a PersistentVolumeClaim + are also valid here. + properties: + accessModes: + description: |- + accessModes contains the desired access modes the volume should have. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1 + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + description: |- + dataSource field can be used to specify either: + * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) + * An existing PVC (PersistentVolumeClaim) + If the provisioner or an external controller can support the specified data source, + it will create a new volume based on the contents of the specified data source. + When the AnyVolumeDataSource feature gate is enabled, dataSource contents will be copied to dataSourceRef, + and dataSourceRef contents will be copied to dataSource when dataSourceRef.namespace is not specified. + If the namespace is specified, then dataSourceRef will not be copied to dataSource. + properties: + apiGroup: + description: |- + APIGroup is the group for the resource being referenced. + If APIGroup is not specified, the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource being + referenced + type: string + name: + description: Name is the name of resource being + referenced + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + description: |- + dataSourceRef specifies the object from which to populate the volume with data, if a non-empty + volume is desired. This may be any object from a non-empty API group (non + core object) or a PersistentVolumeClaim object. + When this field is specified, volume binding will only succeed if the type of + the specified object matches some installed volume populator or dynamic + provisioner. + This field will replace the functionality of the dataSource field and as such + if both fields are non-empty, they must have the same value. For backwards + compatibility, when namespace isn't specified in dataSourceRef, + both fields (dataSource and dataSourceRef) will be set to the same + value automatically if one of them is empty and the other is non-empty. + When namespace is specified in dataSourceRef, + dataSource isn't set to the same value and must be empty. + There are three important differences between dataSource and dataSourceRef: + * While dataSource only allows two specific types of objects, dataSourceRef + allows any non-core object, as well as PersistentVolumeClaim objects. + * While dataSource ignores disallowed values (dropping them), dataSourceRef + preserves all values, and generates an error if a disallowed value is + specified. + * While dataSource only allows local objects, dataSourceRef allows objects + in any namespaces. + (Beta) Using this field requires the AnyVolumeDataSource feature gate to be enabled. + (Alpha) Using the namespace field of dataSourceRef requires the CrossNamespaceVolumeDataSource feature gate to be enabled. + properties: + apiGroup: + description: |- + APIGroup is the group for the resource being referenced. + If APIGroup is not specified, the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource being + referenced + type: string + name: + description: Name is the name of resource being + referenced + type: string + namespace: + description: |- + Namespace is the namespace of resource being referenced + Note that when a namespace is specified, a gateway.networking.k8s.io/ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. + (Alpha) This field requires the CrossNamespaceVolumeDataSource feature gate to be enabled. + type: string + required: + - kind + - name + type: object + resources: + description: |- + resources represents the minimum resources the volume should have. + If RecoverVolumeExpansionFailure feature is enabled users are allowed to specify resource requirements + that are lower than previous value but must still be higher than capacity recorded in the + status field of the claim. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + selector: + description: selector is a label query over volumes + to consider for binding. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + description: |- + storageClassName is the name of the StorageClass required by the claim. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1 + type: string + volumeAttributesClassName: + description: |- + volumeAttributesClassName may be used to set the VolumeAttributesClass used by this claim. + If specified, the CSI driver will create or update the volume with the attributes defined + in the corresponding VolumeAttributesClass. This has a different purpose than storageClassName, + it can be changed after the claim is created. An empty string value means that no VolumeAttributesClass + will be applied to the claim but it's not allowed to reset this field to empty string once it is set. + If unspecified and the PersistentVolumeClaim is unbound, the default VolumeAttributesClass + will be set by the persistentvolume controller if it exists. + If the resource referred to by volumeAttributesClass does not exist, this PersistentVolumeClaim will be + set to a Pending state, as reflected by the modifyVolumeStatus field, until such as a resource + exists. + More info: https://kubernetes.io/docs/concepts/storage/volume-attributes-classes/ + (Beta) Using this field requires the VolumeAttributesClass feature gate to be enabled (off by default). + type: string + volumeMode: + description: |- + volumeMode defines what type of volume is required by the claim. + Value of Filesystem is implied when not included in claim spec. + type: string + volumeName: + description: volumeName is the binding reference + to the PersistentVolume backing this claim. + type: string + type: object + required: + - spec + type: object + type: object + fc: + description: fc represents a Fibre Channel resource that is + attached to a kubelet's host machine and then exposed to the + pod. + properties: + fsType: + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + lun: + description: 'lun is Optional: FC target lun number' + format: int32 + type: integer + readOnly: + description: |- + readOnly is Optional: Defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + targetWWNs: + description: 'targetWWNs is Optional: FC target worldwide + names (WWNs)' + items: + type: string + type: array + x-kubernetes-list-type: atomic + wwids: + description: |- + wwids Optional: FC volume world wide identifiers (wwids) + Either wwids or combination of targetWWNs and lun must be set, but not both simultaneously. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + flexVolume: + description: |- + flexVolume represents a generic volume resource that is + provisioned/attached using an exec based plugin. + properties: + driver: + description: driver is the name of the driver to use for + this volume. + type: string + fsType: + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". The default filesystem depends on FlexVolume script. + type: string + options: + additionalProperties: + type: string + description: 'options is Optional: this field holds extra + command options if any.' + type: object + readOnly: + description: |- + readOnly is Optional: defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: |- + secretRef is Optional: secretRef is reference to the secret object containing + sensitive information to pass to the plugin scripts. This may be + empty if no secret object is specified. If the secret object + contains more than one secret, all secrets are passed to the plugin + scripts. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + description: flocker represents a Flocker volume attached to + a kubelet's host machine. This depends on the Flocker control + service being running + properties: + datasetName: + description: |- + datasetName is Name of the dataset stored as metadata -> name on the dataset for Flocker + should be considered as deprecated + type: string + datasetUUID: + description: datasetUUID is the UUID of the dataset. This + is unique identifier of a Flocker dataset + type: string + type: object + gcePersistentDisk: + description: |- + gcePersistentDisk represents a GCE Disk resource that is attached to a + kubelet's host machine and then exposed to the pod. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + properties: + fsType: + description: |- + fsType is filesystem type of the volume that you want to mount. + Tip: Ensure that the filesystem type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + type: string + partition: + description: |- + partition is the partition in the volume that you want to mount. + If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition as "1". + Similarly, the volume partition for /dev/sda is "0" (or you can leave the property empty). + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + format: int32 + type: integer + pdName: + description: |- + pdName is unique name of the PD resource in GCE. Used to identify the disk in GCE. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + type: string + readOnly: + description: |- + readOnly here will force the ReadOnly setting in VolumeMounts. + Defaults to false. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + type: boolean + required: + - pdName + type: object + gitRepo: + description: |- + gitRepo represents a git repository at a particular revision. + DEPRECATED: GitRepo is deprecated. To provision a container with a git repo, mount an + EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir + into the Pod's container. + properties: + directory: + description: |- + directory is the target directory name. + Must not contain or start with '..'. If '.' is supplied, the volume directory will be the + git repository. Otherwise, if specified, the volume will contain the git repository in + the subdirectory with the given name. + type: string + repository: + description: repository is the URL + type: string + revision: + description: revision is the commit hash for the specified + revision. + type: string + required: + - repository + type: object + glusterfs: + description: |- + glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. + More info: https://examples.k8s.io/volumes/glusterfs/README.md + properties: + endpoints: + description: |- + endpoints is the endpoint name that details Glusterfs topology. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod + type: string + path: + description: |- + path is the Glusterfs volume path. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod + type: string + readOnly: + description: |- + readOnly here will force the Glusterfs volume to be mounted with read-only permissions. + Defaults to false. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod + type: boolean + required: + - endpoints + - path + type: object + hostPath: + description: |- + hostPath represents a pre-existing file or directory on the host + machine that is directly exposed to the container. This is generally + used for system agents or other privileged things that are allowed + to see the host machine. Most containers will NOT need this. + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + properties: + path: + description: |- + path of the directory on the host. + If the path is a symlink, it will follow the link to the real path. + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + type: string + type: + description: |- + type for HostPath Volume + Defaults to "" + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + type: string + required: + - path + type: object + image: + description: |- + image represents an OCI object (a container image or artifact) pulled and mounted on the kubelet's host machine. + The volume is resolved at pod startup depending on which PullPolicy value is provided: + + - Always: the kubelet always attempts to pull the reference. Container creation will fail If the pull fails. + - Never: the kubelet never pulls the reference and only uses a local image or artifact. Container creation will fail if the reference isn't present. + - IfNotPresent: the kubelet pulls if the reference isn't already present on disk. Container creation will fail if the reference isn't present and the pull fails. + + The volume gets re-resolved if the pod gets deleted and recreated, which means that new remote content will become available on pod recreation. + A failure to resolve or pull the image during pod startup will block containers from starting and may add significant latency. Failures will be retried using normal volume backoff and will be reported on the pod reason and message. + The types of objects that may be mounted by this volume are defined by the container runtime implementation on a host machine and at minimum must include all valid types supported by the container image field. + The OCI object gets mounted in a single directory (spec.containers[*].volumeMounts.mountPath) by merging the manifest layers in the same way as for container images. + The volume will be mounted read-only (ro) and non-executable files (noexec). + Sub path mounts for containers are not supported (spec.containers[*].volumeMounts.subpath). + The field spec.securityContext.fsGroupChangePolicy has no effect on this volume type. + properties: + pullPolicy: + description: |- + Policy for pulling OCI objects. Possible values are: + Always: the kubelet always attempts to pull the reference. Container creation will fail If the pull fails. + Never: the kubelet never pulls the reference and only uses a local image or artifact. Container creation will fail if the reference isn't present. + IfNotPresent: the kubelet pulls if the reference isn't already present on disk. Container creation will fail if the reference isn't present and the pull fails. + Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. + type: string + reference: + description: |- + Required: Image or artifact reference to be used. + Behaves in the same way as pod.spec.containers[*].image. + Pull secrets will be assembled in the same way as for the container image by looking up node credentials, SA image pull secrets, and pod spec image pull secrets. + More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config management to default or override + container images in workload controllers like Deployments and StatefulSets. + type: string + type: object + iscsi: + description: |- + iscsi represents an ISCSI Disk resource that is attached to a + kubelet's host machine and then exposed to the pod. + More info: https://examples.k8s.io/volumes/iscsi/README.md + properties: + chapAuthDiscovery: + description: chapAuthDiscovery defines whether support iSCSI + Discovery CHAP authentication + type: boolean + chapAuthSession: + description: chapAuthSession defines whether support iSCSI + Session CHAP authentication + type: boolean + fsType: + description: |- + fsType is the filesystem type of the volume that you want to mount. + Tip: Ensure that the filesystem type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi + type: string + initiatorName: + description: |- + initiatorName is the custom iSCSI Initiator Name. + If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface + : will be created for the connection. + type: string + iqn: + description: iqn is the target iSCSI Qualified Name. + type: string + iscsiInterface: + default: default + description: |- + iscsiInterface is the interface Name that uses an iSCSI transport. + Defaults to 'default' (tcp). + type: string + lun: + description: lun represents iSCSI Target Lun number. + format: int32 + type: integer + portals: + description: |- + portals is the iSCSI Target Portal List. The portal is either an IP or ip_addr:port if the port + is other than default (typically TCP ports 860 and 3260). + items: + type: string + type: array + x-kubernetes-list-type: atomic + readOnly: + description: |- + readOnly here will force the ReadOnly setting in VolumeMounts. + Defaults to false. + type: boolean + secretRef: + description: secretRef is the CHAP Secret for iSCSI target + and initiator authentication + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + description: |- + targetPortal is iSCSI Target Portal. The Portal is either an IP or ip_addr:port if the port + is other than default (typically TCP ports 860 and 3260). + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + description: |- + name of the volume. + Must be a DNS_LABEL and unique within the pod. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + nfs: + description: |- + nfs represents an NFS mount on the host that shares a pod's lifetime + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs + properties: + path: + description: |- + path that is exported by the NFS server. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs + type: string + readOnly: + description: |- + readOnly here will force the NFS export to be mounted with read-only permissions. + Defaults to false. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs + type: boolean + server: + description: |- + server is the hostname or IP address of the NFS server. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + description: |- + persistentVolumeClaimVolumeSource represents a reference to a + PersistentVolumeClaim in the same namespace. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims + properties: + claimName: + description: |- + claimName is the name of a PersistentVolumeClaim in the same namespace as the pod using this volume. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims + type: string + readOnly: + description: |- + readOnly Will force the ReadOnly setting in VolumeMounts. + Default false. + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + description: photonPersistentDisk represents a PhotonController + persistent disk attached and mounted on kubelets host machine + properties: + fsType: + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + pdID: + description: pdID is the ID that identifies Photon Controller + persistent disk + type: string + required: + - pdID + type: object + portworxVolume: + description: portworxVolume represents a portworx volume attached + and mounted on kubelets host machine + properties: + fsType: + description: |- + fSType represents the filesystem type to mount + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs". Implicitly inferred to be "ext4" if unspecified. + type: string + readOnly: + description: |- + readOnly defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + volumeID: + description: volumeID uniquely identifies a Portworx volume + type: string + required: + - volumeID + type: object + projected: + description: projected items for all in one resources secrets, + configmaps, and downward API + properties: + defaultMode: + description: |- + defaultMode are the mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + sources: + description: |- + sources is the list of volume projections. Each entry in this list + handles one source. + items: + description: |- + Projection that may be projected along with other supported volume types. + Exactly one of these fields must be set. + properties: + clusterTrustBundle: + description: |- + ClusterTrustBundle allows a pod to access the `.spec.trustBundle` field + of ClusterTrustBundle objects in an auto-updating file. + + Alpha, gated by the ClusterTrustBundleProjection feature gate. + + ClusterTrustBundle objects can either be selected by name, or by the + combination of signer name and a label selector. + + Kubelet performs aggressive normalization of the PEM contents written + into the pod filesystem. Esoteric PEM features such as inter-block + comments and block headers are stripped. Certificates are deduplicated. + The ordering of certificates within the file is arbitrary, and Kubelet + may change the order over time. + properties: + labelSelector: + description: |- + Select all ClusterTrustBundles that match this label selector. Only has + effect if signerName is set. Mutually-exclusive with name. If unset, + interpreted as "match nothing". If set but empty, interpreted as "match + everything". + properties: + matchExpressions: + description: matchExpressions is a list of + label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + name: + description: |- + Select a single ClusterTrustBundle by object name. Mutually-exclusive + with signerName and labelSelector. + type: string + optional: + description: |- + If true, don't block pod startup if the referenced ClusterTrustBundle(s) + aren't available. If using name, then the named ClusterTrustBundle is + allowed not to exist. If using signerName, then the combination of + signerName and labelSelector is allowed to match zero + ClusterTrustBundles. + type: boolean + path: + description: Relative path from the volume root + to write the bundle. + type: string + signerName: + description: |- + Select all ClusterTrustBundles that match this signer name. + Mutually-exclusive with name. The contents of all selected + ClusterTrustBundles will be unified and deduplicated. + type: string + required: + - path + type: object + configMap: + description: configMap information about the configMap + data to project + properties: + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + ConfigMap will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: optional specify whether the ConfigMap + or its keys must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + description: downwardAPI information about the downwardAPI + data to project + properties: + items: + description: Items is a list of DownwardAPIVolume + file + items: + description: DownwardAPIVolumeFile represents + information to create the file containing + the pod field + properties: + fieldRef: + description: 'Required: Selects a field + of the pod: only annotations, labels, + name, namespace and uid are supported.' + properties: + apiVersion: + description: Version of the schema the + FieldPath is written in terms of, + defaults to "v1". + type: string + fieldPath: + description: Path of the field to select + in the specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + description: |- + Optional: mode bits used to set permissions on this file, must be an octal value + between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: 'Required: Path is the relative + path name of the file to be created. Must + not be absolute or contain the ''..'' + path. Must be utf-8 encoded. The first + item of the relative path must not start + with ''..''' + type: string + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. + properties: + containerName: + description: 'Container name: required + for volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format + of the exposed resources, defaults + to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to + select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + secret: + description: secret information about the secret data + to project + properties: + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + Secret will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: optional field specify whether the + Secret or its key must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + description: serviceAccountToken is information about + the serviceAccountToken data to project + properties: + audience: + description: |- + audience is the intended audience of the token. A recipient of a token + must identify itself with an identifier specified in the audience of the + token, and otherwise should reject the token. The audience defaults to the + identifier of the apiserver. + type: string + expirationSeconds: + description: |- + expirationSeconds is the requested duration of validity of the service + account token. As the token approaches expiration, the kubelet volume + plugin will proactively rotate the service account token. The kubelet will + start trying to rotate the token if the token is older than 80 percent of + its time to live or if the token is older than 24 hours.Defaults to 1 hour + and must be at least 10 minutes. + format: int64 + type: integer + path: + description: |- + path is the path relative to the mount point of the file to project the + token into. + type: string + required: + - path + type: object + type: object + type: array + x-kubernetes-list-type: atomic + type: object + quobyte: + description: quobyte represents a Quobyte mount on the host + that shares a pod's lifetime + properties: + group: + description: |- + group to map volume access to + Default is no group + type: string + readOnly: + description: |- + readOnly here will force the Quobyte volume to be mounted with read-only permissions. + Defaults to false. + type: boolean + registry: + description: |- + registry represents a single or multiple Quobyte Registry services + specified as a string as host:port pair (multiple entries are separated with commas) + which acts as the central registry for volumes + type: string + tenant: + description: |- + tenant owning the given Quobyte volume in the Backend + Used with dynamically provisioned Quobyte volumes, value is set by the plugin + type: string + user: + description: |- + user to map volume access to + Defaults to serivceaccount user + type: string + volume: + description: volume is a string that references an already + created Quobyte volume by name. + type: string + required: + - registry + - volume + type: object + rbd: + description: |- + rbd represents a Rados Block Device mount on the host that shares a pod's lifetime. + More info: https://examples.k8s.io/volumes/rbd/README.md + properties: + fsType: + description: |- + fsType is the filesystem type of the volume that you want to mount. + Tip: Ensure that the filesystem type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd + type: string + image: + description: |- + image is the rados image name. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + type: string + keyring: + default: /etc/ceph/keyring + description: |- + keyring is the path to key ring for RBDUser. + Default is /etc/ceph/keyring. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + type: string + monitors: + description: |- + monitors is a collection of Ceph monitors. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + items: + type: string + type: array + x-kubernetes-list-type: atomic + pool: + default: rbd + description: |- + pool is the rados pool name. + Default is rbd. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + type: string + readOnly: + description: |- + readOnly here will force the ReadOnly setting in VolumeMounts. + Defaults to false. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + type: boolean + secretRef: + description: |- + secretRef is name of the authentication secret for RBDUser. If provided + overrides keyring. + Default is nil. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + user: + default: admin + description: |- + user is the rados user name. + Default is admin. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + type: string + required: + - image + - monitors + type: object + scaleIO: + description: scaleIO represents a ScaleIO persistent volume + attached and mounted on Kubernetes nodes. + properties: + fsType: + default: xfs + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". + Default is "xfs". + type: string + gateway: + description: gateway is the host address of the ScaleIO + API Gateway. + type: string + protectionDomain: + description: protectionDomain is the name of the ScaleIO + Protection Domain for the configured storage. + type: string + readOnly: + description: |- + readOnly Defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: |- + secretRef references to the secret for ScaleIO user and other + sensitive information. If this is not provided, Login operation will fail. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + description: sslEnabled Flag enable/disable SSL communication + with Gateway, default false + type: boolean + storageMode: + default: ThinProvisioned + description: |- + storageMode indicates whether the storage for a volume should be ThickProvisioned or ThinProvisioned. + Default is ThinProvisioned. + type: string + storagePool: + description: storagePool is the ScaleIO Storage Pool associated + with the protection domain. + type: string + system: + description: system is the name of the storage system as + configured in ScaleIO. + type: string + volumeName: + description: |- + volumeName is the name of a volume already created in the ScaleIO system + that is associated with this volume source. + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + description: |- + secret represents a secret that should populate this volume. + More info: https://kubernetes.io/docs/concepts/storage/volumes#secret + properties: + defaultMode: + description: |- + defaultMode is Optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values + for mode bits. Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + items: + description: |- + items If unspecified, each key-value pair in the Data field of the referenced + Secret will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + optional: + description: optional field specify whether the Secret or + its keys must be defined + type: boolean + secretName: + description: |- + secretName is the name of the secret in the pod's namespace to use. + More info: https://kubernetes.io/docs/concepts/storage/volumes#secret + type: string + type: object + storageos: + description: storageOS represents a StorageOS volume attached + and mounted on Kubernetes nodes. + properties: + fsType: + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + readOnly: + description: |- + readOnly defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: |- + secretRef specifies the secret to use for obtaining the StorageOS API + credentials. If not specified, default values will be attempted. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + description: |- + volumeName is the human-readable name of the StorageOS volume. Volume + names are only unique within a namespace. + type: string + volumeNamespace: + description: |- + volumeNamespace specifies the scope of the volume within StorageOS. If no + namespace is specified then the Pod's namespace will be used. This allows the + Kubernetes name scoping to be mirrored within StorageOS for tighter integration. + Set VolumeName to any name to override the default behaviour. + Set to "default" if you are not using namespaces within StorageOS. + Namespaces that do not pre-exist within StorageOS will be created. + type: string + type: object + vsphereVolume: + description: vsphereVolume represents a vSphere volume attached + and mounted on kubelets host machine + properties: + fsType: + description: |- + fsType is filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + storagePolicyID: + description: storagePolicyID is the storage Policy Based + Management (SPBM) profile ID associated with the StoragePolicyName. + type: string + storagePolicyName: + description: storagePolicyName is the storage Policy Based + Management (SPBM) profile name. + type: string + volumePath: + description: volumePath is the path that identifies vSphere + volume vmdk + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + type: object + status: + description: VectorAggregatorStatus defines the observed state of VectorAggregator + properties: + LastAppliedConfigHash: + format: int32 + type: integer + configCheckResult: + type: boolean + reason: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/helm/charts/vector-operator/crds/observability.kaasops.io_vectorpipelines.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_vectorpipelines.yaml index 9861cbd1..bb6a81f2 100644 --- a/helm/charts/vector-operator/crds/observability.kaasops.io_vectorpipelines.yaml +++ b/helm/charts/vector-operator/crds/observability.kaasops.io_vectorpipelines.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.9.2 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.16.1 name: vectorpipelines.observability.kaasops.io spec: group: observability.kaasops.io @@ -26,20 +25,28 @@ spec: - jsonPath: .status.configCheckResult name: Valid type: boolean + - jsonPath: .status.role + name: Role + type: boolean name: v1alpha1 schema: openAPIV3Schema: description: VectorPipeline is the Schema for the vectorpipelines API 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' + 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' + 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: type: object @@ -66,6 +73,8 @@ spec: type: boolean reason: type: string + role: + type: string type: object type: object served: true diff --git a/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml index 2509005f..0fbd4cb2 100644 --- a/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml +++ b/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.9.2 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.16.1 name: vectors.observability.kaasops.io spec: group: observability.kaasops.io @@ -28,14 +27,19 @@ spec: description: Vector is the Schema for the vectors API 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' + 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' + 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: type: object @@ -43,8 +47,7 @@ spec: description: VectorSpec defines the desired state of Vector properties: agent: - description: DisableAggregation DisableAggregation bool `json:"disableAggregation,omitempty"` - Vector Agent + description: Vector Agent properties: affinity: description: Affinity If specified, the pod's scheduling constraints. @@ -54,22 +57,20 @@ spec: the pod. properties: preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule pods - to nodes that satisfy the affinity expressions specified - by this field, but it may choose a node that violates - one or more of the expressions. The node that is most - preferred is the one with the greatest sum of weights, - i.e. for each node that meets all of the scheduling - requirements (resource request, requiredDuringScheduling - affinity expressions, etc.), compute a sum by iterating - through the elements of this field and adding "weight" - to the sum if the node matches the corresponding matchExpressions; - the node(s) with the highest sum are the most preferred. + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. items: - description: An empty preferred scheduling term matches - all objects with implicit weight 0 (i.e. it's a no-op). - A null preferred scheduling term matches no objects - (i.e. is also a no-op). + description: |- + An empty preferred scheduling term matches all objects with implicit weight 0 + (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). properties: preference: description: A node selector term, associated with @@ -79,78 +80,70 @@ spec: description: A list of node selector requirements by node's labels. items: - description: A node selector requirement is - a selector that contains values, a key, - and an operator that relates the key and - values. + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. properties: key: description: The label key that the selector applies to. type: string operator: - description: Represents a key's relationship - to a set of values. Valid operators - are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: - description: An array of string values. - If the operator is In or NotIn, the - values array must be non-empty. If the - operator is Exists or DoesNotExist, - the values array must be empty. If the - operator is Gt or Lt, the values array - must have a single element, which will - be interpreted as an integer. This array - is replaced during a strategic merge - patch. + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchFields: description: A list of node selector requirements by node's fields. items: - description: A node selector requirement is - a selector that contains values, a key, - and an operator that relates the key and - values. + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. properties: key: description: The label key that the selector applies to. type: string operator: - description: Represents a key's relationship - to a set of values. Valid operators - are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: - description: An array of string values. - If the operator is In or NotIn, the - values array must be non-empty. If the - operator is Exists or DoesNotExist, - the values array must be empty. If the - operator is Gt or Lt, the values array - must have a single element, which will - be interpreted as an integer. This array - is replaced during a strategic merge - patch. + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic type: object x-kubernetes-map-type: atomic weight: @@ -163,103 +156,96 @@ spec: - weight type: object type: array + x-kubernetes-list-type: atomic requiredDuringSchedulingIgnoredDuringExecution: - description: If the affinity requirements specified by - this field are not met at scheduling time, the pod will - not be scheduled onto the node. If the affinity requirements - specified by this field cease to be met at some point - during pod execution (e.g. due to an update), the system - may or may not try to eventually evict the pod from - its node. + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from its node. properties: nodeSelectorTerms: description: Required. A list of node selector terms. The terms are ORed. items: - description: A null or empty node selector term - matches no objects. The requirements of them are - ANDed. The TopologySelectorTerm type implements - a subset of the NodeSelectorTerm. + description: |- + A null or empty node selector term matches no objects. The requirements of + them are ANDed. + The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. properties: matchExpressions: description: A list of node selector requirements by node's labels. items: - description: A node selector requirement is - a selector that contains values, a key, - and an operator that relates the key and - values. + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. properties: key: description: The label key that the selector applies to. type: string operator: - description: Represents a key's relationship - to a set of values. Valid operators - are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: - description: An array of string values. - If the operator is In or NotIn, the - values array must be non-empty. If the - operator is Exists or DoesNotExist, - the values array must be empty. If the - operator is Gt or Lt, the values array - must have a single element, which will - be interpreted as an integer. This array - is replaced during a strategic merge - patch. + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchFields: description: A list of node selector requirements by node's fields. items: - description: A node selector requirement is - a selector that contains values, a key, - and an operator that relates the key and - values. + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. properties: key: description: The label key that the selector applies to. type: string operator: - description: Represents a key's relationship - to a set of values. Valid operators - are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: - description: An array of string values. - If the operator is In or NotIn, the - values array must be non-empty. If the - operator is Exists or DoesNotExist, - the values array must be empty. If the - operator is Gt or Lt, the values array - must have a single element, which will - be interpreted as an integer. This array - is replaced during a strategic merge - patch. + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic type: object x-kubernetes-map-type: atomic type: array + x-kubernetes-list-type: atomic required: - nodeSelectorTerms type: object @@ -271,18 +257,16 @@ spec: other pod(s)). properties: preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule pods - to nodes that satisfy the affinity expressions specified - by this field, but it may choose a node that violates - one or more of the expressions. The node that is most - preferred is the one with the greatest sum of weights, - i.e. for each node that meets all of the scheduling - requirements (resource request, requiredDuringScheduling - affinity expressions, etc.), compute a sum by iterating - through the elements of this field and adding "weight" - to the sum if the node has pods which matches the corresponding - podAffinityTerm; the node(s) with the highest sum are - the most preferred. + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. items: description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred @@ -293,143 +277,161 @@ spec: with the corresponding weight. properties: labelSelector: - description: A label query over a set of resources, - in this case pods. + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied - to the union of the namespaces selected by - this field and the ones listed in the namespaces - field. null selector and null or empty namespaces - list means "this pod's namespace". An empty - selector ({}) matches all namespaces. + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: - description: namespaces specifies a static list - of namespace names that the term applies to. - The term is applied to the union of the namespaces - listed in this field and the ones selected - by namespaceSelector. null or empty namespaces - list and null namespaceSelector means "this - pod's namespace". + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array + x-kubernetes-list-type: atomic topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the - pods matching the labelSelector in the specified - namespaces, where co-located is defined as - running on a node whose value of the label - with key topologyKey matches that of any node - on which any of the selected pods is running. + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. Empty topologyKey is not allowed. type: string required: - topologyKey type: object weight: - description: weight associated with matching the - corresponding podAffinityTerm, in the range 1-100. + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. format: int32 type: integer required: @@ -437,157 +439,179 @@ spec: - weight type: object type: array + x-kubernetes-list-type: atomic requiredDuringSchedulingIgnoredDuringExecution: - description: If the affinity requirements specified by - this field are not met at scheduling time, the pod will - not be scheduled onto the node. If the affinity requirements - specified by this field cease to be met at some point - during pod execution (e.g. due to a pod label update), - the system may or may not try to eventually evict the - pod from its node. When there are multiple elements, - the lists of nodes corresponding to each podAffinityTerm - are intersected, i.e. all terms must be satisfied. + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. items: - description: Defines a set of pods (namely those matching - the labelSelector relative to the given namespace(s)) - that this pod should be co-located (affinity) or not - co-located (anti-affinity) with, where co-located - is defined as running on a node whose value of the - label with key matches that of any node - on which a pod of the set of pods is running + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running properties: labelSelector: - description: A label query over a set of resources, - in this case pods. + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, a key, - and an operator that relates the key and - values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a key's - relationship to a set of values. Valid - operators are In, NotIn, Exists and - DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. This - array is replaced during a strategic + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is - "In", and the values array contains only "value". - The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied - to the union of the namespaces selected by this - field and the ones listed in the namespaces field. - null selector and null or empty namespaces list - means "this pod's namespace". An empty selector - ({}) matches all namespaces. + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, a key, - and an operator that relates the key and - values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a key's - relationship to a set of values. Valid - operators are In, NotIn, Exists and - DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. This - array is replaced during a strategic + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is - "In", and the values array contains only "value". - The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: - description: namespaces specifies a static list - of namespace names that the term applies to. The - term is applied to the union of the namespaces - listed in this field and the ones selected by - namespaceSelector. null or empty namespaces list - and null namespaceSelector means "this pod's namespace". + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array + x-kubernetes-list-type: atomic topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the pods - matching the labelSelector in the specified namespaces, - where co-located is defined as running on a node - whose value of the label with key topologyKey - matches that of any node on which any of the selected - pods is running. Empty topologyKey is not allowed. + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. type: string required: - topologyKey type: object type: array + x-kubernetes-list-type: atomic type: object podAntiAffinity: description: Describes pod anti-affinity scheduling rules @@ -595,18 +619,16 @@ spec: as some other pod(s)). properties: preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule pods - to nodes that satisfy the anti-affinity expressions - specified by this field, but it may choose a node that - violates one or more of the expressions. The node that - is most preferred is the one with the greatest sum of - weights, i.e. for each node that meets all of the scheduling - requirements (resource request, requiredDuringScheduling - anti-affinity expressions, etc.), compute a sum by iterating - through the elements of this field and adding "weight" - to the sum if the node has pods which matches the corresponding - podAffinityTerm; the node(s) with the highest sum are - the most preferred. + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the anti-affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. items: description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred @@ -617,143 +639,161 @@ spec: with the corresponding weight. properties: labelSelector: - description: A label query over a set of resources, - in this case pods. + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied - to the union of the namespaces selected by - this field and the ones listed in the namespaces - field. null selector and null or empty namespaces - list means "this pod's namespace". An empty - selector ({}) matches all namespaces. + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: - description: namespaces specifies a static list - of namespace names that the term applies to. - The term is applied to the union of the namespaces - listed in this field and the ones selected - by namespaceSelector. null or empty namespaces - list and null namespaceSelector means "this - pod's namespace". + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array + x-kubernetes-list-type: atomic topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the - pods matching the labelSelector in the specified - namespaces, where co-located is defined as - running on a node whose value of the label - with key topologyKey matches that of any node - on which any of the selected pods is running. + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. Empty topologyKey is not allowed. type: string required: - topologyKey type: object weight: - description: weight associated with matching the - corresponding podAffinityTerm, in the range 1-100. + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. format: int32 type: integer required: @@ -761,157 +801,179 @@ spec: - weight type: object type: array + x-kubernetes-list-type: atomic requiredDuringSchedulingIgnoredDuringExecution: - description: If the anti-affinity requirements specified - by this field are not met at scheduling time, the pod - will not be scheduled onto the node. If the anti-affinity - requirements specified by this field cease to be met - at some point during pod execution (e.g. due to a pod - label update), the system may or may not try to eventually - evict the pod from its node. When there are multiple - elements, the lists of nodes corresponding to each podAffinityTerm - are intersected, i.e. all terms must be satisfied. + description: |- + If the anti-affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the anti-affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. items: - description: Defines a set of pods (namely those matching - the labelSelector relative to the given namespace(s)) - that this pod should be co-located (affinity) or not - co-located (anti-affinity) with, where co-located - is defined as running on a node whose value of the - label with key matches that of any node - on which a pod of the set of pods is running + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running properties: labelSelector: - description: A label query over a set of resources, - in this case pods. + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, a key, - and an operator that relates the key and - values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a key's - relationship to a set of values. Valid - operators are In, NotIn, Exists and - DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. This - array is replaced during a strategic + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is - "In", and the values array contains only "value". - The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied - to the union of the namespaces selected by this - field and the ones listed in the namespaces field. - null selector and null or empty namespaces list - means "this pod's namespace". An empty selector - ({}) matches all namespaces. + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, a key, - and an operator that relates the key and - values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a key's - relationship to a set of values. Valid - operators are In, NotIn, Exists and - DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. This - array is replaced during a strategic + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is - "In", and the values array contains only "value". - The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: - description: namespaces specifies a static list - of namespace names that the term applies to. The - term is applied to the union of the namespaces - listed in this field and the ones selected by - namespaceSelector. null or empty namespaces list - and null namespaceSelector means "this pod's namespace". + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array + x-kubernetes-list-type: atomic topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the pods - matching the labelSelector in the specified namespaces, - where co-located is defined as running on a node - whose value of the label with key topologyKey - matches that of any node on which any of the selected - pods is running. Empty topologyKey is not allowed. + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. type: string required: - topologyKey type: object type: array + x-kubernetes-list-type: atomic type: object type: object annotations: @@ -922,15 +984,16 @@ spec: retrieve arbitrary metadata. type: object api: - description: Vector API params. Allows to interact with a running - Vector instance. https://vector.dev/docs/reference/api/ + description: |- + Vector API params. Allows to interact with a running Vector instance. + https://vector.dev/docs/reference/api/ properties: enabled: type: boolean healthcheck: - description: Enable ReadinessProbe and LivenessProbe via api - /health endpoint. If probe enabled via VectorAgent, this - setting will be ignored for that probe. + description: |- + Enable ReadinessProbe and LivenessProbe via api /health endpoint. + If probe enabled via VectorAgent or VectorAggregator, this setting will be ignored for that probe. type: boolean playground: type: boolean @@ -950,23 +1013,20 @@ spec: for the pod. properties: preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule - pods to nodes that satisfy the affinity expressions - specified by this field, but it may choose a node - that violates one or more of the expressions. The - node that is most preferred is the one with the - greatest sum of weights, i.e. for each node that - meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, - etc.), compute a sum by iterating through the elements - of this field and adding "weight" to the sum if - the node matches the corresponding matchExpressions; - the node(s) with the highest sum are the most preferred. + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. items: - description: An empty preferred scheduling term - matches all objects with implicit weight 0 (i.e. - it's a no-op). A null preferred scheduling term - matches no objects (i.e. is also a no-op). + description: |- + An empty preferred scheduling term matches all objects with implicit weight 0 + (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). properties: preference: description: A node selector term, associated @@ -976,78 +1036,70 @@ spec: description: A list of node selector requirements by node's labels. items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. properties: key: description: The label key that the selector applies to. type: string operator: - description: Represents a key's relationship - to a set of values. Valid operators - are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: - description: An array of string values. - If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. - If the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchFields: description: A list of node selector requirements by node's fields. items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. properties: key: description: The label key that the selector applies to. type: string operator: - description: Represents a key's relationship - to a set of values. Valid operators - are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: - description: An array of string values. - If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. - If the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic type: object x-kubernetes-map-type: atomic weight: @@ -1061,103 +1113,96 @@ spec: - weight type: object type: array + x-kubernetes-list-type: atomic requiredDuringSchedulingIgnoredDuringExecution: - description: If the affinity requirements specified - by this field are not met at scheduling time, the - pod will not be scheduled onto the node. If the - affinity requirements specified by this field cease - to be met at some point during pod execution (e.g. - due to an update), the system may or may not try - to eventually evict the pod from its node. + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from its node. properties: nodeSelectorTerms: description: Required. A list of node selector terms. The terms are ORed. items: - description: A null or empty node selector term - matches no objects. The requirements of them - are ANDed. The TopologySelectorTerm type implements - a subset of the NodeSelectorTerm. + description: |- + A null or empty node selector term matches no objects. The requirements of + them are ANDed. + The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. properties: matchExpressions: description: A list of node selector requirements by node's labels. items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. properties: key: description: The label key that the selector applies to. type: string operator: - description: Represents a key's relationship - to a set of values. Valid operators - are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: - description: An array of string values. - If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. - If the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchFields: description: A list of node selector requirements by node's fields. items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. properties: key: description: The label key that the selector applies to. type: string operator: - description: Represents a key's relationship - to a set of values. Valid operators - are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: - description: An array of string values. - If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. - If the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic type: object x-kubernetes-map-type: atomic type: array + x-kubernetes-list-type: atomic required: - nodeSelectorTerms type: object @@ -1169,19 +1214,16 @@ spec: other pod(s)). properties: preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule - pods to nodes that satisfy the affinity expressions - specified by this field, but it may choose a node - that violates one or more of the expressions. The - node that is most preferred is the one with the - greatest sum of weights, i.e. for each node that - meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, - etc.), compute a sum by iterating through the elements - of this field and adding "weight" to the sum if - the node has pods which matches the corresponding - podAffinityTerm; the node(s) with the highest sum - are the most preferred. + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. items: description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred @@ -1192,18 +1234,18 @@ spec: associated with the corresponding weight. properties: labelSelector: - description: A label query over a set of - resources, in this case pods. + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label @@ -1211,61 +1253,82 @@ spec: to. type: string operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. - This array is replaced during - a strategic merge patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string - description: matchLabels is a map of - {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic namespaceSelector: - description: A label query over the set - of namespaces that the term applies to. - The term is applied to the union of the - namespaces selected by this field and - the ones listed in the namespaces field. - null selector and null or empty namespaces - list means "this pod's namespace". An - empty selector ({}) matches all namespaces. + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label @@ -1273,71 +1336,61 @@ spec: to. type: string operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. - This array is replaced during - a strategic merge patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string - description: matchLabels is a map of - {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: - description: namespaces specifies a static - list of namespace names that the term - applies to. The term is applied to the - union of the namespaces listed in this - field and the ones selected by namespaceSelector. - null or empty namespaces list and null - namespaceSelector means "this pod's namespace". + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array + x-kubernetes-list-type: atomic topologyKey: - description: This pod should be co-located - (affinity) or not co-located (anti-affinity) - with the pods matching the labelSelector - in the specified namespaces, where co-located - is defined as running on a node whose - value of the label with key topologyKey - matches that of any node on which any - of the selected pods is running. Empty - topologyKey is not allowed. + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. type: string required: - topologyKey type: object weight: - description: weight associated with matching - the corresponding podAffinityTerm, in the - range 1-100. + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. format: int32 type: integer required: @@ -1345,163 +1398,179 @@ spec: - weight type: object type: array + x-kubernetes-list-type: atomic requiredDuringSchedulingIgnoredDuringExecution: - description: If the affinity requirements specified - by this field are not met at scheduling time, the - pod will not be scheduled onto the node. If the - affinity requirements specified by this field cease - to be met at some point during pod execution (e.g. - due to a pod label update), the system may or may - not try to eventually evict the pod from its node. - When there are multiple elements, the lists of nodes - corresponding to each podAffinityTerm are intersected, - i.e. all terms must be satisfied. + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. items: - description: Defines a set of pods (namely those - matching the labelSelector relative to the given - namespace(s)) that this pod should be co-located - (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node - whose value of the label with key - matches that of any node on which a pod of the - set of pods is running + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running properties: labelSelector: - description: A label query over a set of resources, - in this case pods. + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied - to the union of the namespaces selected by - this field and the ones listed in the namespaces - field. null selector and null or empty namespaces - list means "this pod's namespace". An empty - selector ({}) matches all namespaces. + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: - description: namespaces specifies a static list - of namespace names that the term applies to. - The term is applied to the union of the namespaces - listed in this field and the ones selected - by namespaceSelector. null or empty namespaces - list and null namespaceSelector means "this - pod's namespace". + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array + x-kubernetes-list-type: atomic topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the - pods matching the labelSelector in the specified - namespaces, where co-located is defined as - running on a node whose value of the label - with key topologyKey matches that of any node - on which any of the selected pods is running. + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. Empty topologyKey is not allowed. type: string required: - topologyKey type: object type: array + x-kubernetes-list-type: atomic type: object podAntiAffinity: description: Describes pod anti-affinity scheduling rules @@ -1509,19 +1578,16 @@ spec: etc. as some other pod(s)). properties: preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule - pods to nodes that satisfy the anti-affinity expressions - specified by this field, but it may choose a node - that violates one or more of the expressions. The - node that is most preferred is the one with the - greatest sum of weights, i.e. for each node that - meets all of the scheduling requirements (resource - request, requiredDuringScheduling anti-affinity - expressions, etc.), compute a sum by iterating through - the elements of this field and adding "weight" to - the sum if the node has pods which matches the corresponding - podAffinityTerm; the node(s) with the highest sum - are the most preferred. + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the anti-affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. items: description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred @@ -1532,18 +1598,18 @@ spec: associated with the corresponding weight. properties: labelSelector: - description: A label query over a set of - resources, in this case pods. + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label @@ -1551,61 +1617,82 @@ spec: to. type: string operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. - This array is replaced during - a strategic merge patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string - description: matchLabels is a map of - {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic namespaceSelector: - description: A label query over the set - of namespaces that the term applies to. - The term is applied to the union of the - namespaces selected by this field and - the ones listed in the namespaces field. - null selector and null or empty namespaces - list means "this pod's namespace". An - empty selector ({}) matches all namespaces. + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label @@ -1613,71 +1700,61 @@ spec: to. type: string operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. - This array is replaced during - a strategic merge patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string - description: matchLabels is a map of - {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: - description: namespaces specifies a static - list of namespace names that the term - applies to. The term is applied to the - union of the namespaces listed in this - field and the ones selected by namespaceSelector. - null or empty namespaces list and null - namespaceSelector means "this pod's namespace". + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array + x-kubernetes-list-type: atomic topologyKey: - description: This pod should be co-located - (affinity) or not co-located (anti-affinity) - with the pods matching the labelSelector - in the specified namespaces, where co-located - is defined as running on a node whose - value of the label with key topologyKey - matches that of any node on which any - of the selected pods is running. Empty - topologyKey is not allowed. + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. type: string required: - topologyKey type: object weight: - description: weight associated with matching - the corresponding podAffinityTerm, in the - range 1-100. + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. format: int32 type: integer required: @@ -1685,163 +1762,179 @@ spec: - weight type: object type: array + x-kubernetes-list-type: atomic requiredDuringSchedulingIgnoredDuringExecution: - description: If the anti-affinity requirements specified - by this field are not met at scheduling time, the - pod will not be scheduled onto the node. If the - anti-affinity requirements specified by this field - cease to be met at some point during pod execution - (e.g. due to a pod label update), the system may - or may not try to eventually evict the pod from - its node. When there are multiple elements, the - lists of nodes corresponding to each podAffinityTerm - are intersected, i.e. all terms must be satisfied. + description: |- + If the anti-affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the anti-affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. items: - description: Defines a set of pods (namely those - matching the labelSelector relative to the given - namespace(s)) that this pod should be co-located - (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node - whose value of the label with key - matches that of any node on which a pod of the - set of pods is running + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running properties: labelSelector: - description: A label query over a set of resources, - in this case pods. + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied - to the union of the namespaces selected by - this field and the ones listed in the namespaces - field. null selector and null or empty namespaces - list means "this pod's namespace". An empty - selector ({}) matches all namespaces. + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: - description: namespaces specifies a static list - of namespace names that the term applies to. - The term is applied to the union of the namespaces - listed in this field and the ones selected - by namespaceSelector. null or empty namespaces - list and null namespaceSelector means "this - pod's namespace". + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array + x-kubernetes-list-type: atomic topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the - pods matching the labelSelector in the specified - namespaces, where co-located is defined as - running on a node whose value of the label - with key topologyKey matches that of any node - on which any of the selected pods is running. + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. Empty topologyKey is not allowed. type: string required: - topologyKey type: object type: array + x-kubernetes-list-type: atomic type: object type: object annotations: @@ -1854,14 +1947,46 @@ spec: disabled: type: boolean image: - description: Image - docker image settings for Vector Agent + description: |- + Image - docker image settings for Vector Agent if no specified operator uses default config version type: string resources: - description: Resources container resource request and limits, - https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + description: |- + Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ if not specified - default setting will be used properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map limits: additionalProperties: anyOf: @@ -1869,8 +1994,9 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute - resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object requests: additionalProperties: @@ -1879,52 +2005,49 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of - compute resources required. If Requests is omitted for - a container, it defaults to Limits if that is explicitly - specified, otherwise to an implementation-defined value. - More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object type: object tolerations: description: Tolerations If specified, the pod's tolerations. items: - description: The pod this Toleration is attached to tolerates - any taint that matches the triple using - the matching operator . + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . properties: effect: - description: Effect indicates the taint effect to match. - Empty means match all taint effects. When specified, - allowed values are NoSchedule, PreferNoSchedule and - NoExecute. + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. type: string key: - description: Key is the taint key that the toleration - applies to. Empty means match all taint keys. If the - key is empty, operator must be Exists; this combination - means to match all values and all keys. + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. type: string operator: - description: Operator represents a key's relationship - to the value. Valid operators are Exists and Equal. - Defaults to Equal. Exists is equivalent to wildcard - for value, so that a pod can tolerate all taints of - a particular category. + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. type: string tolerationSeconds: - description: TolerationSeconds represents the period - of time the toleration (which must be of effect NoExecute, - otherwise this field is ignored) tolerates the taint. - By default, it is not set, which means tolerate the - taint forever (do not evict). Zero and negative values - will be treated as 0 (evict immediately) by the system. + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. format: int64 type: integer value: - description: Value is the taint value the toleration - matches to. If the operator is Exists, the value should - be empty, otherwise just a regular string. + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. type: string type: object type: array @@ -1935,6 +2058,37 @@ spec: description: ResourceRequirements describes the compute resource requirements. properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map limits: additionalProperties: anyOf: @@ -1942,8 +2096,9 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute - resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object requests: additionalProperties: @@ -1952,32 +2107,58 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, otherwise - to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object type: object containerSecurityContext: - description: SecurityContext holds security configuration that - will be applied to a container. Some fields are present in both - SecurityContext and PodSecurityContext. When both are set, the - values in SecurityContext take precedence. + description: |- + SecurityContext holds security configuration that will be applied to a container. + Some fields are present in both SecurityContext and PodSecurityContext. + When both are set, the values in SecurityContext take precedence. properties: allowPrivilegeEscalation: - description: 'AllowPrivilegeEscalation controls whether a - process can gain more privileges than its parent process. - This bool directly controls if the no_new_privs flag will - be set on the container process. AllowPrivilegeEscalation - is true always when the container is: 1) run as Privileged - 2) has CAP_SYS_ADMIN Note that this field cannot be set - when spec.os.name is windows.' + description: |- + AllowPrivilegeEscalation controls whether a process can gain more + privileges than its parent process. This bool directly controls if + the no_new_privs flag will be set on the container process. + AllowPrivilegeEscalation is true always when the container is: + 1) run as Privileged + 2) has CAP_SYS_ADMIN + Note that this field cannot be set when spec.os.name is windows. type: boolean + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by this container. If set, this profile + overrides the pod's appArmorProfile. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object capabilities: - description: The capabilities to add/drop when running containers. - Defaults to the default set of capabilities granted by the - container runtime. Note that this field cannot be set when - spec.os.name is windows. + description: |- + The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container runtime. + Note that this field cannot be set when spec.os.name is windows. properties: add: description: Added capabilities @@ -1986,6 +2167,7 @@ spec: type type: string type: array + x-kubernetes-list-type: atomic drop: description: Removed capabilities items: @@ -1993,61 +2175,63 @@ spec: type type: string type: array + x-kubernetes-list-type: atomic type: object privileged: - description: Run container in privileged mode. Processes in - privileged containers are essentially equivalent to root - on the host. Defaults to false. Note that this field cannot - be set when spec.os.name is windows. + description: |- + Run container in privileged mode. + Processes in privileged containers are essentially equivalent to root on the host. + Defaults to false. + Note that this field cannot be set when spec.os.name is windows. type: boolean procMount: - description: procMount denotes the type of proc mount to use - for the containers. The default is DefaultProcMount which - uses the container runtime defaults for readonly paths and - masked paths. This requires the ProcMountType feature flag - to be enabled. Note that this field cannot be set when spec.os.name - is windows. + description: |- + procMount denotes the type of proc mount to use for the containers. + The default value is Default which uses the container runtime defaults for + readonly paths and masked paths. + This requires the ProcMountType feature flag to be enabled. + Note that this field cannot be set when spec.os.name is windows. type: string readOnlyRootFilesystem: - description: Whether this container has a read-only root filesystem. - Default is false. Note that this field cannot be set when - spec.os.name is windows. + description: |- + Whether this container has a read-only root filesystem. + Default is false. + Note that this field cannot be set when spec.os.name is windows. type: boolean runAsGroup: - description: The GID to run the entrypoint of the container - process. Uses runtime default if unset. May also be set - in PodSecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext - takes precedence. Note that this field cannot be set when - spec.os.name is windows. + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. format: int64 type: integer runAsNonRoot: - description: Indicates that the container must run as a non-root - user. If true, the Kubelet will validate the image at runtime - to ensure that it does not run as UID 0 (root) and fail - to start the container if it does. If unset or false, no - such validation will be performed. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, the - value specified in SecurityContext takes precedence. + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. type: boolean runAsUser: - description: The UID to run the entrypoint of the container - process. Defaults to user specified in image metadata if - unspecified. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, the - value specified in SecurityContext takes precedence. Note - that this field cannot be set when spec.os.name is windows. + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. format: int64 type: integer seLinuxOptions: - description: The SELinux context to be applied to the container. - If unspecified, the container runtime will allocate a random - SELinux context for each container. May also be set in - PodSecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext - takes precedence. Note that this field cannot be set when - spec.os.name is windows. + description: |- + The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. properties: level: description: Level is SELinux level label that applies @@ -2067,72 +2251,67 @@ spec: type: string type: object seccompProfile: - description: The seccomp options to use by this container. - If seccomp options are provided at both the pod & container - level, the container options override the pod options. Note - that this field cannot be set when spec.os.name is windows. + description: |- + The seccomp options to use by this container. If seccomp options are + provided at both the pod & container level, the container options + override the pod options. + Note that this field cannot be set when spec.os.name is windows. properties: localhostProfile: - description: localhostProfile indicates a profile defined - in a file on the node should be used. The profile must - be preconfigured on the node to work. Must be a descending - path, relative to the kubelet's configured seccomp profile - location. Must only be set if type is "Localhost". + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. type: string type: - description: "type indicates which kind of seccomp profile - will be applied. Valid options are: \n Localhost - a - profile defined in a file on the node should be used. - RuntimeDefault - the container runtime default profile - should be used. Unconfined - no profile should be applied." + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. type: string required: - type type: object windowsOptions: - description: The Windows specific settings applied to all - containers. If unspecified, the options from the PodSecurityContext - will be used. If set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence. - Note that this field cannot be set when spec.os.name is - linux. + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options from the PodSecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. properties: gmsaCredentialSpec: - description: GMSACredentialSpec is where the GMSA admission - webhook (https://github.com/kubernetes-sigs/windows-gmsa) - inlines the contents of the GMSA credential spec named - by the GMSACredentialSpecName field. + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. type: string gmsaCredentialSpecName: description: GMSACredentialSpecName is the name of the GMSA credential spec to use. type: string hostProcess: - description: HostProcess determines if a container should - be run as a 'Host Process' container. This field is - alpha-level and will only be honored by components that - enable the WindowsHostProcessContainers feature flag. - Setting this field without the feature flag will result - in errors when validating the Pod. All of a Pod's containers - must have the same effective HostProcess value (it is - not allowed to have a mix of HostProcess containers - and non-HostProcess containers). In addition, if HostProcess - is true then HostNetwork must also be set to true. + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. type: boolean runAsUserName: - description: The UserName in Windows to run the entrypoint - of the container process. Defaults to the user specified - in image metadata if unspecified. May also be set in - PodSecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext - takes precedence. + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. type: string type: object type: object dataDir: - description: The directory used for persisting Vector state, such - as on-disk buffers, file checkpoints, and more. Please make - sure the Vector project has write permissions to this directory. + description: |- + The directory used for persisting Vector state, such as on-disk buffers, file checkpoints, and more. Please make sure the Vector project has write permissions to this directory. https://vector.dev/docs/reference/configuration/global-options/#data_dir type: string env: @@ -2146,15 +2325,16 @@ spec: C_IDENTIFIER. type: string value: - description: 'Variable references $(VAR_NAME) are expanded - using the previously defined environment variables in - the container and any service environment variables. If - a variable cannot be resolved, the reference in the input - string will be unchanged. Double $$ are reduced to a single - $, which allows for escaping the $(VAR_NAME) syntax: i.e. + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". - Escaped references will never be expanded, regardless - of whether the variable exists or not. Defaults to "".' + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". type: string valueFrom: description: Source for the environment variable's value. @@ -2167,9 +2347,13 @@ spec: description: The key to select. type: string name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string optional: description: Specify whether the ConfigMap or its @@ -2180,11 +2364,9 @@ spec: type: object x-kubernetes-map-type: atomic fieldRef: - description: 'Selects a field of the pod: supports metadata.name, - metadata.namespace, `metadata.labels['''']`, - `metadata.annotations['''']`, spec.nodeName, - spec.serviceAccountName, status.hostIP, status.podIP, - status.podIPs.' + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. properties: apiVersion: description: Version of the schema the FieldPath @@ -2199,10 +2381,9 @@ spec: type: object x-kubernetes-map-type: atomic resourceFieldRef: - description: 'Selects a resource of the container: only - resources limits and requests (limits.cpu, limits.memory, - limits.ephemeral-storage, requests.cpu, requests.memory - and requests.ephemeral-storage) are currently supported.' + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. properties: containerName: description: 'Container name: required for volumes, @@ -2232,9 +2413,13 @@ spec: be a valid secret key. type: string name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string optional: description: Specify whether the Secret or its key @@ -2250,20 +2435,26 @@ spec: type: object type: array host_aliases: - description: HostAliases provides mapping between ip and hostnames, - that would be propagated to pod, cannot be used with HostNetwork. + description: |- + HostAliases provides mapping between ip and hostnames, + that would be propagated to pod, + cannot be used with HostNetwork. items: - description: HostAlias holds the mapping between IP and hostnames - that will be injected as an entry in the pod's hosts file. + description: |- + HostAlias holds the mapping between IP and hostnames that will be injected as an entry in the + pod's hosts file. properties: hostnames: description: Hostnames for the above IP address. items: type: string type: array + x-kubernetes-list-type: atomic ip: description: IP address of the host file entry. type: string + required: + - ip type: object type: array hostNetwork: @@ -2271,23 +2462,31 @@ spec: node network namespace type: boolean image: - description: Image - docker image settings for Vector Agent if - no specified operator uses default config version + description: |- + Image - docker image settings for Vector Agent + if no specified operator uses default config version type: string imagePullPolicy: description: ImagePullPolicy of pods type: string imagePullSecrets: - description: ImagePullSecrets An optional list of references to - secrets in the same namespace to use for pulling images from - registries see http://kubernetes.io/docs/user-guide/images#specifying-imagepullsecrets-on-a-pod + description: |- + ImagePullSecrets An optional list of references to secrets in the same namespace + to use for pulling images from registries + see http://kubernetes.io/docs/user-guide/images#specifying-imagepullsecrets-on-a-pod items: - description: LocalObjectReference contains enough information - to let you locate the referenced object inside the same namespace. + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. properties: name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string type: object x-kubernetes-map-type: atomic @@ -2303,28 +2502,25 @@ spec: description: Exec specifies the action to take. properties: command: - description: Command is the command line to execute inside - the container, the working directory for the command is - root ('/') in the container's filesystem. The command - is simply exec'd, it is not run inside a shell, so traditional - shell instructions ('|', etc) won't work. To use a shell, - you need to explicitly call out to that shell. Exit - status of 0 is treated as live/healthy and non-zero - is unhealthy. + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. items: type: string type: array + x-kubernetes-list-type: atomic type: object failureThreshold: - description: Minimum consecutive failures for the probe to - be considered failed after having succeeded. Defaults to - 3. Minimum value is 1. + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. format: int32 type: integer grpc: description: GRPC specifies an action involving a GRPC port. - This is a beta field and requires enabling GRPCContainerProbe - feature gate. properties: port: description: Port number of the gRPC service. Number must @@ -2332,10 +2528,12 @@ spec: format: int32 type: integer service: - description: "Service is the name of the service to place - in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). - \n If this is not specified, the default behavior is - defined by gRPC." + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. type: string required: - port @@ -2344,9 +2542,9 @@ spec: description: HTTPGet specifies the http request to perform. properties: host: - description: Host name to connect to, defaults to the - pod IP. You probably want to set "Host" in httpHeaders - instead. + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. type: string httpHeaders: description: Custom headers to set in the request. HTTP @@ -2356,7 +2554,9 @@ spec: be used in HTTP probes properties: name: - description: The header field name + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. type: string value: description: The header field value @@ -2366,6 +2566,7 @@ spec: - value type: object type: array + x-kubernetes-list-type: atomic path: description: Path to access on the HTTP server. type: string @@ -2373,32 +2574,35 @@ spec: anyOf: - type: integer - type: string - description: Name or number of the port to access on the - container. Number must be in the range 1 to 65535. Name - must be an IANA_SVC_NAME. + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true scheme: - description: Scheme to use for connecting to the host. + description: |- + Scheme to use for connecting to the host. Defaults to HTTP. type: string required: - port type: object initialDelaySeconds: - description: 'Number of seconds after the container has started - before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes format: int32 type: integer periodSeconds: - description: How often (in seconds) to perform the probe. + description: |- + How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1. format: int32 type: integer successThreshold: - description: Minimum consecutive successes for the probe to - be considered successful after having failed. Defaults to - 1. Must be 1 for liveness and startup. Minimum value is - 1. + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. format: int32 type: integer tcpSocket: @@ -2413,100 +2617,125 @@ spec: anyOf: - type: integer - type: string - description: Number or name of the port to access on the - container. Number must be in the range 1 to 65535. Name - must be an IANA_SVC_NAME. + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true required: - port type: object terminationGracePeriodSeconds: - description: Optional duration in seconds the pod needs to - terminate gracefully upon probe failure. The grace period - is the duration in seconds after the processes running in - the pod are sent a termination signal and the time when - the processes are forcibly halted with a kill signal. Set - this value longer than the expected cleanup time for your - process. If this value is nil, the pod's terminationGracePeriodSeconds - will be used. Otherwise, this value overrides the value - provided by the pod spec. Value must be non-negative integer. - The value zero indicates stop immediately via the kill signal - (no opportunity to shut down). This is a beta field and - requires enabling ProbeTerminationGracePeriod feature gate. - Minimum value is 1. spec.terminationGracePeriodSeconds is - used if unset. + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. format: int64 type: integer timeoutSeconds: - description: 'Number of seconds after which the probe times - out. Defaults to 1 second. Minimum value is 1. More info: - https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes format: int32 type: integer type: object podSecurityContext: - description: SecurityContext holds pod-level security attributes - and common container settings. This defaults to the default - PodSecurityContext. + description: |- + SecurityContext holds pod-level security attributes and common container settings. + This defaults to the default PodSecurityContext. properties: + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by the containers in this pod. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object fsGroup: - description: "A special supplemental group that applies to - all containers in a pod. Some volume types allow the Kubelet - to change the ownership of that volume to be owned by the - pod: \n 1. The owning GID will be the FSGroup 2. The setgid - bit is set (new files created in the volume will be owned - by FSGroup) 3. The permission bits are OR'd with rw-rw---- - \n If unset, the Kubelet will not modify the ownership and - permissions of any volume. Note that this field cannot be - set when spec.os.name is windows." + description: |- + A special supplemental group that applies to all containers in a pod. + Some volume types allow the Kubelet to change the ownership of that volume + to be owned by the pod: + + 1. The owning GID will be the FSGroup + 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) + 3. The permission bits are OR'd with rw-rw---- + + If unset, the Kubelet will not modify the ownership and permissions of any volume. + Note that this field cannot be set when spec.os.name is windows. format: int64 type: integer fsGroupChangePolicy: - description: 'fsGroupChangePolicy defines behavior of changing - ownership and permission of the volume before being exposed - inside Pod. This field will only apply to volume types which - support fsGroup based ownership(and permissions). It will - have no effect on ephemeral volume types such as: secret, - configmaps and emptydir. Valid values are "OnRootMismatch" - and "Always". If not specified, "Always" is used. Note that - this field cannot be set when spec.os.name is windows.' + description: |- + fsGroupChangePolicy defines behavior of changing ownership and permission of the volume + before being exposed inside Pod. This field will only apply to + volume types which support fsGroup based ownership(and permissions). + It will have no effect on ephemeral volume types such as: secret, configmaps + and emptydir. + Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used. + Note that this field cannot be set when spec.os.name is windows. type: string runAsGroup: - description: The GID to run the entrypoint of the container - process. Uses runtime default if unset. May also be set - in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext - takes precedence for that container. Note that this field - cannot be set when spec.os.name is windows. + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. format: int64 type: integer runAsNonRoot: - description: Indicates that the container must run as a non-root - user. If true, the Kubelet will validate the image at runtime - to ensure that it does not run as UID 0 (root) and fail - to start the container if it does. If unset or false, no - such validation will be performed. May also be set in SecurityContext. If - set in both SecurityContext and PodSecurityContext, the - value specified in SecurityContext takes precedence. + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. type: boolean runAsUser: - description: The UID to run the entrypoint of the container - process. Defaults to user specified in image metadata if - unspecified. May also be set in SecurityContext. If set - in both SecurityContext and PodSecurityContext, the value - specified in SecurityContext takes precedence for that container. - Note that this field cannot be set when spec.os.name is - windows. + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. format: int64 type: integer seLinuxOptions: - description: The SELinux context to be applied to all containers. - If unspecified, the container runtime will allocate a random - SELinux context for each container. May also be set in - SecurityContext. If set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence - for that container. Note that this field cannot be set when - spec.os.name is windows. + description: |- + The SELinux context to be applied to all containers. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in SecurityContext. If set in + both SecurityContext and PodSecurityContext, the value specified in SecurityContext + takes precedence for that container. + Note that this field cannot be set when spec.os.name is windows. properties: level: description: Level is SELinux level label that applies @@ -2526,42 +2755,58 @@ spec: type: string type: object seccompProfile: - description: The seccomp options to use by the containers - in this pod. Note that this field cannot be set when spec.os.name - is windows. + description: |- + The seccomp options to use by the containers in this pod. + Note that this field cannot be set when spec.os.name is windows. properties: localhostProfile: - description: localhostProfile indicates a profile defined - in a file on the node should be used. The profile must - be preconfigured on the node to work. Must be a descending - path, relative to the kubelet's configured seccomp profile - location. Must only be set if type is "Localhost". + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. type: string type: - description: "type indicates which kind of seccomp profile - will be applied. Valid options are: \n Localhost - a - profile defined in a file on the node should be used. - RuntimeDefault - the container runtime default profile - should be used. Unconfined - no profile should be applied." + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. type: string required: - type type: object supplementalGroups: - description: A list of groups applied to the first process - run in each container, in addition to the container's primary - GID. If unspecified, no groups will be added to any container. - Note that this field cannot be set when spec.os.name is - windows. + description: |- + A list of groups applied to the first process run in each container, in + addition to the container's primary GID and fsGroup (if specified). If + the SupplementalGroupsPolicy feature is enabled, the + supplementalGroupsPolicy field determines whether these are in addition + to or instead of any group memberships defined in the container image. + If unspecified, no additional groups are added, though group memberships + defined in the container image may still be used, depending on the + supplementalGroupsPolicy field. + Note that this field cannot be set when spec.os.name is windows. items: format: int64 type: integer type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + description: |- + Defines how supplemental groups of the first container processes are calculated. + Valid values are "Merge" and "Strict". If not specified, "Merge" is used. + (Alpha) Using the field requires the SupplementalGroupsPolicy feature gate to be enabled + and the container runtime must implement support for this feature. + Note that this field cannot be set when spec.os.name is windows. + type: string sysctls: - description: Sysctls hold a list of namespaced sysctls used - for the pod. Pods with unsupported sysctls (by the container - runtime) might fail to launch. Note that this field cannot - be set when spec.os.name is windows. + description: |- + Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported + sysctls (by the container runtime) might fail to launch. + Note that this field cannot be set when spec.os.name is windows. items: description: Sysctl defines a kernel parameter to be set properties: @@ -2576,48 +2821,43 @@ spec: - value type: object type: array + x-kubernetes-list-type: atomic windowsOptions: - description: The Windows specific settings applied to all - containers. If unspecified, the options within a container's - SecurityContext will be used. If set in both SecurityContext - and PodSecurityContext, the value specified in SecurityContext - takes precedence. Note that this field cannot be set when - spec.os.name is linux. + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options within a container's SecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. properties: gmsaCredentialSpec: - description: GMSACredentialSpec is where the GMSA admission - webhook (https://github.com/kubernetes-sigs/windows-gmsa) - inlines the contents of the GMSA credential spec named - by the GMSACredentialSpecName field. + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. type: string gmsaCredentialSpecName: description: GMSACredentialSpecName is the name of the GMSA credential spec to use. type: string hostProcess: - description: HostProcess determines if a container should - be run as a 'Host Process' container. This field is - alpha-level and will only be honored by components that - enable the WindowsHostProcessContainers feature flag. - Setting this field without the feature flag will result - in errors when validating the Pod. All of a Pod's containers - must have the same effective HostProcess value (it is - not allowed to have a mix of HostProcess containers - and non-HostProcess containers). In addition, if HostProcess - is true then HostNetwork must also be set to true. + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. type: boolean runAsUserName: - description: The UserName in Windows to run the entrypoint - of the container process. Defaults to the user specified - in image metadata if unspecified. May also be set in - PodSecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext - takes precedence. + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. type: string type: object type: object podSecurityPolicyName: - description: PodSecurityPolicyName - defines name for podSecurityPolicy + description: |- + PodSecurityPolicyName - defines name for podSecurityPolicy in case of empty value, prefixedName will be used. type: string priorityClassName: @@ -2631,28 +2871,25 @@ spec: description: Exec specifies the action to take. properties: command: - description: Command is the command line to execute inside - the container, the working directory for the command is - root ('/') in the container's filesystem. The command - is simply exec'd, it is not run inside a shell, so traditional - shell instructions ('|', etc) won't work. To use a shell, - you need to explicitly call out to that shell. Exit - status of 0 is treated as live/healthy and non-zero - is unhealthy. + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. items: type: string type: array + x-kubernetes-list-type: atomic type: object failureThreshold: - description: Minimum consecutive failures for the probe to - be considered failed after having succeeded. Defaults to - 3. Minimum value is 1. + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. format: int32 type: integer grpc: description: GRPC specifies an action involving a GRPC port. - This is a beta field and requires enabling GRPCContainerProbe - feature gate. properties: port: description: Port number of the gRPC service. Number must @@ -2660,10 +2897,12 @@ spec: format: int32 type: integer service: - description: "Service is the name of the service to place - in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). - \n If this is not specified, the default behavior is - defined by gRPC." + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. type: string required: - port @@ -2672,9 +2911,9 @@ spec: description: HTTPGet specifies the http request to perform. properties: host: - description: Host name to connect to, defaults to the - pod IP. You probably want to set "Host" in httpHeaders - instead. + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. type: string httpHeaders: description: Custom headers to set in the request. HTTP @@ -2684,7 +2923,9 @@ spec: be used in HTTP probes properties: name: - description: The header field name + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. type: string value: description: The header field value @@ -2694,6 +2935,7 @@ spec: - value type: object type: array + x-kubernetes-list-type: atomic path: description: Path to access on the HTTP server. type: string @@ -2701,32 +2943,35 @@ spec: anyOf: - type: integer - type: string - description: Name or number of the port to access on the - container. Number must be in the range 1 to 65535. Name - must be an IANA_SVC_NAME. + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true scheme: - description: Scheme to use for connecting to the host. + description: |- + Scheme to use for connecting to the host. Defaults to HTTP. type: string required: - port type: object initialDelaySeconds: - description: 'Number of seconds after the container has started - before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes format: int32 type: integer periodSeconds: - description: How often (in seconds) to perform the probe. + description: |- + How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1. format: int32 type: integer successThreshold: - description: Minimum consecutive successes for the probe to - be considered successful after having failed. Defaults to - 1. Must be 1 for liveness and startup. Minimum value is - 1. + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. format: int32 type: integer tcpSocket: @@ -2741,42 +2986,72 @@ spec: anyOf: - type: integer - type: string - description: Number or name of the port to access on the - container. Number must be in the range 1 to 65535. Name - must be an IANA_SVC_NAME. + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true required: - port type: object terminationGracePeriodSeconds: - description: Optional duration in seconds the pod needs to - terminate gracefully upon probe failure. The grace period - is the duration in seconds after the processes running in - the pod are sent a termination signal and the time when - the processes are forcibly halted with a kill signal. Set - this value longer than the expected cleanup time for your - process. If this value is nil, the pod's terminationGracePeriodSeconds - will be used. Otherwise, this value overrides the value - provided by the pod spec. Value must be non-negative integer. - The value zero indicates stop immediately via the kill signal - (no opportunity to shut down). This is a beta field and - requires enabling ProbeTerminationGracePeriod feature gate. - Minimum value is 1. spec.terminationGracePeriodSeconds is - used if unset. + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. format: int64 type: integer timeoutSeconds: - description: 'Number of seconds after which the probe times - out. Defaults to 1 second. Minimum value is 1. More info: - https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes format: int32 type: integer type: object resources: - description: Resources container resource request and limits, - https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + description: |- + Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ if not specified - default setting will be used properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map limits: additionalProperties: anyOf: @@ -2784,8 +3059,9 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute - resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object requests: additionalProperties: @@ -2794,15 +3070,17 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, otherwise - to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object type: object runtimeClassName: - description: RuntimeClassName - defines runtime class for kubernetes - pod. https://kubernetes.io/docs/concepts/containers/runtime-class/ + description: |- + RuntimeClassName - defines runtime class for kubernetes pod. + https://kubernetes.io/docs/concepts/containers/runtime-class/ type: string schedulerName: description: SchedulerName - defines kubernetes scheduler name @@ -2810,41 +3088,39 @@ spec: tolerations: description: Tolerations If specified, the pod's tolerations. items: - description: The pod this Toleration is attached to tolerates - any taint that matches the triple using - the matching operator . + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . properties: effect: - description: Effect indicates the taint effect to match. - Empty means match all taint effects. When specified, allowed - values are NoSchedule, PreferNoSchedule and NoExecute. + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. type: string key: - description: Key is the taint key that the toleration applies - to. Empty means match all taint keys. If the key is empty, - operator must be Exists; this combination means to match - all values and all keys. + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. type: string operator: - description: Operator represents a key's relationship to - the value. Valid operators are Exists and Equal. Defaults - to Equal. Exists is equivalent to wildcard for value, - so that a pod can tolerate all taints of a particular - category. + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. type: string tolerationSeconds: - description: TolerationSeconds represents the period of - time the toleration (which must be of effect NoExecute, - otherwise this field is ignored) tolerates the taint. - By default, it is not set, which means tolerate the taint - forever (do not evict). Zero and negative values will - be treated as 0 (evict immediately) by the system. + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. format: int64 type: integer value: - description: Value is the taint value the toleration matches - to. If the operator is Exists, the value should be empty, - otherwise just a regular string. + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. type: string type: object type: array @@ -2855,33 +3131,57 @@ spec: a container. properties: mountPath: - description: Path within the container at which the volume - should be mounted. Must not contain ':'. + description: |- + Path within the container at which the volume should be mounted. Must + not contain ':'. type: string mountPropagation: - description: mountPropagation determines how mounts are - propagated from the host to container and the other way - around. When not set, MountPropagationNone is used. This - field is beta in 1.10. + description: |- + mountPropagation determines how mounts are propagated from the host + to container and the other way around. + When not set, MountPropagationNone is used. + This field is beta in 1.10. + When RecursiveReadOnly is set to IfPossible or to Enabled, MountPropagation must be None or unspecified + (which defaults to None). type: string name: description: This must match the Name of a Volume. type: string readOnly: - description: Mounted read-only if true, read-write otherwise - (false or unspecified). Defaults to false. + description: |- + Mounted read-only if true, read-write otherwise (false or unspecified). + Defaults to false. type: boolean + recursiveReadOnly: + description: |- + RecursiveReadOnly specifies whether read-only mounts should be handled + recursively. + + If ReadOnly is false, this field has no meaning and must be unspecified. + + If ReadOnly is true, and this field is set to Disabled, the mount is not made + recursively read-only. If this field is set to IfPossible, the mount is made + recursively read-only, if it is supported by the container runtime. If this + field is set to Enabled, the mount is made recursively read-only if it is + supported by the container runtime, otherwise the pod will not be started and + an error will be generated to indicate the reason. + + If this field is set to IfPossible or Enabled, MountPropagation must be set to + None (or be unspecified, which defaults to None). + + If this field is not specified, it is treated as an equivalent of Disabled. + type: string subPath: - description: Path within the volume from which the container's - volume should be mounted. Defaults to "" (volume's root). + description: |- + Path within the volume from which the container's volume should be mounted. + Defaults to "" (volume's root). type: string subPathExpr: - description: Expanded path within the volume from which - the container's volume should be mounted. Behaves similarly - to SubPath but environment variable references $(VAR_NAME) - are expanded using the container's environment. Defaults - to "" (volume's root). SubPathExpr and SubPath are mutually - exclusive. + description: |- + Expanded path within the volume from which the container's volume should be mounted. + Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. + Defaults to "" (volume's root). + SubPathExpr and SubPath are mutually exclusive. type: string required: - mountPath @@ -2896,36 +3196,35 @@ spec: may be accessed by any container in the pod. properties: awsElasticBlockStore: - description: 'awsElasticBlockStore represents an AWS Disk - resource that is attached to a kubelet''s host machine - and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + description: |- + awsElasticBlockStore represents an AWS Disk resource that is attached to a + kubelet's host machine and then exposed to the pod. + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore properties: fsType: - description: 'fsType is the filesystem type of the volume - that you want to mount. Tip: Ensure that the filesystem - type is supported by the host operating system. Examples: - "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" - if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore - TODO: how do we prevent errors in the filesystem from - compromising the machine' + description: |- + fsType is the filesystem type of the volume that you want to mount. + Tip: Ensure that the filesystem type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore type: string partition: - description: 'partition is the partition in the volume - that you want to mount. If omitted, the default is - to mount by volume name. Examples: For volume /dev/sda1, - you specify the partition as "1". Similarly, the volume - partition for /dev/sda is "0" (or you can leave the - property empty).' + description: |- + partition is the partition in the volume that you want to mount. + If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition as "1". + Similarly, the volume partition for /dev/sda is "0" (or you can leave the property empty). format: int32 type: integer readOnly: - description: 'readOnly value true will force the readOnly - setting in VolumeMounts. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + description: |- + readOnly value true will force the readOnly setting in VolumeMounts. + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore type: boolean volumeID: - description: 'volumeID is unique ID of the persistent - disk resource in AWS (Amazon EBS volume). More info: - https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + description: |- + volumeID is unique ID of the persistent disk resource in AWS (Amazon EBS volume). + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore type: string required: - volumeID @@ -2947,10 +3246,11 @@ spec: blob storage type: string fsType: - description: fsType is Filesystem type to mount. Must - be a filesystem type supported by the host operating - system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred - to be "ext4" if unspecified. + default: ext4 + description: |- + fsType is Filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. type: string kind: description: 'kind expected values are Shared: multiple @@ -2960,8 +3260,10 @@ spec: to shared' type: string readOnly: - description: readOnly Defaults to false (read/write). - ReadOnly here will force the ReadOnly setting in VolumeMounts. + default: false + description: |- + readOnly Defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. type: boolean required: - diskName @@ -2972,8 +3274,9 @@ spec: mount on the host and bind mount to the pod. properties: readOnly: - description: readOnly defaults to false (read/write). - ReadOnly here will force the ReadOnly setting in VolumeMounts. + description: |- + readOnly defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. type: boolean secretName: description: secretName is the name of secret that @@ -2991,73 +3294,90 @@ spec: that shares a pod's lifetime properties: monitors: - description: 'monitors is Required: Monitors is a collection - of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + description: |- + monitors is Required: Monitors is a collection of Ceph monitors + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it items: type: string type: array + x-kubernetes-list-type: atomic path: description: 'path is Optional: Used as the mounted root, rather than the full Ceph tree, default is /' type: string readOnly: - description: 'readOnly is Optional: Defaults to false - (read/write). ReadOnly here will force the ReadOnly - setting in VolumeMounts. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + description: |- + readOnly is Optional: Defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it type: boolean secretFile: - description: 'secretFile is Optional: SecretFile is - the path to key ring for User, default is /etc/ceph/user.secret - More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + description: |- + secretFile is Optional: SecretFile is the path to key ring for User, default is /etc/ceph/user.secret + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it type: string secretRef: - description: 'secretRef is Optional: SecretRef is reference - to the authentication secret for User, default is - empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + description: |- + secretRef is Optional: SecretRef is reference to the authentication secret for User, default is empty. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it properties: name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string type: object x-kubernetes-map-type: atomic user: - description: 'user is optional: User is the rados user - name, default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + description: |- + user is optional: User is the rados user name, default is admin + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it type: string required: - monitors type: object cinder: - description: 'cinder represents a cinder volume attached - and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + description: |- + cinder represents a cinder volume attached and mounted on kubelets host machine. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md properties: fsType: - description: 'fsType is the filesystem type to mount. - Must be a filesystem type supported by the host operating - system. Examples: "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md type: string readOnly: - description: 'readOnly defaults to false (read/write). - ReadOnly here will force the ReadOnly setting in VolumeMounts. - More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + description: |- + readOnly defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md type: boolean secretRef: - description: 'secretRef is optional: points to a secret - object containing parameters used to connect to OpenStack.' + description: |- + secretRef is optional: points to a secret object containing parameters used to connect + to OpenStack. properties: name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string type: object x-kubernetes-map-type: atomic volumeID: - description: 'volumeID used to identify the volume in - cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + description: |- + volumeID used to identify the volume in cinder. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md type: string required: - volumeID @@ -3067,29 +3387,25 @@ spec: populate this volume properties: defaultMode: - description: 'defaultMode is optional: mode bits used - to set permissions on created files by default. Must - be an octal value between 0000 and 0777 or a decimal - value between 0 and 511. YAML accepts both octal and - decimal values, JSON requires decimal values for mode - bits. Defaults to 0644. Directories within the path - are not affected by this setting. This might be in - conflict with other options that affect the file mode, - like fsGroup, and the result can be other mode bits - set.' + description: |- + defaultMode is optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. format: int32 type: integer items: - description: items if unspecified, each key-value pair - in the Data field of the referenced ConfigMap will - be projected into the volume as a file whose name - is the key and content is the value. If specified, - the listed keys will be projected into the specified - paths, and unlisted keys will not be present. If a - key is specified which is not present in the ConfigMap, - the volume setup will error unless it is marked optional. - Paths must be relative and may not contain the '..' - path or start with '..'. + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + ConfigMap will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. items: description: Maps a string key to a path within a volume. @@ -3098,22 +3414,20 @@ spec: description: key is the key to project. type: string mode: - description: 'mode is Optional: mode bits used - to set permissions on this file. Must be an - octal value between 0000 and 0777 or a decimal - value between 0 and 511. YAML accepts both octal - and decimal values, JSON requires decimal values - for mode bits. If not specified, the volume - defaultMode will be used. This might be in conflict - with other options that affect the file mode, - like fsGroup, and the result can be other mode - bits set.' + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. format: int32 type: integer path: - description: path is the relative path of the - file to map the key to. May not be an absolute - path. May not contain the path element '..'. + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. May not start with the string '..'. type: string required: @@ -3121,9 +3435,15 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string optional: description: optional specify whether the ConfigMap @@ -3137,42 +3457,46 @@ spec: CSI drivers (Beta feature). properties: driver: - description: driver is the name of the CSI driver that - handles this volume. Consult with your admin for the - correct name as registered in the cluster. + description: |- + driver is the name of the CSI driver that handles this volume. + Consult with your admin for the correct name as registered in the cluster. type: string fsType: - description: fsType to mount. Ex. "ext4", "xfs", "ntfs". - If not provided, the empty value is passed to the - associated CSI driver which will determine the default - filesystem to apply. + description: |- + fsType to mount. Ex. "ext4", "xfs", "ntfs". + If not provided, the empty value is passed to the associated CSI driver + which will determine the default filesystem to apply. type: string nodePublishSecretRef: - description: nodePublishSecretRef is a reference to - the secret object containing sensitive information - to pass to the CSI driver to complete the CSI NodePublishVolume - and NodeUnpublishVolume calls. This field is optional, - and may be empty if no secret is required. If the - secret object contains more than one secret, all secret - references are passed. + description: |- + nodePublishSecretRef is a reference to the secret object containing + sensitive information to pass to the CSI driver to complete the CSI + NodePublishVolume and NodeUnpublishVolume calls. + This field is optional, and may be empty if no secret is required. If the + secret object contains more than one secret, all secret references are passed. properties: name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string type: object x-kubernetes-map-type: atomic readOnly: - description: readOnly specifies a read-only configuration - for the volume. Defaults to false (read/write). + description: |- + readOnly specifies a read-only configuration for the volume. + Defaults to false (read/write). type: boolean volumeAttributes: additionalProperties: type: string - description: volumeAttributes stores driver-specific - properties that are passed to the CSI driver. Consult - your driver's documentation for supported values. + description: |- + volumeAttributes stores driver-specific properties that are passed to the CSI + driver. Consult your driver's documentation for supported values. type: object required: - driver @@ -3182,17 +3506,15 @@ spec: pod that should populate this volume properties: defaultMode: - description: 'Optional: mode bits to use on created - files by default. Must be a Optional: mode bits used - to set permissions on created files by default. Must - be an octal value between 0000 and 0777 or a decimal - value between 0 and 511. YAML accepts both octal and - decimal values, JSON requires decimal values for mode - bits. Defaults to 0644. Directories within the path - are not affected by this setting. This might be in - conflict with other options that affect the file mode, - like fsGroup, and the result can be other mode bits - set.' + description: |- + Optional: mode bits to use on created files by default. Must be a + Optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. format: int32 type: integer items: @@ -3204,8 +3526,8 @@ spec: properties: fieldRef: description: 'Required: Selects a field of the - pod: only annotations, labels, name and namespace - are supported.' + pod: only annotations, labels, name, namespace + and uid are supported.' properties: apiVersion: description: Version of the schema the FieldPath @@ -3220,16 +3542,13 @@ spec: type: object x-kubernetes-map-type: atomic mode: - description: 'Optional: mode bits used to set - permissions on this file, must be an octal value - between 0000 and 0777 or a decimal value between - 0 and 511. YAML accepts both octal and decimal - values, JSON requires decimal values for mode - bits. If not specified, the volume defaultMode - will be used. This might be in conflict with - other options that affect the file mode, like - fsGroup, and the result can be other mode bits - set.' + description: |- + Optional: mode bits used to set permissions on this file, must be an octal value + between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. format: int32 type: integer path: @@ -3240,10 +3559,9 @@ spec: path must not start with ''..''' type: string resourceFieldRef: - description: 'Selects a resource of the container: - only resources limits and requests (limits.cpu, - limits.memory, requests.cpu and requests.memory) - are currently supported.' + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. properties: containerName: description: 'Container name: required for @@ -3268,115 +3586,122 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic type: object emptyDir: - description: 'emptyDir represents a temporary directory - that shares a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + description: |- + emptyDir represents a temporary directory that shares a pod's lifetime. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir properties: medium: - description: 'medium represents what type of storage - medium should back this directory. The default is - "" which means to use the node''s default medium. - Must be an empty string (default) or Memory. More - info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + description: |- + medium represents what type of storage medium should back this directory. + The default is "" which means to use the node's default medium. + Must be an empty string (default) or Memory. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir type: string sizeLimit: anyOf: - type: integer - type: string - description: 'sizeLimit is the total amount of local - storage required for this EmptyDir volume. The size - limit is also applicable for memory medium. The maximum - usage on memory medium EmptyDir would be the minimum - value between the SizeLimit specified here and the - sum of memory limits of all containers in a pod. The - default is nil which means that the limit is undefined. - More info: http://kubernetes.io/docs/user-guide/volumes#emptydir' + description: |- + sizeLimit is the total amount of local storage required for this EmptyDir volume. + The size limit is also applicable for memory medium. + The maximum usage on memory medium EmptyDir would be the minimum value between + the SizeLimit specified here and the sum of memory limits of all containers in a pod. + The default is nil which means that the limit is undefined. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true type: object ephemeral: - description: "ephemeral represents a volume that is handled - by a cluster storage driver. The volume's lifecycle is - tied to the pod that defines it - it will be created before - the pod starts, and deleted when the pod is removed. \n - Use this if: a) the volume is only needed while the pod - runs, b) features of normal volumes like restoring from - snapshot or capacity tracking are needed, c) the storage - driver is specified through a storage class, and d) the - storage driver supports dynamic volume provisioning through - a PersistentVolumeClaim (see EphemeralVolumeSource for - more information on the connection between this volume - type and PersistentVolumeClaim). \n Use PersistentVolumeClaim - or one of the vendor-specific APIs for volumes that persist - for longer than the lifecycle of an individual pod. \n - Use CSI for light-weight local ephemeral volumes if the - CSI driver is meant to be used that way - see the documentation - of the driver for more information. \n A pod can use both - types of ephemeral volumes and persistent volumes at the - same time." + description: |- + ephemeral represents a volume that is handled by a cluster storage driver. + The volume's lifecycle is tied to the pod that defines it - it will be created before the pod starts, + and deleted when the pod is removed. + + Use this if: + a) the volume is only needed while the pod runs, + b) features of normal volumes like restoring from snapshot or capacity + tracking are needed, + c) the storage driver is specified through a storage class, and + d) the storage driver supports dynamic volume provisioning through + a PersistentVolumeClaim (see EphemeralVolumeSource for more + information on the connection between this volume type + and PersistentVolumeClaim). + + Use PersistentVolumeClaim or one of the vendor-specific + APIs for volumes that persist for longer than the lifecycle + of an individual pod. + + Use CSI for light-weight local ephemeral volumes if the CSI driver is meant to + be used that way - see the documentation of the driver for + more information. + + A pod can use both types of ephemeral volumes and + persistent volumes at the same time. properties: volumeClaimTemplate: - description: "Will be used to create a stand-alone PVC - to provision the volume. The pod in which this EphemeralVolumeSource - is embedded will be the owner of the PVC, i.e. the - PVC will be deleted together with the pod. The name - of the PVC will be `-` where - `` is the name from the `PodSpec.Volumes` - array entry. Pod validation will reject the pod if - the concatenated name is not valid for a PVC (for - example, too long). \n An existing PVC with that name - that is not owned by the pod will *not* be used for - the pod to avoid using an unrelated volume by mistake. - Starting the pod is then blocked until the unrelated - PVC is removed. If such a pre-created PVC is meant - to be used by the pod, the PVC has to updated with - an owner reference to the pod once the pod exists. - Normally this should not be necessary, but it may - be useful when manually reconstructing a broken cluster. - \n This field is read-only and no changes will be - made by Kubernetes to the PVC after it has been created. - \n Required, must not be nil." + description: |- + Will be used to create a stand-alone PVC to provision the volume. + The pod in which this EphemeralVolumeSource is embedded will be the + owner of the PVC, i.e. the PVC will be deleted together with the + pod. The name of the PVC will be `-` where + `` is the name from the `PodSpec.Volumes` array + entry. Pod validation will reject the pod if the concatenated name + is not valid for a PVC (for example, too long). + + An existing PVC with that name that is not owned by the pod + will *not* be used for the pod to avoid using an unrelated + volume by mistake. Starting the pod is then blocked until + the unrelated PVC is removed. If such a pre-created PVC is + meant to be used by the pod, the PVC has to updated with an + owner reference to the pod once the pod exists. Normally + this should not be necessary, but it may be useful when + manually reconstructing a broken cluster. + + This field is read-only and no changes will be made by Kubernetes + to the PVC after it has been created. + + Required, must not be nil. properties: metadata: - description: May contain labels and annotations - that will be copied into the PVC when creating - it. No other fields are allowed and will be rejected - during validation. + description: |- + May contain labels and annotations that will be copied into the PVC + when creating it. No other fields are allowed and will be rejected during + validation. type: object spec: - description: The specification for the PersistentVolumeClaim. - The entire content is copied unchanged into the - PVC that gets created from this template. The - same fields as in a PersistentVolumeClaim are - also valid here. + description: |- + The specification for the PersistentVolumeClaim. The entire content is + copied unchanged into the PVC that gets created from this + template. The same fields as in a PersistentVolumeClaim + are also valid here. properties: accessModes: - description: 'accessModes contains the desired - access modes the volume should have. More - info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' + description: |- + accessModes contains the desired access modes the volume should have. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1 items: type: string type: array + x-kubernetes-list-type: atomic dataSource: - description: 'dataSource field can be used to - specify either: * An existing VolumeSnapshot - object (snapshot.storage.k8s.io/VolumeSnapshot) + description: |- + dataSource field can be used to specify either: + * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) * An existing PVC (PersistentVolumeClaim) - If the provisioner or an external controller - can support the specified data source, it - will create a new volume based on the contents - of the specified data source. If the AnyVolumeDataSource - feature gate is enabled, this field will always - have the same contents as the DataSourceRef - field.' + If the provisioner or an external controller can support the specified data source, + it will create a new volume based on the contents of the specified data source. + When the AnyVolumeDataSource feature gate is enabled, dataSource contents will be copied to dataSourceRef, + and dataSourceRef contents will be copied to dataSource when dataSourceRef.namespace is not specified. + If the namespace is specified, then dataSourceRef will not be copied to dataSource. properties: apiGroup: - description: APIGroup is the group for the - resource being referenced. If APIGroup - is not specified, the specified Kind must - be in the core API group. For any other - third-party types, APIGroup is required. + description: |- + APIGroup is the group for the resource being referenced. + If APIGroup is not specified, the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. type: string kind: description: Kind is the type of resource @@ -3392,39 +3717,36 @@ spec: type: object x-kubernetes-map-type: atomic dataSourceRef: - description: 'dataSourceRef specifies the object - from which to populate the volume with data, - if a non-empty volume is desired. This may - be any local object from a non-empty API group - (non core object) or a PersistentVolumeClaim - object. When this field is specified, volume - binding will only succeed if the type of the - specified object matches some installed volume - populator or dynamic provisioner. This field - will replace the functionality of the DataSource - field and as such if both fields are non-empty, - they must have the same value. For backwards - compatibility, both fields (DataSource and - DataSourceRef) will be set to the same value - automatically if one of them is empty and - the other is non-empty. There are two important - differences between DataSource and DataSourceRef: - * While DataSource only allows two specific - types of objects, DataSourceRef allows any - non-core object, as well as PersistentVolumeClaim - objects. * While DataSource ignores disallowed - values (dropping them), DataSourceRef preserves - all values, and generates an error if a disallowed - value is specified. (Beta) Using this field - requires the AnyVolumeDataSource feature gate - to be enabled.' + description: |- + dataSourceRef specifies the object from which to populate the volume with data, if a non-empty + volume is desired. This may be any object from a non-empty API group (non + core object) or a PersistentVolumeClaim object. + When this field is specified, volume binding will only succeed if the type of + the specified object matches some installed volume populator or dynamic + provisioner. + This field will replace the functionality of the dataSource field and as such + if both fields are non-empty, they must have the same value. For backwards + compatibility, when namespace isn't specified in dataSourceRef, + both fields (dataSource and dataSourceRef) will be set to the same + value automatically if one of them is empty and the other is non-empty. + When namespace is specified in dataSourceRef, + dataSource isn't set to the same value and must be empty. + There are three important differences between dataSource and dataSourceRef: + * While dataSource only allows two specific types of objects, dataSourceRef + allows any non-core object, as well as PersistentVolumeClaim objects. + * While dataSource ignores disallowed values (dropping them), dataSourceRef + preserves all values, and generates an error if a disallowed value is + specified. + * While dataSource only allows local objects, dataSourceRef allows objects + in any namespaces. + (Beta) Using this field requires the AnyVolumeDataSource feature gate to be enabled. + (Alpha) Using the namespace field of dataSourceRef requires the CrossNamespaceVolumeDataSource feature gate to be enabled. properties: apiGroup: - description: APIGroup is the group for the - resource being referenced. If APIGroup - is not specified, the specified Kind must - be in the core API group. For any other - third-party types, APIGroup is required. + description: |- + APIGroup is the group for the resource being referenced. + If APIGroup is not specified, the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. type: string kind: description: Kind is the type of resource @@ -3434,19 +3756,23 @@ spec: description: Name is the name of resource being referenced type: string + namespace: + description: |- + Namespace is the namespace of resource being referenced + Note that when a namespace is specified, a gateway.networking.k8s.io/ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. + (Alpha) This field requires the CrossNamespaceVolumeDataSource feature gate to be enabled. + type: string required: - kind - name type: object - x-kubernetes-map-type: atomic resources: - description: 'resources represents the minimum - resources the volume should have. If RecoverVolumeExpansionFailure - feature is enabled users are allowed to specify - resource requirements that are lower than - previous value but must still be higher than - capacity recorded in the status field of the - claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' + description: |- + resources represents the minimum resources the volume should have. + If RecoverVolumeExpansionFailure feature is enabled users are allowed to specify resource requirements + that are lower than previous value but must still be higher than capacity recorded in the + status field of the claim. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources properties: limits: additionalProperties: @@ -3455,9 +3781,9 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Limits describes the maximum - amount of compute resources allowed. More - info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object requests: additionalProperties: @@ -3466,12 +3792,11 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Requests describes the minimum - amount of compute resources required. - If Requests is omitted for a container, - it defaults to Limits if that is explicitly - specified, otherwise to an implementation-defined - value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object type: object selector: @@ -3483,60 +3808,69 @@ spec: of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic storageClassName: - description: 'storageClassName is the name of - the StorageClass required by the claim. More - info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' + description: |- + storageClassName is the name of the StorageClass required by the claim. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1 + type: string + volumeAttributesClassName: + description: |- + volumeAttributesClassName may be used to set the VolumeAttributesClass used by this claim. + If specified, the CSI driver will create or update the volume with the attributes defined + in the corresponding VolumeAttributesClass. This has a different purpose than storageClassName, + it can be changed after the claim is created. An empty string value means that no VolumeAttributesClass + will be applied to the claim but it's not allowed to reset this field to empty string once it is set. + If unspecified and the PersistentVolumeClaim is unbound, the default VolumeAttributesClass + will be set by the persistentvolume controller if it exists. + If the resource referred to by volumeAttributesClass does not exist, this PersistentVolumeClaim will be + set to a Pending state, as reflected by the modifyVolumeStatus field, until such as a resource + exists. + More info: https://kubernetes.io/docs/concepts/storage/volume-attributes-classes/ + (Beta) Using this field requires the VolumeAttributesClass feature gate to be enabled (off by default). type: string volumeMode: - description: volumeMode defines what type of - volume is required by the claim. Value of - Filesystem is implied when not included in - claim spec. + description: |- + volumeMode defines what type of volume is required by the claim. + Value of Filesystem is implied when not included in claim spec. type: string volumeName: description: volumeName is the binding reference @@ -3553,20 +3887,19 @@ spec: to the pod. properties: fsType: - description: 'fsType is the filesystem type to mount. - Must be a filesystem type supported by the host operating - system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred - to be "ext4" if unspecified. TODO: how do we prevent - errors in the filesystem from compromising the machine' + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. type: string lun: description: 'lun is Optional: FC target lun number' format: int32 type: integer readOnly: - description: 'readOnly is Optional: Defaults to false - (read/write). ReadOnly here will force the ReadOnly - setting in VolumeMounts.' + description: |- + readOnly is Optional: Defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. type: boolean targetWWNs: description: 'targetWWNs is Optional: FC target worldwide @@ -3574,27 +3907,30 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic wwids: - description: 'wwids Optional: FC volume world wide identifiers - (wwids) Either wwids or combination of targetWWNs - and lun must be set, but not both simultaneously.' + description: |- + wwids Optional: FC volume world wide identifiers (wwids) + Either wwids or combination of targetWWNs and lun must be set, but not both simultaneously. items: type: string type: array + x-kubernetes-list-type: atomic type: object flexVolume: - description: flexVolume represents a generic volume resource - that is provisioned/attached using an exec based plugin. + description: |- + flexVolume represents a generic volume resource that is + provisioned/attached using an exec based plugin. properties: driver: description: driver is the name of the driver to use for this volume. type: string fsType: - description: fsType is the filesystem type to mount. - Must be a filesystem type supported by the host operating - system. Ex. "ext4", "xfs", "ntfs". The default filesystem - depends on FlexVolume script. + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". The default filesystem depends on FlexVolume script. type: string options: additionalProperties: @@ -3603,22 +3939,26 @@ spec: extra command options if any.' type: object readOnly: - description: 'readOnly is Optional: defaults to false - (read/write). ReadOnly here will force the ReadOnly - setting in VolumeMounts.' + description: |- + readOnly is Optional: defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. type: boolean secretRef: - description: 'secretRef is Optional: secretRef is reference - to the secret object containing sensitive information - to pass to the plugin scripts. This may be empty if - no secret object is specified. If the secret object - contains more than one secret, all secrets are passed - to the plugin scripts.' + description: |- + secretRef is Optional: secretRef is reference to the secret object containing + sensitive information to pass to the plugin scripts. This may be + empty if no secret object is specified. If the secret object + contains more than one secret, all secrets are passed to the plugin + scripts. properties: name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string type: object x-kubernetes-map-type: atomic @@ -3631,9 +3971,9 @@ spec: control service being running properties: datasetName: - description: datasetName is Name of the dataset stored - as metadata -> name on the dataset for Flocker should - be considered as deprecated + description: |- + datasetName is Name of the dataset stored as metadata -> name on the dataset for Flocker + should be considered as deprecated type: string datasetUUID: description: datasetUUID is the UUID of the dataset. @@ -3641,54 +3981,54 @@ spec: type: string type: object gcePersistentDisk: - description: 'gcePersistentDisk represents a GCE Disk resource - that is attached to a kubelet''s host machine and then - exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + description: |- + gcePersistentDisk represents a GCE Disk resource that is attached to a + kubelet's host machine and then exposed to the pod. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk properties: fsType: - description: 'fsType is filesystem type of the volume - that you want to mount. Tip: Ensure that the filesystem - type is supported by the host operating system. Examples: - "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" - if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk - TODO: how do we prevent errors in the filesystem from - compromising the machine' + description: |- + fsType is filesystem type of the volume that you want to mount. + Tip: Ensure that the filesystem type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk type: string partition: - description: 'partition is the partition in the volume - that you want to mount. If omitted, the default is - to mount by volume name. Examples: For volume /dev/sda1, - you specify the partition as "1". Similarly, the volume - partition for /dev/sda is "0" (or you can leave the - property empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + description: |- + partition is the partition in the volume that you want to mount. + If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition as "1". + Similarly, the volume partition for /dev/sda is "0" (or you can leave the property empty). + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk format: int32 type: integer pdName: - description: 'pdName is unique name of the PD resource - in GCE. Used to identify the disk in GCE. More info: - https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + description: |- + pdName is unique name of the PD resource in GCE. Used to identify the disk in GCE. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk type: string readOnly: - description: 'readOnly here will force the ReadOnly - setting in VolumeMounts. Defaults to false. More info: - https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + description: |- + readOnly here will force the ReadOnly setting in VolumeMounts. + Defaults to false. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk type: boolean required: - pdName type: object gitRepo: - description: 'gitRepo represents a git repository at a particular - revision. DEPRECATED: GitRepo is deprecated. To provision - a container with a git repo, mount an EmptyDir into an - InitContainer that clones the repo using git, then mount - the EmptyDir into the Pod''s container.' + description: |- + gitRepo represents a git repository at a particular revision. + DEPRECATED: GitRepo is deprecated. To provision a container with a git repo, mount an + EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir + into the Pod's container. properties: directory: - description: directory is the target directory name. - Must not contain or start with '..'. If '.' is supplied, - the volume directory will be the git repository. Otherwise, - if specified, the volume will contain the git repository - in the subdirectory with the given name. + description: |- + directory is the target directory name. + Must not contain or start with '..'. If '.' is supplied, the volume directory will be the + git repository. Otherwise, if specified, the volume will contain the git repository in + the subdirectory with the given name. type: string repository: description: repository is the URL @@ -3701,53 +4041,93 @@ spec: - repository type: object glusterfs: - description: 'glusterfs represents a Glusterfs mount on - the host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md' + description: |- + glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. + More info: https://examples.k8s.io/volumes/glusterfs/README.md properties: endpoints: - description: 'endpoints is the endpoint name that details - Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + description: |- + endpoints is the endpoint name that details Glusterfs topology. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod type: string path: - description: 'path is the Glusterfs volume path. More - info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + description: |- + path is the Glusterfs volume path. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod type: string readOnly: - description: 'readOnly here will force the Glusterfs - volume to be mounted with read-only permissions. Defaults - to false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + description: |- + readOnly here will force the Glusterfs volume to be mounted with read-only permissions. + Defaults to false. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod type: boolean required: - endpoints - path type: object hostPath: - description: 'hostPath represents a pre-existing file or - directory on the host machine that is directly exposed - to the container. This is generally used for system agents - or other privileged things that are allowed to see the - host machine. Most containers will NOT need this. More - info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath - --- TODO(jonesdl) We need to restrict who can use host - directory mounts and who can/can not mount host directories - as read/write.' + description: |- + hostPath represents a pre-existing file or directory on the host + machine that is directly exposed to the container. This is generally + used for system agents or other privileged things that are allowed + to see the host machine. Most containers will NOT need this. + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath properties: path: - description: 'path of the directory on the host. If - the path is a symlink, it will follow the link to - the real path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + description: |- + path of the directory on the host. + If the path is a symlink, it will follow the link to the real path. + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath type: string type: - description: 'type for HostPath Volume Defaults to "" - More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + description: |- + type for HostPath Volume + Defaults to "" + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath type: string required: - path type: object + image: + description: |- + image represents an OCI object (a container image or artifact) pulled and mounted on the kubelet's host machine. + The volume is resolved at pod startup depending on which PullPolicy value is provided: + + - Always: the kubelet always attempts to pull the reference. Container creation will fail If the pull fails. + - Never: the kubelet never pulls the reference and only uses a local image or artifact. Container creation will fail if the reference isn't present. + - IfNotPresent: the kubelet pulls if the reference isn't already present on disk. Container creation will fail if the reference isn't present and the pull fails. + + The volume gets re-resolved if the pod gets deleted and recreated, which means that new remote content will become available on pod recreation. + A failure to resolve or pull the image during pod startup will block containers from starting and may add significant latency. Failures will be retried using normal volume backoff and will be reported on the pod reason and message. + The types of objects that may be mounted by this volume are defined by the container runtime implementation on a host machine and at minimum must include all valid types supported by the container image field. + The OCI object gets mounted in a single directory (spec.containers[*].volumeMounts.mountPath) by merging the manifest layers in the same way as for container images. + The volume will be mounted read-only (ro) and non-executable files (noexec). + Sub path mounts for containers are not supported (spec.containers[*].volumeMounts.subpath). + The field spec.securityContext.fsGroupChangePolicy has no effect on this volume type. + properties: + pullPolicy: + description: |- + Policy for pulling OCI objects. Possible values are: + Always: the kubelet always attempts to pull the reference. Container creation will fail If the pull fails. + Never: the kubelet never pulls the reference and only uses a local image or artifact. Container creation will fail if the reference isn't present. + IfNotPresent: the kubelet pulls if the reference isn't already present on disk. Container creation will fail if the reference isn't present and the pull fails. + Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. + type: string + reference: + description: |- + Required: Image or artifact reference to be used. + Behaves in the same way as pod.spec.containers[*].image. + Pull secrets will be assembled in the same way as for the container image by looking up node credentials, SA image pull secrets, and pod spec image pull secrets. + More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config management to default or override + container images in workload controllers like Deployments and StatefulSets. + type: string + type: object iscsi: - description: 'iscsi represents an ISCSI Disk resource that - is attached to a kubelet''s host machine and then exposed - to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + description: |- + iscsi represents an ISCSI Disk resource that is attached to a + kubelet's host machine and then exposed to the pod. + More info: https://examples.k8s.io/volumes/iscsi/README.md properties: chapAuthDiscovery: description: chapAuthDiscovery defines whether support @@ -3758,59 +4138,63 @@ spec: iSCSI Session CHAP authentication type: boolean fsType: - description: 'fsType is the filesystem type of the volume - that you want to mount. Tip: Ensure that the filesystem - type is supported by the host operating system. Examples: - "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" - if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi - TODO: how do we prevent errors in the filesystem from - compromising the machine' + description: |- + fsType is the filesystem type of the volume that you want to mount. + Tip: Ensure that the filesystem type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi type: string initiatorName: - description: initiatorName is the custom iSCSI Initiator - Name. If initiatorName is specified with iscsiInterface - simultaneously, new iSCSI interface : will be created for the connection. + description: |- + initiatorName is the custom iSCSI Initiator Name. + If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface + : will be created for the connection. type: string iqn: description: iqn is the target iSCSI Qualified Name. type: string iscsiInterface: - description: iscsiInterface is the interface Name that - uses an iSCSI transport. Defaults to 'default' (tcp). + default: default + description: |- + iscsiInterface is the interface Name that uses an iSCSI transport. + Defaults to 'default' (tcp). type: string lun: description: lun represents iSCSI Target Lun number. format: int32 type: integer portals: - description: portals is the iSCSI Target Portal List. - The portal is either an IP or ip_addr:port if the - port is other than default (typically TCP ports 860 - and 3260). + description: |- + portals is the iSCSI Target Portal List. The portal is either an IP or ip_addr:port if the port + is other than default (typically TCP ports 860 and 3260). items: type: string type: array + x-kubernetes-list-type: atomic readOnly: - description: readOnly here will force the ReadOnly setting - in VolumeMounts. Defaults to false. + description: |- + readOnly here will force the ReadOnly setting in VolumeMounts. + Defaults to false. type: boolean secretRef: description: secretRef is the CHAP Secret for iSCSI target and initiator authentication properties: name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string type: object x-kubernetes-map-type: atomic targetPortal: - description: targetPortal is iSCSI Target Portal. The - Portal is either an IP or ip_addr:port if the port - is other than default (typically TCP ports 860 and - 3260). + description: |- + targetPortal is iSCSI Target Portal. The Portal is either an IP or ip_addr:port if the port + is other than default (typically TCP ports 860 and 3260). type: string required: - iqn @@ -3818,43 +4202,51 @@ spec: - targetPortal type: object name: - description: 'name of the volume. Must be a DNS_LABEL and - unique within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + description: |- + name of the volume. + Must be a DNS_LABEL and unique within the pod. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string nfs: - description: 'nfs represents an NFS mount on the host that - shares a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + description: |- + nfs represents an NFS mount on the host that shares a pod's lifetime + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs properties: path: - description: 'path that is exported by the NFS server. - More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + description: |- + path that is exported by the NFS server. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs type: string readOnly: - description: 'readOnly here will force the NFS export - to be mounted with read-only permissions. Defaults - to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + description: |- + readOnly here will force the NFS export to be mounted with read-only permissions. + Defaults to false. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs type: boolean server: - description: 'server is the hostname or IP address of - the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + description: |- + server is the hostname or IP address of the NFS server. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs type: string required: - path - server type: object persistentVolumeClaim: - description: 'persistentVolumeClaimVolumeSource represents - a reference to a PersistentVolumeClaim in the same namespace. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + description: |- + persistentVolumeClaimVolumeSource represents a reference to a + PersistentVolumeClaim in the same namespace. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims properties: claimName: - description: 'claimName is the name of a PersistentVolumeClaim - in the same namespace as the pod using this volume. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + description: |- + claimName is the name of a PersistentVolumeClaim in the same namespace as the pod using this volume. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims type: string readOnly: - description: readOnly Will force the ReadOnly setting - in VolumeMounts. Default false. + description: |- + readOnly Will force the ReadOnly setting in VolumeMounts. + Default false. type: boolean required: - claimName @@ -3865,10 +4257,10 @@ spec: machine properties: fsType: - description: fsType is the filesystem type to mount. - Must be a filesystem type supported by the host operating - system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred - to be "ext4" if unspecified. + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. type: string pdID: description: pdID is the ID that identifies Photon Controller @@ -3882,14 +4274,15 @@ spec: attached and mounted on kubelets host machine properties: fsType: - description: fSType represents the filesystem type to - mount Must be a filesystem type supported by the host - operating system. Ex. "ext4", "xfs". Implicitly inferred - to be "ext4" if unspecified. + description: |- + fSType represents the filesystem type to mount + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs". Implicitly inferred to be "ext4" if unspecified. type: string readOnly: - description: readOnly defaults to false (read/write). - ReadOnly here will force the ReadOnly setting in VolumeMounts. + description: |- + readOnly defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. type: boolean volumeID: description: volumeID uniquely identifies a Portworx @@ -3903,40 +4296,130 @@ spec: configmaps, and downward API properties: defaultMode: - description: defaultMode are the mode bits used to set - permissions on created files by default. Must be an - octal value between 0000 and 0777 or a decimal value - between 0 and 511. YAML accepts both octal and decimal - values, JSON requires decimal values for mode bits. - Directories within the path are not affected by this - setting. This might be in conflict with other options - that affect the file mode, like fsGroup, and the result - can be other mode bits set. + description: |- + defaultMode are the mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. format: int32 type: integer sources: - description: sources is the list of volume projections + description: |- + sources is the list of volume projections. Each entry in this list + handles one source. items: - description: Projection that may be projected along - with other supported volume types + description: |- + Projection that may be projected along with other supported volume types. + Exactly one of these fields must be set. properties: + clusterTrustBundle: + description: |- + ClusterTrustBundle allows a pod to access the `.spec.trustBundle` field + of ClusterTrustBundle objects in an auto-updating file. + + Alpha, gated by the ClusterTrustBundleProjection feature gate. + + ClusterTrustBundle objects can either be selected by name, or by the + combination of signer name and a label selector. + + Kubelet performs aggressive normalization of the PEM contents written + into the pod filesystem. Esoteric PEM features such as inter-block + comments and block headers are stripped. Certificates are deduplicated. + The ordering of certificates within the file is arbitrary, and Kubelet + may change the order over time. + properties: + labelSelector: + description: |- + Select all ClusterTrustBundles that match this label selector. Only has + effect if signerName is set. Mutually-exclusive with name. If unset, + interpreted as "match nothing". If set but empty, interpreted as "match + everything". + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The + requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + name: + description: |- + Select a single ClusterTrustBundle by object name. Mutually-exclusive + with signerName and labelSelector. + type: string + optional: + description: |- + If true, don't block pod startup if the referenced ClusterTrustBundle(s) + aren't available. If using name, then the named ClusterTrustBundle is + allowed not to exist. If using signerName, then the combination of + signerName and labelSelector is allowed to match zero + ClusterTrustBundles. + type: boolean + path: + description: Relative path from the volume + root to write the bundle. + type: string + signerName: + description: |- + Select all ClusterTrustBundles that match this signer name. + Mutually-exclusive with name. The contents of all selected + ClusterTrustBundles will be unified and deduplicated. + type: string + required: + - path + type: object configMap: description: configMap information about the configMap data to project properties: items: - description: items if unspecified, each key-value - pair in the Data field of the referenced - ConfigMap will be projected into the volume - as a file whose name is the key and content - is the value. If specified, the listed keys - will be projected into the specified paths, - and unlisted keys will not be present. If - a key is specified which is not present - in the ConfigMap, the volume setup will - error unless it is marked optional. Paths - must be relative and may not contain the - '..' path or start with '..'. + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + ConfigMap will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. items: description: Maps a string key to a path within a volume. @@ -3945,37 +4428,36 @@ spec: description: key is the key to project. type: string mode: - description: 'mode is Optional: mode - bits used to set permissions on this - file. Must be an octal value between - 0000 and 0777 or a decimal value between - 0 and 511. YAML accepts both octal - and decimal values, JSON requires - decimal values for mode bits. If not - specified, the volume defaultMode - will be used. This might be in conflict - with other options that affect the - file mode, like fsGroup, and the result - can be other mode bits set.' + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. format: int32 type: integer path: - description: path is the relative path - of the file to map the key to. May - not be an absolute path. May not contain - the path element '..'. May not start - with the string '..'. + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. type: string required: - key - path type: object type: array + x-kubernetes-list-type: atomic name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string optional: description: optional specify whether the @@ -3998,7 +4480,7 @@ spec: fieldRef: description: 'Required: Selects a field of the pod: only annotations, labels, - name and namespace are supported.' + name, namespace and uid are supported.' properties: apiVersion: description: Version of the schema @@ -4014,18 +4496,13 @@ spec: type: object x-kubernetes-map-type: atomic mode: - description: 'Optional: mode bits used - to set permissions on this file, must - be an octal value between 0000 and - 0777 or a decimal value between 0 - and 511. YAML accepts both octal and - decimal values, JSON requires decimal - values for mode bits. If not specified, - the volume defaultMode will be used. - This might be in conflict with other - options that affect the file mode, - like fsGroup, and the result can be - other mode bits set.' + description: |- + Optional: mode bits used to set permissions on this file, must be an octal value + between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. format: int32 type: integer path: @@ -4037,11 +4514,9 @@ spec: path must not start with ''..''' type: string resourceFieldRef: - description: 'Selects a resource of - the container: only resources limits - and requests (limits.cpu, limits.memory, - requests.cpu and requests.memory) - are currently supported.' + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. properties: containerName: description: 'Container name: required @@ -4069,24 +4544,21 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic type: object secret: description: secret information about the secret data to project properties: items: - description: items if unspecified, each key-value - pair in the Data field of the referenced - Secret will be projected into the volume - as a file whose name is the key and content - is the value. If specified, the listed keys - will be projected into the specified paths, - and unlisted keys will not be present. If - a key is specified which is not present - in the Secret, the volume setup will error - unless it is marked optional. Paths must - be relative and may not contain the '..' - path or start with '..'. + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + Secret will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. items: description: Maps a string key to a path within a volume. @@ -4095,37 +4567,36 @@ spec: description: key is the key to project. type: string mode: - description: 'mode is Optional: mode - bits used to set permissions on this - file. Must be an octal value between - 0000 and 0777 or a decimal value between - 0 and 511. YAML accepts both octal - and decimal values, JSON requires - decimal values for mode bits. If not - specified, the volume defaultMode - will be used. This might be in conflict - with other options that affect the - file mode, like fsGroup, and the result - can be other mode bits set.' + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. format: int32 type: integer path: - description: path is the relative path - of the file to map the key to. May - not be an absolute path. May not contain - the path element '..'. May not start - with the string '..'. + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. type: string required: - key - path type: object type: array + x-kubernetes-list-type: atomic name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string optional: description: optional field specify whether @@ -4138,29 +4609,25 @@ spec: about the serviceAccountToken data to project properties: audience: - description: audience is the intended audience - of the token. A recipient of a token must - identify itself with an identifier specified - in the audience of the token, and otherwise - should reject the token. The audience defaults - to the identifier of the apiserver. + description: |- + audience is the intended audience of the token. A recipient of a token + must identify itself with an identifier specified in the audience of the + token, and otherwise should reject the token. The audience defaults to the + identifier of the apiserver. type: string expirationSeconds: - description: expirationSeconds is the requested - duration of validity of the service account - token. As the token approaches expiration, - the kubelet volume plugin will proactively - rotate the service account token. The kubelet - will start trying to rotate the token if - the token is older than 80 percent of its - time to live or if the token is older than - 24 hours.Defaults to 1 hour and must be - at least 10 minutes. + description: |- + expirationSeconds is the requested duration of validity of the service + account token. As the token approaches expiration, the kubelet volume + plugin will proactively rotate the service account token. The kubelet will + start trying to rotate the token if the token is older than 80 percent of + its time to live or if the token is older than 24 hours.Defaults to 1 hour + and must be at least 10 minutes. format: int64 type: integer path: - description: path is the path relative to - the mount point of the file to project the + description: |- + path is the path relative to the mount point of the file to project the token into. type: string required: @@ -4168,34 +4635,37 @@ spec: type: object type: object type: array + x-kubernetes-list-type: atomic type: object quobyte: description: quobyte represents a Quobyte mount on the host that shares a pod's lifetime properties: group: - description: group to map volume access to Default is - no group + description: |- + group to map volume access to + Default is no group type: string readOnly: - description: readOnly here will force the Quobyte volume - to be mounted with read-only permissions. Defaults - to false. + description: |- + readOnly here will force the Quobyte volume to be mounted with read-only permissions. + Defaults to false. type: boolean registry: - description: registry represents a single or multiple - Quobyte Registry services specified as a string as - host:port pair (multiple entries are separated with - commas) which acts as the central registry for volumes + description: |- + registry represents a single or multiple Quobyte Registry services + specified as a string as host:port pair (multiple entries are separated with commas) + which acts as the central registry for volumes type: string tenant: - description: tenant owning the given Quobyte volume - in the Backend Used with dynamically provisioned Quobyte - volumes, value is set by the plugin + description: |- + tenant owning the given Quobyte volume in the Backend + Used with dynamically provisioned Quobyte volumes, value is set by the plugin type: string user: - description: user to map volume access to Defaults to - serivceaccount user + description: |- + user to map volume access to + Defaults to serivceaccount user type: string volume: description: volume is a string that references an already @@ -4206,57 +4676,74 @@ spec: - volume type: object rbd: - description: 'rbd represents a Rados Block Device mount - on the host that shares a pod''s lifetime. More info: - https://examples.k8s.io/volumes/rbd/README.md' + description: |- + rbd represents a Rados Block Device mount on the host that shares a pod's lifetime. + More info: https://examples.k8s.io/volumes/rbd/README.md properties: fsType: - description: 'fsType is the filesystem type of the volume - that you want to mount. Tip: Ensure that the filesystem - type is supported by the host operating system. Examples: - "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" - if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd - TODO: how do we prevent errors in the filesystem from - compromising the machine' + description: |- + fsType is the filesystem type of the volume that you want to mount. + Tip: Ensure that the filesystem type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd type: string image: - description: 'image is the rados image name. More info: - https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + description: |- + image is the rados image name. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it type: string keyring: - description: 'keyring is the path to key ring for RBDUser. - Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + default: /etc/ceph/keyring + description: |- + keyring is the path to key ring for RBDUser. + Default is /etc/ceph/keyring. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it type: string monitors: - description: 'monitors is a collection of Ceph monitors. - More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + description: |- + monitors is a collection of Ceph monitors. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it items: type: string type: array + x-kubernetes-list-type: atomic pool: - description: 'pool is the rados pool name. Default is - rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + default: rbd + description: |- + pool is the rados pool name. + Default is rbd. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it type: string readOnly: - description: 'readOnly here will force the ReadOnly - setting in VolumeMounts. Defaults to false. More info: - https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + description: |- + readOnly here will force the ReadOnly setting in VolumeMounts. + Defaults to false. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it type: boolean secretRef: - description: 'secretRef is name of the authentication - secret for RBDUser. If provided overrides keyring. - Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + description: |- + secretRef is name of the authentication secret for RBDUser. If provided + overrides keyring. + Default is nil. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it properties: name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string type: object x-kubernetes-map-type: atomic user: - description: 'user is the rados user name. Default is - admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + default: admin + description: |- + user is the rados user name. + Default is admin. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it type: string required: - image @@ -4267,9 +4754,12 @@ spec: attached and mounted on Kubernetes nodes. properties: fsType: - description: fsType is the filesystem type to mount. - Must be a filesystem type supported by the host operating - system. Ex. "ext4", "xfs", "ntfs". Default is "xfs". + default: xfs + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". + Default is "xfs". type: string gateway: description: gateway is the host address of the ScaleIO @@ -4280,18 +4770,23 @@ spec: Protection Domain for the configured storage. type: string readOnly: - description: readOnly Defaults to false (read/write). - ReadOnly here will force the ReadOnly setting in VolumeMounts. + description: |- + readOnly Defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. type: boolean secretRef: - description: secretRef references to the secret for - ScaleIO user and other sensitive information. If this - is not provided, Login operation will fail. + description: |- + secretRef references to the secret for ScaleIO user and other + sensitive information. If this is not provided, Login operation will fail. properties: name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string type: object x-kubernetes-map-type: atomic @@ -4300,8 +4795,9 @@ spec: with Gateway, default false type: boolean storageMode: - description: storageMode indicates whether the storage - for a volume should be ThickProvisioned or ThinProvisioned. + default: ThinProvisioned + description: |- + storageMode indicates whether the storage for a volume should be ThickProvisioned or ThinProvisioned. Default is ThinProvisioned. type: string storagePool: @@ -4313,9 +4809,9 @@ spec: as configured in ScaleIO. type: string volumeName: - description: volumeName is the name of a volume already - created in the ScaleIO system that is associated with - this volume source. + description: |- + volumeName is the name of a volume already created in the ScaleIO system + that is associated with this volume source. type: string required: - gateway @@ -4323,33 +4819,30 @@ spec: - system type: object secret: - description: 'secret represents a secret that should populate - this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + description: |- + secret represents a secret that should populate this volume. + More info: https://kubernetes.io/docs/concepts/storage/volumes#secret properties: defaultMode: - description: 'defaultMode is Optional: mode bits used - to set permissions on created files by default. Must - be an octal value between 0000 and 0777 or a decimal - value between 0 and 511. YAML accepts both octal and - decimal values, JSON requires decimal values for mode - bits. Defaults to 0644. Directories within the path - are not affected by this setting. This might be in - conflict with other options that affect the file mode, - like fsGroup, and the result can be other mode bits - set.' + description: |- + defaultMode is Optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values + for mode bits. Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. format: int32 type: integer items: - description: items If unspecified, each key-value pair - in the Data field of the referenced Secret will be - projected into the volume as a file whose name is - the key and content is the value. If specified, the - listed keys will be projected into the specified paths, - and unlisted keys will not be present. If a key is - specified which is not present in the Secret, the - volume setup will error unless it is marked optional. - Paths must be relative and may not contain the '..' - path or start with '..'. + description: |- + items If unspecified, each key-value pair in the Data field of the referenced + Secret will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. items: description: Maps a string key to a path within a volume. @@ -4358,22 +4851,20 @@ spec: description: key is the key to project. type: string mode: - description: 'mode is Optional: mode bits used - to set permissions on this file. Must be an - octal value between 0000 and 0777 or a decimal - value between 0 and 511. YAML accepts both octal - and decimal values, JSON requires decimal values - for mode bits. If not specified, the volume - defaultMode will be used. This might be in conflict - with other options that affect the file mode, - like fsGroup, and the result can be other mode - bits set.' + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. format: int32 type: integer path: - description: path is the relative path of the - file to map the key to. May not be an absolute - path. May not contain the path element '..'. + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. May not start with the string '..'. type: string required: @@ -4381,13 +4872,15 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic optional: description: optional field specify whether the Secret or its keys must be defined type: boolean secretName: - description: 'secretName is the name of the secret in - the pod''s namespace to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + description: |- + secretName is the name of the secret in the pod's namespace to use. + More info: https://kubernetes.io/docs/concepts/storage/volumes#secret type: string type: object storageos: @@ -4395,42 +4888,45 @@ spec: and mounted on Kubernetes nodes. properties: fsType: - description: fsType is the filesystem type to mount. - Must be a filesystem type supported by the host operating - system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred - to be "ext4" if unspecified. + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. type: string readOnly: - description: readOnly defaults to false (read/write). - ReadOnly here will force the ReadOnly setting in VolumeMounts. + description: |- + readOnly defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. type: boolean secretRef: - description: secretRef specifies the secret to use for - obtaining the StorageOS API credentials. If not specified, - default values will be attempted. + description: |- + secretRef specifies the secret to use for obtaining the StorageOS API + credentials. If not specified, default values will be attempted. properties: name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string type: object x-kubernetes-map-type: atomic volumeName: - description: volumeName is the human-readable name of - the StorageOS volume. Volume names are only unique - within a namespace. + description: |- + volumeName is the human-readable name of the StorageOS volume. Volume + names are only unique within a namespace. type: string volumeNamespace: - description: volumeNamespace specifies the scope of - the volume within StorageOS. If no namespace is specified - then the Pod's namespace will be used. This allows - the Kubernetes name scoping to be mirrored within - StorageOS for tighter integration. Set VolumeName - to any name to override the default behaviour. Set - to "default" if you are not using namespaces within - StorageOS. Namespaces that do not pre-exist within - StorageOS will be created. + description: |- + volumeNamespace specifies the scope of the volume within StorageOS. If no + namespace is specified then the Pod's namespace will be used. This allows the + Kubernetes name scoping to be mirrored within StorageOS for tighter integration. + Set VolumeName to any name to override the default behaviour. + Set to "default" if you are not using namespaces within StorageOS. + Namespaces that do not pre-exist within StorageOS will be created. type: string type: object vsphereVolume: @@ -4438,10 +4934,10 @@ spec: and mounted on kubelets host machine properties: fsType: - description: fsType is filesystem type to mount. Must - be a filesystem type supported by the host operating - system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred - to be "ext4" if unspecified. + description: |- + fsType is filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. type: string storagePolicyID: description: storagePolicyID is the storage Policy Based @@ -4463,6 +4959,16 @@ spec: type: object type: array type: object + selector: + description: |- + Defines a filter for the Vector Pipeline and Cluster Vector Pipeline by labels. + If not specified, all pipelines will be selected. + properties: + matchLabels: + additionalProperties: + type: string + type: object + type: object useApiServerCache: description: Determines if requests to the kube-apiserver can be served by a cache. diff --git a/helm/charts/vector-operator/templates/clusterrole.yaml b/helm/charts/vector-operator/templates/clusterrole.yaml index 66e43312..90507f54 100644 --- a/helm/charts/vector-operator/templates/clusterrole.yaml +++ b/helm/charts/vector-operator/templates/clusterrole.yaml @@ -78,6 +78,8 @@ rules: - clustervectorpipelines - vectorpipelines - vectors + - clustervectoraggregators + - vectoraggregators verbs: - create - delete @@ -93,6 +95,9 @@ rules: - vectorpipelines/status - vectors/status - vectors/finalizers + - clustervectoraggregators/status + - vectoraggregators/status + - vectoraggregators/finalizers verbs: - get - patch diff --git a/helm/index.yaml b/helm/index.yaml index 09f5f1f5..1c7d7ea9 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: pre-v0.1.0-r1 + created: "2024-10-07T10:57:49.918804+03:00" + description: A Helm chart to install Vector Operator + digest: 01bd2e347c5782127511a0a0fbbab72508cbe667664f2c9b615cabb80a4c40c7 + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.1.0-rc1.tgz + version: 0.1.0-rc1 - apiVersion: v2 appVersion: v0.0.40 - created: "2024-05-07T12:01:59.961007+03:00" + created: "2024-10-07T10:57:49.914215+03:00" description: A Helm chart to install Vector Operator digest: c50e673e811b8d4c03ad45c92ce23b9bc54eef4665091e70fab9a4b7e4c6c3f1 home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.0.40 - apiVersion: v2 appVersion: v0.0.39 - created: "2024-05-07T12:01:59.960184+03:00" + created: "2024-10-07T10:57:49.91345+03:00" description: A Helm chart to install Vector Operator digest: e1de38c869bf896bfb9f5f615c329d3ccce3bd91cb64bb6fa1c783a40ea290a2 home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.0.39 - apiVersion: v2 appVersion: v0.0.38 - created: "2024-05-07T12:01:59.959349+03:00" + created: "2024-10-07T10:57:49.912632+03:00" description: A Helm chart to install Vector Operator digest: 565b148184400900f5572b06ceebaac6340af5e5fd1122b308bdbfcbc2d2040a home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.0.38 - apiVersion: v2 appVersion: v0.0.37 - created: "2024-05-07T12:01:59.958197+03:00" + created: "2024-10-07T10:57:49.911953+03:00" description: A Helm chart to install Vector Operator digest: 65e10ee46e6855ba95f51e21ca14abc4411191b260c6b96ec72c775e73c1e331 home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.0.37 - apiVersion: v2 appVersion: v0.0.36 - created: "2024-05-07T12:01:59.957399+03:00" + created: "2024-10-07T10:57:49.911269+03:00" description: A Helm chart to install Vector Operator digest: 972b6b4048b6d17b0616786e49915ef52cb7c9573cfb6eb359b6c19b66eabe31 home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.0.36 - apiVersion: v2 appVersion: v0.0.35 - created: "2024-05-07T12:01:59.956626+03:00" + created: "2024-10-07T10:57:49.910542+03:00" description: A Helm chart to install Vector Operator digest: d03ba759c42f2bd8d8f1df71702ee4f26a73b8bc28760ca7254af20d811cee8a home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.0.35 - apiVersion: v2 appVersion: v0.0.34 - created: "2024-05-07T12:01:59.955826+03:00" + created: "2024-10-07T10:57:49.909446+03:00" description: A Helm chart to install Vector Operator digest: 01e9d488ee78c8603d821f96344edee568ebcb42049d586de37b8df39b372bd4 home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.0.34 - apiVersion: v2 appVersion: v0.0.33 - created: "2024-05-07T12:01:59.954638+03:00" + created: "2024-10-07T10:57:49.908516+03:00" description: A Helm chart to install Vector Operator digest: fcde3c94a0fa6caa5f3d333226c95b7c85ede8489d46277e1222a868ed4ec8c3 home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.0.33 - apiVersion: v2 appVersion: v0.0.32 - created: "2024-05-07T12:01:59.95383+03:00" + created: "2024-10-07T10:57:49.907819+03:00" description: A Helm chart to install Vector Operator digest: 26323037ec47f1703ea930a99ab4ec8fb93b44975ce969514ea68d4130017015 home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.0.32 - apiVersion: v2 appVersion: v0.0.31 - created: "2024-05-07T12:01:59.953024+03:00" + created: "2024-10-07T10:57:49.90709+03:00" description: A Helm chart to install Vector Operator digest: 45b924c07a825e0f7cd3fb534a6ffd16604790d13be1aff59150c045474754e3 home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.0.31 - apiVersion: v2 appVersion: v0.0.30 - created: "2024-05-07T12:01:59.952256+03:00" + created: "2024-10-07T10:57:49.906085+03:00" description: A Helm chart to install Vector Operator digest: 03beda549d15f50325028ea29af5f2065ac0b8adf3078bf7dc1312981aa5e7db home: https://github.com/kaasops/vector-operator @@ -146,7 +159,7 @@ entries: version: 0.0.30 - apiVersion: v2 appVersion: v0.0.29 - created: "2024-05-07T12:01:59.951028+03:00" + created: "2024-10-07T10:57:49.905307+03:00" description: A Helm chart to install Vector Operator digest: 0f025fc3a924b37b8c4131c4d8cfa437d2d4e557ab9476ed3e69a00232c7dca6 home: https://github.com/kaasops/vector-operator @@ -159,7 +172,7 @@ entries: version: 0.0.29 - apiVersion: v2 appVersion: v0.0.28 - created: "2024-05-07T12:01:59.950259+03:00" + created: "2024-10-07T10:57:49.904596+03:00" description: A Helm chart to install Vector Operator digest: af856d41314313e04f15e7143409a9c564c6ca610b0d2eaec3112add8573e668 home: https://github.com/kaasops/vector-operator @@ -172,7 +185,7 @@ entries: version: 0.0.28 - apiVersion: v2 appVersion: v0.0.27 - created: "2024-05-07T12:01:59.949486+03:00" + created: "2024-10-07T10:57:49.903883+03:00" description: A Helm chart to install Vector Operator digest: 631e2ff02bbd7f247cb486494fd2af60c57cc551066a6a3858226551bc1745a4 home: https://github.com/kaasops/vector-operator @@ -185,7 +198,7 @@ entries: version: 0.0.27 - apiVersion: v2 appVersion: v0.0.26 - created: "2024-05-07T12:01:59.948041+03:00" + created: "2024-10-07T10:57:49.90224+03:00" description: A Helm chart to install Vector Operator digest: 760a2833f4c1a33466982419b079ff18d996331ebacc40cf93b0f55229cdb7db home: https://github.com/kaasops/vector-operator @@ -198,7 +211,7 @@ entries: version: 0.0.26 - apiVersion: v2 appVersion: v0.0.25 - created: "2024-05-07T12:01:59.947206+03:00" + created: "2024-10-07T10:57:49.901525+03:00" description: A Helm chart to install Vector Operator digest: fd22b996b071b6d85740ccf76e85cb640fa717c2620748d206d3f4fdd44cbcc2 home: https://github.com/kaasops/vector-operator @@ -211,7 +224,7 @@ entries: version: 0.0.25 - apiVersion: v2 appVersion: v0.0.24 - created: "2024-05-07T12:01:59.946416+03:00" + created: "2024-10-07T10:57:49.900819+03:00" description: A Helm chart to install Vector Operator digest: ea257e60ecde063a0d1ed52ce5e3283245b8f0e2daba58ea3a5adb0ba82d7799 home: https://github.com/kaasops/vector-operator @@ -224,7 +237,7 @@ entries: version: 0.0.24 - apiVersion: v2 appVersion: v0.0.23 - created: "2024-05-07T12:01:59.945623+03:00" + created: "2024-10-07T10:57:49.899975+03:00" description: A Helm chart to install Vector Operator digest: 546d202b3b9263f789b88335263191098dfcabd5d8698105f37cad24d56a8ed0 home: https://github.com/kaasops/vector-operator @@ -237,7 +250,7 @@ entries: version: 0.0.23 - apiVersion: v2 appVersion: v0.0.22 - created: "2024-05-07T12:01:59.944612+03:00" + created: "2024-10-07T10:57:49.899212+03:00" description: A Helm chart to install Vector Operator digest: bf96ddc8ac61e9d6beb8bc763fbf3fa6025d950b29d70d80de6e8a0ea45e0411 home: https://github.com/kaasops/vector-operator @@ -250,7 +263,7 @@ entries: version: 0.0.22 - apiVersion: v2 appVersion: v0.0.21 - created: "2024-05-07T12:01:59.943791+03:00" + created: "2024-10-07T10:57:49.898422+03:00" description: A Helm chart to install Vector Operator digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d home: https://github.com/kaasops/vector-operator @@ -263,7 +276,7 @@ entries: version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2024-05-07T12:01:59.943012+03:00" + created: "2024-10-07T10:57:49.897403+03:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -276,7 +289,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2024-05-07T12:01:59.942501+03:00" + created: "2024-10-07T10:57:49.896707+03:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -289,7 +302,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2024-05-07T12:01:59.941667+03:00" + created: "2024-10-07T10:57:49.896254+03:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -302,7 +315,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2024-05-07T12:01:59.941093+03:00" + created: "2024-10-07T10:57:49.895815+03:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -315,7 +328,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2024-05-07T12:01:59.940593+03:00" + created: "2024-10-07T10:57:49.895378+03:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -328,7 +341,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2024-05-07T12:01:59.940044+03:00" + created: "2024-10-07T10:57:49.894927+03:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -341,7 +354,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2024-05-07T12:01:59.939519+03:00" + created: "2024-10-07T10:57:49.894437+03:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -354,7 +367,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2024-05-07T12:01:59.93897+03:00" + created: "2024-10-07T10:57:49.892715+03:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -367,7 +380,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2024-05-07T12:01:59.93735+03:00" + created: "2024-10-07T10:57:49.892084+03:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -380,7 +393,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2024-05-07T12:01:59.936802+03:00" + created: "2024-10-07T10:57:49.891466+03:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -393,7 +406,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2024-05-07T12:01:59.936344+03:00" + created: "2024-10-07T10:57:49.890841+03:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -406,7 +419,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2024-05-07T12:01:59.96261+03:00" + created: "2024-10-07T10:57:49.915965+03:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -419,7 +432,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2024-05-07T12:01:59.962184+03:00" + created: "2024-10-07T10:57:49.915634+03:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -432,7 +445,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2024-05-07T12:01:59.961794+03:00" + created: "2024-10-07T10:57:49.915303+03:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -445,7 +458,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2024-05-07T12:01:59.961408+03:00" + created: "2024-10-07T10:57:49.914817+03:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -458,7 +471,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2024-05-07T12:01:59.935855+03:00" + created: "2024-10-07T10:57:49.890295+03:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -469,4 +482,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2024-05-07T12:01:59.934718+03:00" +generated: "2024-10-07T10:57:49.889465+03:00" diff --git a/helm/packages/vector-operator-0.1.0-rc1.tgz b/helm/packages/vector-operator-0.1.0-rc1.tgz new file mode 100644 index 0000000000000000000000000000000000000000..5e2993fa0d17bce61b59270070e0bbbbb52d7c3f GIT binary patch literal 99683 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 P00960u$^!<0EiC&P8ktL literal 0 HcmV?d00001 From 46a9a49768bfbdb44a5fda36d89ef7e7a3369984 Mon Sep 17 00:00:00 2001 From: aa1ex <115736127+aa1ex@users.noreply.github.com> Date: Mon, 7 Oct 2024 12:50:27 +0300 Subject: [PATCH 275/316] fix role print && add doc fix role print && add doc Signed-off-by: Aleksandr Aleksandrov --- api/v1alpha1/clustervectorpipeline_types.go | 2 +- api/v1alpha1/vectorpipeline_types.go | 2 +- ...ity.kaasops.io_clustervectorpipelines.yaml | 2 +- ...ervability.kaasops.io_vectorpipelines.yaml | 2 +- docs/aggregator.md | 108 ++++++++++++++++++ docs/kubernetes-events.md | 86 ++++++++++++++ docs/logs-from-file.md | 2 +- 7 files changed, 199 insertions(+), 5 deletions(-) create mode 100644 docs/aggregator.md create mode 100644 docs/kubernetes-events.md diff --git a/api/v1alpha1/clustervectorpipeline_types.go b/api/v1alpha1/clustervectorpipeline_types.go index a9049b54..05d4b0e8 100644 --- a/api/v1alpha1/clustervectorpipeline_types.go +++ b/api/v1alpha1/clustervectorpipeline_types.go @@ -28,7 +28,7 @@ import ( //+kubebuilder:resource:scope=Cluster,shortName=cvp //+kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" //+kubebuilder:printcolumn:name="Valid",type="boolean",JSONPath=".status.configCheckResult" -//+kubebuilder:printcolumn:name="Role",type="boolean",JSONPath=".status.role" +//+kubebuilder:printcolumn:name="Role",type="string",JSONPath=".status.role" // ClusterVectorPipeline is the Schema for the clustervectorpipelines API type ClusterVectorPipeline struct { diff --git a/api/v1alpha1/vectorpipeline_types.go b/api/v1alpha1/vectorpipeline_types.go index 49b9c07a..f0366531 100644 --- a/api/v1alpha1/vectorpipeline_types.go +++ b/api/v1alpha1/vectorpipeline_types.go @@ -47,7 +47,7 @@ type VectorPipelineStatus struct { //+kubebuilder:resource:shortName=vp,categories=all //+kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" //+kubebuilder:printcolumn:name="Valid",type="boolean",JSONPath=".status.configCheckResult" -//+kubebuilder:printcolumn:name="Role",type="boolean",JSONPath=".status.role" +//+kubebuilder:printcolumn:name="Role",type="string",JSONPath=".status.role" // VectorPipeline is the Schema for the vectorpipelines API type VectorPipeline struct { diff --git a/config/crd/bases/observability.kaasops.io_clustervectorpipelines.yaml b/config/crd/bases/observability.kaasops.io_clustervectorpipelines.yaml index 994403bd..8515663d 100644 --- a/config/crd/bases/observability.kaasops.io_clustervectorpipelines.yaml +++ b/config/crd/bases/observability.kaasops.io_clustervectorpipelines.yaml @@ -25,7 +25,7 @@ spec: type: boolean - jsonPath: .status.role name: Role - type: boolean + type: string name: v1alpha1 schema: openAPIV3Schema: diff --git a/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml b/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml index bb6a81f2..9814ffc9 100644 --- a/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml +++ b/config/crd/bases/observability.kaasops.io_vectorpipelines.yaml @@ -27,7 +27,7 @@ spec: type: boolean - jsonPath: .status.role name: Role - type: boolean + type: string name: v1alpha1 schema: openAPIV3Schema: diff --git a/docs/aggregator.md b/docs/aggregator.md new file mode 100644 index 00000000..11448f55 --- /dev/null +++ b/docs/aggregator.md @@ -0,0 +1,108 @@ +# Aggregator + +The operator allows deploying Vector in the cluster as an aggregator for remote processing, [more details about this](https://vector.dev/docs/setup/going-to-prod/arch/aggregator/). +Two types of resources are available for deploying aggregators in the cluster: +- VectorAggregator +- ClusterVectorAggregator + +## VectorAggregator + +“The configuration for the aggregator is formed from valid vector pipelines in the same namespace. +The Service name is generated as follows: -aggregator-.” + +```yaml +apiVersion: observability.kaasops.io/v1alpha1 +kind: VectorAggregator +metadata: + name: vectorAggregator1 + namespace: vector +spec: + image: timberio/vector:0.40.0-debian + api: + enabled: true + replicas: 1 + tolerations: + - effect: NoSchedule + key: node-role.kubernetes.io/master + operator: Exists + - effect: NoSchedule + key: node-role.kubernetes.io/control-plane + operator: Exists +``` + +```yaml +apiVersion: observability.kaasops.io/v1alpha1 +kind: VectorPipeline +metadata: + name: vectorPipeline1 + namespace: vector +spec: + sources: + source-test: + type: "socket" + address: "0.0.0.0:9000" + mode: tcp + sinks: + sink-test: + type: "console" + encoding: + codec: "json" + inputs: + - source-test +``` + +## ClusterVectorAggregator + +ClusterVectorAggregator works similarly to VectorAggregator, but ClusterVectorPipeline is used for configuration formation. +To deploy resources (Deployment, Service), you need to specify resourceNamespace in the specification. + +```yaml +apiVersion: observability.kaasops.io/v1alpha1 +kind: ClusterVectorAggregator +metadata: + name: clusterVectorAggregator1 +spec: + image: timberio/vector:0.40.0-debian + resourceNamespace: default + api: + enabled: true + replicas: 1 + tolerations: + - effect: NoSchedule + key: node-role.kubernetes.io/master + operator: Exists + - effect: NoSchedule + key: node-role.kubernetes.io/control-plane + operator: Exists +``` + +```yaml +apiVersion: observability.kaasops.io/v1alpha1 +kind: ClusterVectorPipeline +metadata: + name: clusterVectorPipeline1 +spec: + sources: + source-test: + type: "socket" + mode: "tcp" + address: "0.0.0.0:9000" + sinks: + sink-test: + inputs: + - source-test + type: "elasticsearch" + api_version: auto + endpoints: + - https://test-elastic-http.default:9200 + mode: bulk + tls: + verify_certificate: false + bulk: + action: create + index: "test-%Y-%m-%d" + auth: + user: elastic + password: test-password + strategy: basic +``` \ No newline at end of file diff --git a/docs/kubernetes-events.md b/docs/kubernetes-events.md new file mode 100644 index 00000000..4a9c4013 --- /dev/null +++ b/docs/kubernetes-events.md @@ -0,0 +1,86 @@ +# [EXPERIMENTAL] Kubernetes events + +The operator allows organizing the collection of events from the Kubernetes cluster in which it is deployed. +To do this, you need to deploy an aggregator and a pipeline. +The operator allows collecting events from the entire cluster or from a specific namespace. + +## Namespace event collection + +```yaml +apiVersion: observability.kaasops.io/v1alpha1 +kind: VectorAggregator +metadata: + name: vectorAggregator1 + namespace: vector +spec: + image: timberio/vector:0.40.0-debian + api: + enabled: true + replicas: 1 + tolerations: + - effect: NoSchedule + key: node-role.kubernetes.io/master + operator: Exists + - effect: NoSchedule + key: node-role.kubernetes.io/control-plane + operator: Exists +``` + +```yaml +apiVersion: observability.kaasops.io/v1alpha1 +kind: VectorPipeline +metadata: + name: vectorPipeline1 + namespace: vector +spec: + sources: + source-test: + type: "kubernetes_events" + sinks: + sink-test: + type: "console" + encoding: + codec: "json" + inputs: + - source-test +``` + +## Collection of all events in the cluster + +```yaml +apiVersion: observability.kaasops.io/v1alpha1 +kind: ClusterVectorAggregator +metadata: + name: clusterVectorAggregator1 +spec: + image: timberio/vector:0.40.0-debian + resourceNamespace: default + api: + enabled: true + replicas: 1 + tolerations: + - effect: NoSchedule + key: node-role.kubernetes.io/master + operator: Exists + - effect: NoSchedule + key: node-role.kubernetes.io/control-plane + operator: Exists +``` + +```yaml +apiVersion: observability.kaasops.io/v1alpha1 +kind: ClusterVectorPipeline +metadata: + name: clusterVectorPipeline1 +spec: + sources: + source-test: + type: "kubernetes_events" + sinks: + sink-test: + type: "console" + encoding: + codec: "json" + inputs: + - source-test +``` \ No newline at end of file diff --git a/docs/logs-from-file.md b/docs/logs-from-file.md index 8192efa4..fe8cb775 100644 --- a/docs/logs-from-file.md +++ b/docs/logs-from-file.md @@ -1,4 +1,4 @@ -# Secure credential +# Logs from file If you want collect logs from file (like k8s-audit logs) you can use example. From fa713d8a8d5226ad9a1fc0782bf824b173869607 Mon Sep 17 00:00:00 2001 From: zvlb Date: Mon, 7 Oct 2024 12:53:00 +0300 Subject: [PATCH 276/316] prepare for v0.1.0 release Signed-off-by: zvlb --- helm/charts/vector-operator/Chart.yaml | 4 +- ...ity.kaasops.io_clustervectorpipelines.yaml | 2 +- ...ervability.kaasops.io_vectorpipelines.yaml | 2 +- helm/index.yaml | 89 ++++++++++-------- helm/packages/vector-operator-0.1.0.tgz | Bin 0 -> 99676 bytes 5 files changed, 55 insertions(+), 42 deletions(-) create mode 100644 helm/packages/vector-operator-0.1.0.tgz diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index 941d5d4a..99dbe95b 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.1.0-rc1 +version: 0.1.0 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "pre-v0.1.0-r1" +appVersion: "v0.1.0" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectorpipelines.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectorpipelines.yaml index 994403bd..8515663d 100644 --- a/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectorpipelines.yaml +++ b/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectorpipelines.yaml @@ -25,7 +25,7 @@ spec: type: boolean - jsonPath: .status.role name: Role - type: boolean + type: string name: v1alpha1 schema: openAPIV3Schema: diff --git a/helm/charts/vector-operator/crds/observability.kaasops.io_vectorpipelines.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_vectorpipelines.yaml index bb6a81f2..9814ffc9 100644 --- a/helm/charts/vector-operator/crds/observability.kaasops.io_vectorpipelines.yaml +++ b/helm/charts/vector-operator/crds/observability.kaasops.io_vectorpipelines.yaml @@ -27,7 +27,7 @@ spec: type: boolean - jsonPath: .status.role name: Role - type: boolean + type: string name: v1alpha1 schema: openAPIV3Schema: diff --git a/helm/index.yaml b/helm/index.yaml index 1c7d7ea9..e61fc1be 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.1.0 + created: "2024-10-07T12:52:16.968427+03:00" + description: A Helm chart to install Vector Operator + digest: 191ec4f83f11541df19680ce220992c84fb10210f0d54a5acc29361ccfd787bb + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.1.0.tgz + version: 0.1.0 - apiVersion: v2 appVersion: pre-v0.1.0-r1 - created: "2024-10-07T10:57:49.918804+03:00" + created: "2024-10-07T12:52:16.965982+03:00" description: A Helm chart to install Vector Operator digest: 01bd2e347c5782127511a0a0fbbab72508cbe667664f2c9b615cabb80a4c40c7 home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.1.0-rc1 - apiVersion: v2 appVersion: v0.0.40 - created: "2024-10-07T10:57:49.914215+03:00" + created: "2024-10-07T12:52:16.961708+03:00" description: A Helm chart to install Vector Operator digest: c50e673e811b8d4c03ad45c92ce23b9bc54eef4665091e70fab9a4b7e4c6c3f1 home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.0.40 - apiVersion: v2 appVersion: v0.0.39 - created: "2024-10-07T10:57:49.91345+03:00" + created: "2024-10-07T12:52:16.961019+03:00" description: A Helm chart to install Vector Operator digest: e1de38c869bf896bfb9f5f615c329d3ccce3bd91cb64bb6fa1c783a40ea290a2 home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.0.39 - apiVersion: v2 appVersion: v0.0.38 - created: "2024-10-07T10:57:49.912632+03:00" + created: "2024-10-07T12:52:16.960231+03:00" description: A Helm chart to install Vector Operator digest: 565b148184400900f5572b06ceebaac6340af5e5fd1122b308bdbfcbc2d2040a home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.0.38 - apiVersion: v2 appVersion: v0.0.37 - created: "2024-10-07T10:57:49.911953+03:00" + created: "2024-10-07T12:52:16.959438+03:00" description: A Helm chart to install Vector Operator digest: 65e10ee46e6855ba95f51e21ca14abc4411191b260c6b96ec72c775e73c1e331 home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.0.37 - apiVersion: v2 appVersion: v0.0.36 - created: "2024-10-07T10:57:49.911269+03:00" + created: "2024-10-07T12:52:16.958747+03:00" description: A Helm chart to install Vector Operator digest: 972b6b4048b6d17b0616786e49915ef52cb7c9573cfb6eb359b6c19b66eabe31 home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.0.36 - apiVersion: v2 appVersion: v0.0.35 - created: "2024-10-07T10:57:49.910542+03:00" + created: "2024-10-07T12:52:16.958047+03:00" description: A Helm chart to install Vector Operator digest: d03ba759c42f2bd8d8f1df71702ee4f26a73b8bc28760ca7254af20d811cee8a home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.0.35 - apiVersion: v2 appVersion: v0.0.34 - created: "2024-10-07T10:57:49.909446+03:00" + created: "2024-10-07T12:52:16.957272+03:00" description: A Helm chart to install Vector Operator digest: 01e9d488ee78c8603d821f96344edee568ebcb42049d586de37b8df39b372bd4 home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.0.34 - apiVersion: v2 appVersion: v0.0.33 - created: "2024-10-07T10:57:49.908516+03:00" + created: "2024-10-07T12:52:16.956217+03:00" description: A Helm chart to install Vector Operator digest: fcde3c94a0fa6caa5f3d333226c95b7c85ede8489d46277e1222a868ed4ec8c3 home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.0.33 - apiVersion: v2 appVersion: v0.0.32 - created: "2024-10-07T10:57:49.907819+03:00" + created: "2024-10-07T12:52:16.955519+03:00" description: A Helm chart to install Vector Operator digest: 26323037ec47f1703ea930a99ab4ec8fb93b44975ce969514ea68d4130017015 home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.0.32 - apiVersion: v2 appVersion: v0.0.31 - created: "2024-10-07T10:57:49.90709+03:00" + created: "2024-10-07T12:52:16.954785+03:00" description: A Helm chart to install Vector Operator digest: 45b924c07a825e0f7cd3fb534a6ffd16604790d13be1aff59150c045474754e3 home: https://github.com/kaasops/vector-operator @@ -146,7 +159,7 @@ entries: version: 0.0.31 - apiVersion: v2 appVersion: v0.0.30 - created: "2024-10-07T10:57:49.906085+03:00" + created: "2024-10-07T12:52:16.954078+03:00" description: A Helm chart to install Vector Operator digest: 03beda549d15f50325028ea29af5f2065ac0b8adf3078bf7dc1312981aa5e7db home: https://github.com/kaasops/vector-operator @@ -159,7 +172,7 @@ entries: version: 0.0.30 - apiVersion: v2 appVersion: v0.0.29 - created: "2024-10-07T10:57:49.905307+03:00" + created: "2024-10-07T12:52:16.952864+03:00" description: A Helm chart to install Vector Operator digest: 0f025fc3a924b37b8c4131c4d8cfa437d2d4e557ab9476ed3e69a00232c7dca6 home: https://github.com/kaasops/vector-operator @@ -172,7 +185,7 @@ entries: version: 0.0.29 - apiVersion: v2 appVersion: v0.0.28 - created: "2024-10-07T10:57:49.904596+03:00" + created: "2024-10-07T12:52:16.95213+03:00" description: A Helm chart to install Vector Operator digest: af856d41314313e04f15e7143409a9c564c6ca610b0d2eaec3112add8573e668 home: https://github.com/kaasops/vector-operator @@ -185,7 +198,7 @@ entries: version: 0.0.28 - apiVersion: v2 appVersion: v0.0.27 - created: "2024-10-07T10:57:49.903883+03:00" + created: "2024-10-07T12:52:16.951355+03:00" description: A Helm chart to install Vector Operator digest: 631e2ff02bbd7f247cb486494fd2af60c57cc551066a6a3858226551bc1745a4 home: https://github.com/kaasops/vector-operator @@ -198,7 +211,7 @@ entries: version: 0.0.27 - apiVersion: v2 appVersion: v0.0.26 - created: "2024-10-07T10:57:49.90224+03:00" + created: "2024-10-07T12:52:16.950419+03:00" description: A Helm chart to install Vector Operator digest: 760a2833f4c1a33466982419b079ff18d996331ebacc40cf93b0f55229cdb7db home: https://github.com/kaasops/vector-operator @@ -211,7 +224,7 @@ entries: version: 0.0.26 - apiVersion: v2 appVersion: v0.0.25 - created: "2024-10-07T10:57:49.901525+03:00" + created: "2024-10-07T12:52:16.949319+03:00" description: A Helm chart to install Vector Operator digest: fd22b996b071b6d85740ccf76e85cb640fa717c2620748d206d3f4fdd44cbcc2 home: https://github.com/kaasops/vector-operator @@ -224,7 +237,7 @@ entries: version: 0.0.25 - apiVersion: v2 appVersion: v0.0.24 - created: "2024-10-07T10:57:49.900819+03:00" + created: "2024-10-07T12:52:16.948579+03:00" description: A Helm chart to install Vector Operator digest: ea257e60ecde063a0d1ed52ce5e3283245b8f0e2daba58ea3a5adb0ba82d7799 home: https://github.com/kaasops/vector-operator @@ -237,7 +250,7 @@ entries: version: 0.0.24 - apiVersion: v2 appVersion: v0.0.23 - created: "2024-10-07T10:57:49.899975+03:00" + created: "2024-10-07T12:52:16.947812+03:00" description: A Helm chart to install Vector Operator digest: 546d202b3b9263f789b88335263191098dfcabd5d8698105f37cad24d56a8ed0 home: https://github.com/kaasops/vector-operator @@ -250,7 +263,7 @@ entries: version: 0.0.23 - apiVersion: v2 appVersion: v0.0.22 - created: "2024-10-07T10:57:49.899212+03:00" + created: "2024-10-07T12:52:16.947071+03:00" description: A Helm chart to install Vector Operator digest: bf96ddc8ac61e9d6beb8bc763fbf3fa6025d950b29d70d80de6e8a0ea45e0411 home: https://github.com/kaasops/vector-operator @@ -263,7 +276,7 @@ entries: version: 0.0.22 - apiVersion: v2 appVersion: v0.0.21 - created: "2024-10-07T10:57:49.898422+03:00" + created: "2024-10-07T12:52:16.945968+03:00" description: A Helm chart to install Vector Operator digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d home: https://github.com/kaasops/vector-operator @@ -276,7 +289,7 @@ entries: version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2024-10-07T10:57:49.897403+03:00" + created: "2024-10-07T12:52:16.94499+03:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -289,7 +302,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2024-10-07T10:57:49.896707+03:00" + created: "2024-10-07T12:52:16.944555+03:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -302,7 +315,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2024-10-07T10:57:49.896254+03:00" + created: "2024-10-07T12:52:16.944108+03:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -315,7 +328,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2024-10-07T10:57:49.895815+03:00" + created: "2024-10-07T12:52:16.943656+03:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -328,7 +341,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2024-10-07T10:57:49.895378+03:00" + created: "2024-10-07T12:52:16.942591+03:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -341,7 +354,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2024-10-07T10:57:49.894927+03:00" + created: "2024-10-07T12:52:16.942078+03:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -354,7 +367,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2024-10-07T10:57:49.894437+03:00" + created: "2024-10-07T12:52:16.941624+03:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -367,7 +380,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2024-10-07T10:57:49.892715+03:00" + created: "2024-10-07T12:52:16.941009+03:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -380,7 +393,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2024-10-07T10:57:49.892084+03:00" + created: "2024-10-07T12:52:16.940398+03:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -393,7 +406,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2024-10-07T10:57:49.891466+03:00" + created: "2024-10-07T12:52:16.938328+03:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -406,7 +419,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2024-10-07T10:57:49.890841+03:00" + created: "2024-10-07T12:52:16.93774+03:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -419,7 +432,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2024-10-07T10:57:49.915965+03:00" + created: "2024-10-07T12:52:16.963791+03:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -432,7 +445,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2024-10-07T10:57:49.915634+03:00" + created: "2024-10-07T12:52:16.963453+03:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -445,7 +458,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2024-10-07T10:57:49.915303+03:00" + created: "2024-10-07T12:52:16.963115+03:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -458,7 +471,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2024-10-07T10:57:49.914817+03:00" + created: "2024-10-07T12:52:16.962648+03:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -471,7 +484,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2024-10-07T10:57:49.890295+03:00" + created: "2024-10-07T12:52:16.937139+03:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -482,4 +495,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2024-10-07T10:57:49.889465+03:00" +generated: "2024-10-07T12:52:16.936329+03:00" diff --git a/helm/packages/vector-operator-0.1.0.tgz b/helm/packages/vector-operator-0.1.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..bdae75ffe44c2a74a0881208aeb98aa18daa118f GIT binary patch literal 99676 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

TdArk649aWGs=~3usbn3h56M9^E5>& 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Ð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 Date: Tue, 8 Oct 2024 08:40:34 +0300 Subject: [PATCH 277/316] fix: filter vector pipelines for agent --- internal/pipeline/pipeline.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/pipeline/pipeline.go b/internal/pipeline/pipeline.go index f2678499..559bef6e 100644 --- a/internal/pipeline/pipeline.go +++ b/internal/pipeline/pipeline.go @@ -79,7 +79,7 @@ func GetValidPipelines(ctx context.Context, client client.Client, filter FilterP if !vp.IsDeleted() && vp.IsValid() && vp.GetRole() == filter.Role && - vp.Namespace == filter.Namespace && + (filter.Scope == AllPipelines || vp.Namespace == filter.Namespace) && MatchLabels(matchLabels, vp.Labels) { validPipelines = append(validPipelines, vp.DeepCopy()) } From 51c66da1a076662d8cd1f938edaaac66b045a826 Mon Sep 17 00:00:00 2001 From: zvlb Date: Tue, 8 Oct 2024 08:41:31 +0300 Subject: [PATCH 278/316] prepare v0.1.1 release Signed-off-by: zvlb --- helm/charts/vector-operator/Chart.yaml | 4 +- helm/index.yaml | 91 ++++++++++++++---------- helm/packages/vector-operator-0.1.1.tgz | Bin 0 -> 99674 bytes 3 files changed, 54 insertions(+), 41 deletions(-) create mode 100644 helm/packages/vector-operator-0.1.1.tgz diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index 99dbe95b..088dea72 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.1.0 +version: 0.1.1 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.1.0" +appVersion: "v0.1.1" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/index.yaml b/helm/index.yaml index e61fc1be..1a0d6f7a 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.1.1 + created: "2024-10-08T08:41:18.45172+03:00" + description: A Helm chart to install Vector Operator + digest: 3ac5a422f3f1861528f737a0fea077cad5f7b7516db3fe5d392c887d5d3459d5 + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.1.1.tgz + version: 0.1.1 - apiVersion: v2 appVersion: v0.1.0 - created: "2024-10-07T12:52:16.968427+03:00" + created: "2024-10-08T08:41:18.449862+03:00" description: A Helm chart to install Vector Operator digest: 191ec4f83f11541df19680ce220992c84fb10210f0d54a5acc29361ccfd787bb home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.1.0 - apiVersion: v2 appVersion: pre-v0.1.0-r1 - created: "2024-10-07T12:52:16.965982+03:00" + created: "2024-10-08T08:41:18.447345+03:00" description: A Helm chart to install Vector Operator digest: 01bd2e347c5782127511a0a0fbbab72508cbe667664f2c9b615cabb80a4c40c7 home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.1.0-rc1 - apiVersion: v2 appVersion: v0.0.40 - created: "2024-10-07T12:52:16.961708+03:00" + created: "2024-10-08T08:41:18.443641+03:00" description: A Helm chart to install Vector Operator digest: c50e673e811b8d4c03ad45c92ce23b9bc54eef4665091e70fab9a4b7e4c6c3f1 home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.0.40 - apiVersion: v2 appVersion: v0.0.39 - created: "2024-10-07T12:52:16.961019+03:00" + created: "2024-10-08T08:41:18.442948+03:00" description: A Helm chart to install Vector Operator digest: e1de38c869bf896bfb9f5f615c329d3ccce3bd91cb64bb6fa1c783a40ea290a2 home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.0.39 - apiVersion: v2 appVersion: v0.0.38 - created: "2024-10-07T12:52:16.960231+03:00" + created: "2024-10-08T08:41:18.44217+03:00" description: A Helm chart to install Vector Operator digest: 565b148184400900f5572b06ceebaac6340af5e5fd1122b308bdbfcbc2d2040a home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.0.38 - apiVersion: v2 appVersion: v0.0.37 - created: "2024-10-07T12:52:16.959438+03:00" + created: "2024-10-08T08:41:18.441346+03:00" description: A Helm chart to install Vector Operator digest: 65e10ee46e6855ba95f51e21ca14abc4411191b260c6b96ec72c775e73c1e331 home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.0.37 - apiVersion: v2 appVersion: v0.0.36 - created: "2024-10-07T12:52:16.958747+03:00" + created: "2024-10-08T08:41:18.440663+03:00" description: A Helm chart to install Vector Operator digest: 972b6b4048b6d17b0616786e49915ef52cb7c9573cfb6eb359b6c19b66eabe31 home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.0.36 - apiVersion: v2 appVersion: v0.0.35 - created: "2024-10-07T12:52:16.958047+03:00" + created: "2024-10-08T08:41:18.439969+03:00" description: A Helm chart to install Vector Operator digest: d03ba759c42f2bd8d8f1df71702ee4f26a73b8bc28760ca7254af20d811cee8a home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.0.35 - apiVersion: v2 appVersion: v0.0.34 - created: "2024-10-07T12:52:16.957272+03:00" + created: "2024-10-08T08:41:18.439268+03:00" description: A Helm chart to install Vector Operator digest: 01e9d488ee78c8603d821f96344edee568ebcb42049d586de37b8df39b372bd4 home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.0.34 - apiVersion: v2 appVersion: v0.0.33 - created: "2024-10-07T12:52:16.956217+03:00" + created: "2024-10-08T08:41:18.438095+03:00" description: A Helm chart to install Vector Operator digest: fcde3c94a0fa6caa5f3d333226c95b7c85ede8489d46277e1222a868ed4ec8c3 home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.0.33 - apiVersion: v2 appVersion: v0.0.32 - created: "2024-10-07T12:52:16.955519+03:00" + created: "2024-10-08T08:41:18.437397+03:00" description: A Helm chart to install Vector Operator digest: 26323037ec47f1703ea930a99ab4ec8fb93b44975ce969514ea68d4130017015 home: https://github.com/kaasops/vector-operator @@ -146,7 +159,7 @@ entries: version: 0.0.32 - apiVersion: v2 appVersion: v0.0.31 - created: "2024-10-07T12:52:16.954785+03:00" + created: "2024-10-08T08:41:18.436679+03:00" description: A Helm chart to install Vector Operator digest: 45b924c07a825e0f7cd3fb534a6ffd16604790d13be1aff59150c045474754e3 home: https://github.com/kaasops/vector-operator @@ -159,7 +172,7 @@ entries: version: 0.0.31 - apiVersion: v2 appVersion: v0.0.30 - created: "2024-10-07T12:52:16.954078+03:00" + created: "2024-10-08T08:41:18.435922+03:00" description: A Helm chart to install Vector Operator digest: 03beda549d15f50325028ea29af5f2065ac0b8adf3078bf7dc1312981aa5e7db home: https://github.com/kaasops/vector-operator @@ -172,7 +185,7 @@ entries: version: 0.0.30 - apiVersion: v2 appVersion: v0.0.29 - created: "2024-10-07T12:52:16.952864+03:00" + created: "2024-10-08T08:41:18.435067+03:00" description: A Helm chart to install Vector Operator digest: 0f025fc3a924b37b8c4131c4d8cfa437d2d4e557ab9476ed3e69a00232c7dca6 home: https://github.com/kaasops/vector-operator @@ -185,7 +198,7 @@ entries: version: 0.0.29 - apiVersion: v2 appVersion: v0.0.28 - created: "2024-10-07T12:52:16.95213+03:00" + created: "2024-10-08T08:41:18.43434+03:00" description: A Helm chart to install Vector Operator digest: af856d41314313e04f15e7143409a9c564c6ca610b0d2eaec3112add8573e668 home: https://github.com/kaasops/vector-operator @@ -198,7 +211,7 @@ entries: version: 0.0.28 - apiVersion: v2 appVersion: v0.0.27 - created: "2024-10-07T12:52:16.951355+03:00" + created: "2024-10-08T08:41:18.433622+03:00" description: A Helm chart to install Vector Operator digest: 631e2ff02bbd7f247cb486494fd2af60c57cc551066a6a3858226551bc1745a4 home: https://github.com/kaasops/vector-operator @@ -211,7 +224,7 @@ entries: version: 0.0.27 - apiVersion: v2 appVersion: v0.0.26 - created: "2024-10-07T12:52:16.950419+03:00" + created: "2024-10-08T08:41:18.432733+03:00" description: A Helm chart to install Vector Operator digest: 760a2833f4c1a33466982419b079ff18d996331ebacc40cf93b0f55229cdb7db home: https://github.com/kaasops/vector-operator @@ -224,7 +237,7 @@ entries: version: 0.0.26 - apiVersion: v2 appVersion: v0.0.25 - created: "2024-10-07T12:52:16.949319+03:00" + created: "2024-10-08T08:41:18.431495+03:00" description: A Helm chart to install Vector Operator digest: fd22b996b071b6d85740ccf76e85cb640fa717c2620748d206d3f4fdd44cbcc2 home: https://github.com/kaasops/vector-operator @@ -237,7 +250,7 @@ entries: version: 0.0.25 - apiVersion: v2 appVersion: v0.0.24 - created: "2024-10-07T12:52:16.948579+03:00" + created: "2024-10-08T08:41:18.430725+03:00" description: A Helm chart to install Vector Operator digest: ea257e60ecde063a0d1ed52ce5e3283245b8f0e2daba58ea3a5adb0ba82d7799 home: https://github.com/kaasops/vector-operator @@ -250,7 +263,7 @@ entries: version: 0.0.24 - apiVersion: v2 appVersion: v0.0.23 - created: "2024-10-07T12:52:16.947812+03:00" + created: "2024-10-08T08:41:18.429975+03:00" description: A Helm chart to install Vector Operator digest: 546d202b3b9263f789b88335263191098dfcabd5d8698105f37cad24d56a8ed0 home: https://github.com/kaasops/vector-operator @@ -263,7 +276,7 @@ entries: version: 0.0.23 - apiVersion: v2 appVersion: v0.0.22 - created: "2024-10-07T12:52:16.947071+03:00" + created: "2024-10-08T08:41:18.429203+03:00" description: A Helm chart to install Vector Operator digest: bf96ddc8ac61e9d6beb8bc763fbf3fa6025d950b29d70d80de6e8a0ea45e0411 home: https://github.com/kaasops/vector-operator @@ -276,7 +289,7 @@ entries: version: 0.0.22 - apiVersion: v2 appVersion: v0.0.21 - created: "2024-10-07T12:52:16.945968+03:00" + created: "2024-10-08T08:41:18.42799+03:00" description: A Helm chart to install Vector Operator digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d home: https://github.com/kaasops/vector-operator @@ -289,7 +302,7 @@ entries: version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2024-10-07T12:52:16.94499+03:00" + created: "2024-10-08T08:41:18.426988+03:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -302,7 +315,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2024-10-07T12:52:16.944555+03:00" + created: "2024-10-08T08:41:18.426541+03:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -315,7 +328,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2024-10-07T12:52:16.944108+03:00" + created: "2024-10-08T08:41:18.426091+03:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -328,7 +341,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2024-10-07T12:52:16.943656+03:00" + created: "2024-10-08T08:41:18.425613+03:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -341,7 +354,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2024-10-07T12:52:16.942591+03:00" + created: "2024-10-08T08:41:18.424799+03:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -354,7 +367,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2024-10-07T12:52:16.942078+03:00" + created: "2024-10-08T08:41:18.424292+03:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -367,7 +380,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2024-10-07T12:52:16.941624+03:00" + created: "2024-10-08T08:41:18.423836+03:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -380,7 +393,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2024-10-07T12:52:16.941009+03:00" + created: "2024-10-08T08:41:18.423367+03:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -393,7 +406,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2024-10-07T12:52:16.940398+03:00" + created: "2024-10-08T08:41:18.422778+03:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -406,7 +419,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2024-10-07T12:52:16.938328+03:00" + created: "2024-10-08T08:41:18.422143+03:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -419,7 +432,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2024-10-07T12:52:16.93774+03:00" + created: "2024-10-08T08:41:18.421612+03:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -432,7 +445,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2024-10-07T12:52:16.963791+03:00" + created: "2024-10-08T08:41:18.445171+03:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -445,7 +458,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2024-10-07T12:52:16.963453+03:00" + created: "2024-10-08T08:41:18.444845+03:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -458,7 +471,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2024-10-07T12:52:16.963115+03:00" + created: "2024-10-08T08:41:18.444509+03:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -471,7 +484,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2024-10-07T12:52:16.962648+03:00" + created: "2024-10-08T08:41:18.44401+03:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -484,7 +497,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2024-10-07T12:52:16.937139+03:00" + created: "2024-10-08T08:41:18.420322+03:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -495,4 +508,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2024-10-07T12:52:16.936329+03:00" +generated: "2024-10-08T08:41:18.419557+03:00" diff --git a/helm/packages/vector-operator-0.1.1.tgz b/helm/packages/vector-operator-0.1.1.tgz new file mode 100644 index 0000000000000000000000000000000000000000..81b19aef06fdbc2e0caa45209f1ef0f557a034e9 GIT binary patch literal 99674 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 bUx#uihjJ)~V#@y?00960;L^L#0EiC&F@ID_ literal 0 HcmV?d00001 From fa7b65c29c6534c1ea19b0fff27804e56ec0663c Mon Sep 17 00:00:00 2001 From: Vladimir <31961982+zvlb@users.noreply.github.com> Date: Wed, 9 Oct 2024 11:30:57 +0300 Subject: [PATCH 279/316] fix: add event access for operator (#156) * fix: add event access for operator Signed-off-by: zvlb --- helm/charts/vector-operator/Chart.yaml | 2 +- .../templates/clusterrole.yaml | 6 ++ helm/index.yaml | 93 ++++++++++-------- helm/packages/vector-operator-0.2.tgz | Bin 0 -> 99687 bytes 4 files changed, 60 insertions(+), 41 deletions(-) create mode 100644 helm/packages/vector-operator-0.2.tgz diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index 088dea72..62d0fada 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.1.1 +version: "0.2" # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/helm/charts/vector-operator/templates/clusterrole.yaml b/helm/charts/vector-operator/templates/clusterrole.yaml index 90507f54..d12b0adb 100644 --- a/helm/charts/vector-operator/templates/clusterrole.yaml +++ b/helm/charts/vector-operator/templates/clusterrole.yaml @@ -108,4 +108,10 @@ rules: - podmonitors verbs: - '*' +- apiGroups: + - "" + resources: + - events + verbs: + - watch {{- end -}} diff --git a/helm/index.yaml b/helm/index.yaml index 1a0d6f7a..9a850d34 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -3,7 +3,20 @@ entries: vector-operator: - apiVersion: v2 appVersion: v0.1.1 - created: "2024-10-08T08:41:18.45172+03:00" + created: "2024-10-09T11:29:29.474117+03:00" + description: A Helm chart to install Vector Operator + digest: 582d95c6f63134f6cd815bcb85adce5770e179de21a979ec76f91ab7d8531b45 + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.2.tgz + version: "0.2" + - apiVersion: v2 + appVersion: v0.1.1 + created: "2024-10-09T11:29:29.472112+03:00" description: A Helm chart to install Vector Operator digest: 3ac5a422f3f1861528f737a0fea077cad5f7b7516db3fe5d392c887d5d3459d5 home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.1.1 - apiVersion: v2 appVersion: v0.1.0 - created: "2024-10-08T08:41:18.449862+03:00" + created: "2024-10-09T11:29:29.470032+03:00" description: A Helm chart to install Vector Operator digest: 191ec4f83f11541df19680ce220992c84fb10210f0d54a5acc29361ccfd787bb home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.1.0 - apiVersion: v2 appVersion: pre-v0.1.0-r1 - created: "2024-10-08T08:41:18.447345+03:00" + created: "2024-10-09T11:29:29.467986+03:00" description: A Helm chart to install Vector Operator digest: 01bd2e347c5782127511a0a0fbbab72508cbe667664f2c9b615cabb80a4c40c7 home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.1.0-rc1 - apiVersion: v2 appVersion: v0.0.40 - created: "2024-10-08T08:41:18.443641+03:00" + created: "2024-10-09T11:29:29.463809+03:00" description: A Helm chart to install Vector Operator digest: c50e673e811b8d4c03ad45c92ce23b9bc54eef4665091e70fab9a4b7e4c6c3f1 home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.0.40 - apiVersion: v2 appVersion: v0.0.39 - created: "2024-10-08T08:41:18.442948+03:00" + created: "2024-10-09T11:29:29.462662+03:00" description: A Helm chart to install Vector Operator digest: e1de38c869bf896bfb9f5f615c329d3ccce3bd91cb64bb6fa1c783a40ea290a2 home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.0.39 - apiVersion: v2 appVersion: v0.0.38 - created: "2024-10-08T08:41:18.44217+03:00" + created: "2024-10-09T11:29:29.461634+03:00" description: A Helm chart to install Vector Operator digest: 565b148184400900f5572b06ceebaac6340af5e5fd1122b308bdbfcbc2d2040a home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.0.38 - apiVersion: v2 appVersion: v0.0.37 - created: "2024-10-08T08:41:18.441346+03:00" + created: "2024-10-09T11:29:29.460573+03:00" description: A Helm chart to install Vector Operator digest: 65e10ee46e6855ba95f51e21ca14abc4411191b260c6b96ec72c775e73c1e331 home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.0.37 - apiVersion: v2 appVersion: v0.0.36 - created: "2024-10-08T08:41:18.440663+03:00" + created: "2024-10-09T11:29:29.459556+03:00" description: A Helm chart to install Vector Operator digest: 972b6b4048b6d17b0616786e49915ef52cb7c9573cfb6eb359b6c19b66eabe31 home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.0.36 - apiVersion: v2 appVersion: v0.0.35 - created: "2024-10-08T08:41:18.439969+03:00" + created: "2024-10-09T11:29:29.458472+03:00" description: A Helm chart to install Vector Operator digest: d03ba759c42f2bd8d8f1df71702ee4f26a73b8bc28760ca7254af20d811cee8a home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.0.35 - apiVersion: v2 appVersion: v0.0.34 - created: "2024-10-08T08:41:18.439268+03:00" + created: "2024-10-09T11:29:29.457521+03:00" description: A Helm chart to install Vector Operator digest: 01e9d488ee78c8603d821f96344edee568ebcb42049d586de37b8df39b372bd4 home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.0.34 - apiVersion: v2 appVersion: v0.0.33 - created: "2024-10-08T08:41:18.438095+03:00" + created: "2024-10-09T11:29:29.456526+03:00" description: A Helm chart to install Vector Operator digest: fcde3c94a0fa6caa5f3d333226c95b7c85ede8489d46277e1222a868ed4ec8c3 home: https://github.com/kaasops/vector-operator @@ -146,7 +159,7 @@ entries: version: 0.0.33 - apiVersion: v2 appVersion: v0.0.32 - created: "2024-10-08T08:41:18.437397+03:00" + created: "2024-10-09T11:29:29.45546+03:00" description: A Helm chart to install Vector Operator digest: 26323037ec47f1703ea930a99ab4ec8fb93b44975ce969514ea68d4130017015 home: https://github.com/kaasops/vector-operator @@ -159,7 +172,7 @@ entries: version: 0.0.32 - apiVersion: v2 appVersion: v0.0.31 - created: "2024-10-08T08:41:18.436679+03:00" + created: "2024-10-09T11:29:29.454065+03:00" description: A Helm chart to install Vector Operator digest: 45b924c07a825e0f7cd3fb534a6ffd16604790d13be1aff59150c045474754e3 home: https://github.com/kaasops/vector-operator @@ -172,7 +185,7 @@ entries: version: 0.0.31 - apiVersion: v2 appVersion: v0.0.30 - created: "2024-10-08T08:41:18.435922+03:00" + created: "2024-10-09T11:29:29.453171+03:00" description: A Helm chart to install Vector Operator digest: 03beda549d15f50325028ea29af5f2065ac0b8adf3078bf7dc1312981aa5e7db home: https://github.com/kaasops/vector-operator @@ -185,7 +198,7 @@ entries: version: 0.0.30 - apiVersion: v2 appVersion: v0.0.29 - created: "2024-10-08T08:41:18.435067+03:00" + created: "2024-10-09T11:29:29.452268+03:00" description: A Helm chart to install Vector Operator digest: 0f025fc3a924b37b8c4131c4d8cfa437d2d4e557ab9476ed3e69a00232c7dca6 home: https://github.com/kaasops/vector-operator @@ -198,7 +211,7 @@ entries: version: 0.0.29 - apiVersion: v2 appVersion: v0.0.28 - created: "2024-10-08T08:41:18.43434+03:00" + created: "2024-10-09T11:29:29.451334+03:00" description: A Helm chart to install Vector Operator digest: af856d41314313e04f15e7143409a9c564c6ca610b0d2eaec3112add8573e668 home: https://github.com/kaasops/vector-operator @@ -211,7 +224,7 @@ entries: version: 0.0.28 - apiVersion: v2 appVersion: v0.0.27 - created: "2024-10-08T08:41:18.433622+03:00" + created: "2024-10-09T11:29:29.450019+03:00" description: A Helm chart to install Vector Operator digest: 631e2ff02bbd7f247cb486494fd2af60c57cc551066a6a3858226551bc1745a4 home: https://github.com/kaasops/vector-operator @@ -224,7 +237,7 @@ entries: version: 0.0.27 - apiVersion: v2 appVersion: v0.0.26 - created: "2024-10-08T08:41:18.432733+03:00" + created: "2024-10-09T11:29:29.449212+03:00" description: A Helm chart to install Vector Operator digest: 760a2833f4c1a33466982419b079ff18d996331ebacc40cf93b0f55229cdb7db home: https://github.com/kaasops/vector-operator @@ -237,7 +250,7 @@ entries: version: 0.0.26 - apiVersion: v2 appVersion: v0.0.25 - created: "2024-10-08T08:41:18.431495+03:00" + created: "2024-10-09T11:29:29.448242+03:00" description: A Helm chart to install Vector Operator digest: fd22b996b071b6d85740ccf76e85cb640fa717c2620748d206d3f4fdd44cbcc2 home: https://github.com/kaasops/vector-operator @@ -250,7 +263,7 @@ entries: version: 0.0.25 - apiVersion: v2 appVersion: v0.0.24 - created: "2024-10-08T08:41:18.430725+03:00" + created: "2024-10-09T11:29:29.447093+03:00" description: A Helm chart to install Vector Operator digest: ea257e60ecde063a0d1ed52ce5e3283245b8f0e2daba58ea3a5adb0ba82d7799 home: https://github.com/kaasops/vector-operator @@ -263,7 +276,7 @@ entries: version: 0.0.24 - apiVersion: v2 appVersion: v0.0.23 - created: "2024-10-08T08:41:18.429975+03:00" + created: "2024-10-09T11:29:29.446194+03:00" description: A Helm chart to install Vector Operator digest: 546d202b3b9263f789b88335263191098dfcabd5d8698105f37cad24d56a8ed0 home: https://github.com/kaasops/vector-operator @@ -276,7 +289,7 @@ entries: version: 0.0.23 - apiVersion: v2 appVersion: v0.0.22 - created: "2024-10-08T08:41:18.429203+03:00" + created: "2024-10-09T11:29:29.445263+03:00" description: A Helm chart to install Vector Operator digest: bf96ddc8ac61e9d6beb8bc763fbf3fa6025d950b29d70d80de6e8a0ea45e0411 home: https://github.com/kaasops/vector-operator @@ -289,7 +302,7 @@ entries: version: 0.0.22 - apiVersion: v2 appVersion: v0.0.21 - created: "2024-10-08T08:41:18.42799+03:00" + created: "2024-10-09T11:29:29.443892+03:00" description: A Helm chart to install Vector Operator digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d home: https://github.com/kaasops/vector-operator @@ -302,7 +315,7 @@ entries: version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2024-10-08T08:41:18.426988+03:00" + created: "2024-10-09T11:29:29.442875+03:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -315,7 +328,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2024-10-08T08:41:18.426541+03:00" + created: "2024-10-09T11:29:29.442291+03:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -328,7 +341,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2024-10-08T08:41:18.426091+03:00" + created: "2024-10-09T11:29:29.4416+03:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -341,7 +354,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2024-10-08T08:41:18.425613+03:00" + created: "2024-10-09T11:29:29.440938+03:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -354,7 +367,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2024-10-08T08:41:18.424799+03:00" + created: "2024-10-09T11:29:29.440146+03:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -367,7 +380,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2024-10-08T08:41:18.424292+03:00" + created: "2024-10-09T11:29:29.438822+03:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -380,7 +393,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2024-10-08T08:41:18.423836+03:00" + created: "2024-10-09T11:29:29.438283+03:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -393,7 +406,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2024-10-08T08:41:18.423367+03:00" + created: "2024-10-09T11:29:29.437544+03:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -406,7 +419,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2024-10-08T08:41:18.422778+03:00" + created: "2024-10-09T11:29:29.436822+03:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -419,7 +432,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2024-10-08T08:41:18.422143+03:00" + created: "2024-10-09T11:29:29.43622+03:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -432,7 +445,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2024-10-08T08:41:18.421612+03:00" + created: "2024-10-09T11:29:29.435202+03:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -445,7 +458,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2024-10-08T08:41:18.445171+03:00" + created: "2024-10-09T11:29:29.465634+03:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -458,7 +471,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2024-10-08T08:41:18.444845+03:00" + created: "2024-10-09T11:29:29.465264+03:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -471,7 +484,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2024-10-08T08:41:18.444509+03:00" + created: "2024-10-09T11:29:29.464832+03:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -484,7 +497,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2024-10-08T08:41:18.44401+03:00" + created: "2024-10-09T11:29:29.464328+03:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -497,7 +510,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2024-10-08T08:41:18.420322+03:00" + created: "2024-10-09T11:29:29.434717+03:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -508,4 +521,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2024-10-08T08:41:18.419557+03:00" +generated: "2024-10-09T11:29:29.434068+03:00" diff --git a/helm/packages/vector-operator-0.2.tgz b/helm/packages/vector-operator-0.2.tgz new file mode 100644 index 0000000000000000000000000000000000000000..0195ca3358a0b1eb65507e4113ad1e9d6bd51c57 GIT binary patch literal 99687 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

yY^=yjAP<9NdM%?;VYn%?>hd*4& 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 Date: Wed, 9 Oct 2024 11:40:03 +0300 Subject: [PATCH 280/316] fix: add list access for events (#158) * fix: add list access for events Signed-off-by: zvlb --- helm/charts/vector-operator/Chart.yaml | 2 +- .../templates/clusterrole.yaml | 1 + helm/index.yaml | 95 ++++++++++-------- helm/packages/vector-operator-0.3.tgz | Bin 0 -> 99688 bytes 4 files changed, 56 insertions(+), 42 deletions(-) create mode 100644 helm/packages/vector-operator-0.3.tgz diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index 62d0fada..0a15d008 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: "0.2" +version: "0.3" # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/helm/charts/vector-operator/templates/clusterrole.yaml b/helm/charts/vector-operator/templates/clusterrole.yaml index d12b0adb..09102c1f 100644 --- a/helm/charts/vector-operator/templates/clusterrole.yaml +++ b/helm/charts/vector-operator/templates/clusterrole.yaml @@ -114,4 +114,5 @@ rules: - events verbs: - watch + - list {{- end -}} diff --git a/helm/index.yaml b/helm/index.yaml index 9a850d34..76ea5f61 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -3,7 +3,20 @@ entries: vector-operator: - apiVersion: v2 appVersion: v0.1.1 - created: "2024-10-09T11:29:29.474117+03:00" + created: "2024-10-09T11:39:24.733735+03:00" + description: A Helm chart to install Vector Operator + digest: a916c9e9f81bdbf9f734073fb453a6b67d7a724ed7ff4326d7884b136c103ce5 + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.3.tgz + version: "0.3" + - apiVersion: v2 + appVersion: v0.1.1 + created: "2024-10-09T11:39:24.73182+03:00" description: A Helm chart to install Vector Operator digest: 582d95c6f63134f6cd815bcb85adce5770e179de21a979ec76f91ab7d8531b45 home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: "0.2" - apiVersion: v2 appVersion: v0.1.1 - created: "2024-10-09T11:29:29.472112+03:00" + created: "2024-10-09T11:39:24.729714+03:00" description: A Helm chart to install Vector Operator digest: 3ac5a422f3f1861528f737a0fea077cad5f7b7516db3fe5d392c887d5d3459d5 home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.1.1 - apiVersion: v2 appVersion: v0.1.0 - created: "2024-10-09T11:29:29.470032+03:00" + created: "2024-10-09T11:39:24.727149+03:00" description: A Helm chart to install Vector Operator digest: 191ec4f83f11541df19680ce220992c84fb10210f0d54a5acc29361ccfd787bb home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.1.0 - apiVersion: v2 appVersion: pre-v0.1.0-r1 - created: "2024-10-09T11:29:29.467986+03:00" + created: "2024-10-09T11:39:24.724886+03:00" description: A Helm chart to install Vector Operator digest: 01bd2e347c5782127511a0a0fbbab72508cbe667664f2c9b615cabb80a4c40c7 home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.1.0-rc1 - apiVersion: v2 appVersion: v0.0.40 - created: "2024-10-09T11:29:29.463809+03:00" + created: "2024-10-09T11:39:24.720433+03:00" description: A Helm chart to install Vector Operator digest: c50e673e811b8d4c03ad45c92ce23b9bc54eef4665091e70fab9a4b7e4c6c3f1 home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.0.40 - apiVersion: v2 appVersion: v0.0.39 - created: "2024-10-09T11:29:29.462662+03:00" + created: "2024-10-09T11:39:24.719279+03:00" description: A Helm chart to install Vector Operator digest: e1de38c869bf896bfb9f5f615c329d3ccce3bd91cb64bb6fa1c783a40ea290a2 home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.0.39 - apiVersion: v2 appVersion: v0.0.38 - created: "2024-10-09T11:29:29.461634+03:00" + created: "2024-10-09T11:39:24.718339+03:00" description: A Helm chart to install Vector Operator digest: 565b148184400900f5572b06ceebaac6340af5e5fd1122b308bdbfcbc2d2040a home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.0.38 - apiVersion: v2 appVersion: v0.0.37 - created: "2024-10-09T11:29:29.460573+03:00" + created: "2024-10-09T11:39:24.717327+03:00" description: A Helm chart to install Vector Operator digest: 65e10ee46e6855ba95f51e21ca14abc4411191b260c6b96ec72c775e73c1e331 home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.0.37 - apiVersion: v2 appVersion: v0.0.36 - created: "2024-10-09T11:29:29.459556+03:00" + created: "2024-10-09T11:39:24.716307+03:00" description: A Helm chart to install Vector Operator digest: 972b6b4048b6d17b0616786e49915ef52cb7c9573cfb6eb359b6c19b66eabe31 home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.0.36 - apiVersion: v2 appVersion: v0.0.35 - created: "2024-10-09T11:29:29.458472+03:00" + created: "2024-10-09T11:39:24.715146+03:00" description: A Helm chart to install Vector Operator digest: d03ba759c42f2bd8d8f1df71702ee4f26a73b8bc28760ca7254af20d811cee8a home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.0.35 - apiVersion: v2 appVersion: v0.0.34 - created: "2024-10-09T11:29:29.457521+03:00" + created: "2024-10-09T11:39:24.714207+03:00" description: A Helm chart to install Vector Operator digest: 01e9d488ee78c8603d821f96344edee568ebcb42049d586de37b8df39b372bd4 home: https://github.com/kaasops/vector-operator @@ -146,7 +159,7 @@ entries: version: 0.0.34 - apiVersion: v2 appVersion: v0.0.33 - created: "2024-10-09T11:29:29.456526+03:00" + created: "2024-10-09T11:39:24.713503+03:00" description: A Helm chart to install Vector Operator digest: fcde3c94a0fa6caa5f3d333226c95b7c85ede8489d46277e1222a868ed4ec8c3 home: https://github.com/kaasops/vector-operator @@ -159,7 +172,7 @@ entries: version: 0.0.33 - apiVersion: v2 appVersion: v0.0.32 - created: "2024-10-09T11:29:29.45546+03:00" + created: "2024-10-09T11:39:24.712414+03:00" description: A Helm chart to install Vector Operator digest: 26323037ec47f1703ea930a99ab4ec8fb93b44975ce969514ea68d4130017015 home: https://github.com/kaasops/vector-operator @@ -172,7 +185,7 @@ entries: version: 0.0.32 - apiVersion: v2 appVersion: v0.0.31 - created: "2024-10-09T11:29:29.454065+03:00" + created: "2024-10-09T11:39:24.711008+03:00" description: A Helm chart to install Vector Operator digest: 45b924c07a825e0f7cd3fb534a6ffd16604790d13be1aff59150c045474754e3 home: https://github.com/kaasops/vector-operator @@ -185,7 +198,7 @@ entries: version: 0.0.31 - apiVersion: v2 appVersion: v0.0.30 - created: "2024-10-09T11:29:29.453171+03:00" + created: "2024-10-09T11:39:24.709998+03:00" description: A Helm chart to install Vector Operator digest: 03beda549d15f50325028ea29af5f2065ac0b8adf3078bf7dc1312981aa5e7db home: https://github.com/kaasops/vector-operator @@ -198,7 +211,7 @@ entries: version: 0.0.30 - apiVersion: v2 appVersion: v0.0.29 - created: "2024-10-09T11:29:29.452268+03:00" + created: "2024-10-09T11:39:24.709287+03:00" description: A Helm chart to install Vector Operator digest: 0f025fc3a924b37b8c4131c4d8cfa437d2d4e557ab9476ed3e69a00232c7dca6 home: https://github.com/kaasops/vector-operator @@ -211,7 +224,7 @@ entries: version: 0.0.29 - apiVersion: v2 appVersion: v0.0.28 - created: "2024-10-09T11:29:29.451334+03:00" + created: "2024-10-09T11:39:24.708534+03:00" description: A Helm chart to install Vector Operator digest: af856d41314313e04f15e7143409a9c564c6ca610b0d2eaec3112add8573e668 home: https://github.com/kaasops/vector-operator @@ -224,7 +237,7 @@ entries: version: 0.0.28 - apiVersion: v2 appVersion: v0.0.27 - created: "2024-10-09T11:29:29.450019+03:00" + created: "2024-10-09T11:39:24.707333+03:00" description: A Helm chart to install Vector Operator digest: 631e2ff02bbd7f247cb486494fd2af60c57cc551066a6a3858226551bc1745a4 home: https://github.com/kaasops/vector-operator @@ -237,7 +250,7 @@ entries: version: 0.0.27 - apiVersion: v2 appVersion: v0.0.26 - created: "2024-10-09T11:29:29.449212+03:00" + created: "2024-10-09T11:39:24.706695+03:00" description: A Helm chart to install Vector Operator digest: 760a2833f4c1a33466982419b079ff18d996331ebacc40cf93b0f55229cdb7db home: https://github.com/kaasops/vector-operator @@ -250,7 +263,7 @@ entries: version: 0.0.26 - apiVersion: v2 appVersion: v0.0.25 - created: "2024-10-09T11:29:29.448242+03:00" + created: "2024-10-09T11:39:24.706048+03:00" description: A Helm chart to install Vector Operator digest: fd22b996b071b6d85740ccf76e85cb640fa717c2620748d206d3f4fdd44cbcc2 home: https://github.com/kaasops/vector-operator @@ -263,7 +276,7 @@ entries: version: 0.0.25 - apiVersion: v2 appVersion: v0.0.24 - created: "2024-10-09T11:29:29.447093+03:00" + created: "2024-10-09T11:39:24.705103+03:00" description: A Helm chart to install Vector Operator digest: ea257e60ecde063a0d1ed52ce5e3283245b8f0e2daba58ea3a5adb0ba82d7799 home: https://github.com/kaasops/vector-operator @@ -276,7 +289,7 @@ entries: version: 0.0.24 - apiVersion: v2 appVersion: v0.0.23 - created: "2024-10-09T11:29:29.446194+03:00" + created: "2024-10-09T11:39:24.704224+03:00" description: A Helm chart to install Vector Operator digest: 546d202b3b9263f789b88335263191098dfcabd5d8698105f37cad24d56a8ed0 home: https://github.com/kaasops/vector-operator @@ -289,7 +302,7 @@ entries: version: 0.0.23 - apiVersion: v2 appVersion: v0.0.22 - created: "2024-10-09T11:29:29.445263+03:00" + created: "2024-10-09T11:39:24.703203+03:00" description: A Helm chart to install Vector Operator digest: bf96ddc8ac61e9d6beb8bc763fbf3fa6025d950b29d70d80de6e8a0ea45e0411 home: https://github.com/kaasops/vector-operator @@ -302,7 +315,7 @@ entries: version: 0.0.22 - apiVersion: v2 appVersion: v0.0.21 - created: "2024-10-09T11:29:29.443892+03:00" + created: "2024-10-09T11:39:24.702489+03:00" description: A Helm chart to install Vector Operator digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d home: https://github.com/kaasops/vector-operator @@ -315,7 +328,7 @@ entries: version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2024-10-09T11:29:29.442875+03:00" + created: "2024-10-09T11:39:24.701078+03:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -328,7 +341,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2024-10-09T11:29:29.442291+03:00" + created: "2024-10-09T11:39:24.700584+03:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -341,7 +354,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2024-10-09T11:29:29.4416+03:00" + created: "2024-10-09T11:39:24.69997+03:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -354,7 +367,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2024-10-09T11:29:29.440938+03:00" + created: "2024-10-09T11:39:24.699408+03:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -367,7 +380,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2024-10-09T11:29:29.440146+03:00" + created: "2024-10-09T11:39:24.698883+03:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -380,7 +393,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2024-10-09T11:29:29.438822+03:00" + created: "2024-10-09T11:39:24.698273+03:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -393,7 +406,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2024-10-09T11:29:29.438283+03:00" + created: "2024-10-09T11:39:24.697277+03:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -406,7 +419,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2024-10-09T11:29:29.437544+03:00" + created: "2024-10-09T11:39:24.696713+03:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -419,7 +432,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2024-10-09T11:29:29.436822+03:00" + created: "2024-10-09T11:39:24.696119+03:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -432,7 +445,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2024-10-09T11:29:29.43622+03:00" + created: "2024-10-09T11:39:24.695569+03:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -445,7 +458,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2024-10-09T11:29:29.435202+03:00" + created: "2024-10-09T11:39:24.695047+03:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -458,7 +471,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2024-10-09T11:29:29.465634+03:00" + created: "2024-10-09T11:39:24.722197+03:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -471,7 +484,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2024-10-09T11:29:29.465264+03:00" + created: "2024-10-09T11:39:24.721843+03:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -484,7 +497,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2024-10-09T11:29:29.464832+03:00" + created: "2024-10-09T11:39:24.7215+03:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -497,7 +510,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2024-10-09T11:29:29.464328+03:00" + created: "2024-10-09T11:39:24.720958+03:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -510,7 +523,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2024-10-09T11:29:29.434717+03:00" + created: "2024-10-09T11:39:24.694551+03:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -521,4 +534,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2024-10-09T11:29:29.434068+03:00" +generated: "2024-10-09T11:39:24.693244+03:00" diff --git a/helm/packages/vector-operator-0.3.tgz b/helm/packages/vector-operator-0.3.tgz new file mode 100644 index 0000000000000000000000000000000000000000..f9636e9049c642961f9124f6d284a7ac4d4de50f GIT binary patch literal 99688 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 literal 0 HcmV?d00001 From d09b06f745cb6ee274e35e4f524b873a124eeef8 Mon Sep 17 00:00:00 2001 From: aa1ex <115736127+aa1ex@users.noreply.github.com> Date: Mon, 4 Nov 2024 12:37:42 +0200 Subject: [PATCH 281/316] Event collector (#159) restruct event-collector Signed-off-by: Aleksandr Aleksandrov --- .github/workflows/build-image.yaml | 15 +- Dockerfile | 7 +- Makefile | 6 +- api/v1alpha1/vector_common_types.go | 9 +- api/v1alpha1/zz_generated.deepcopy.go | 16 + cmd/event_collector/main.go | 142 +++++++++ cmd/evgen/main.go | 88 ++++++ cmd/{ => manager}/main.go | 9 +- ...y.kaasops.io_clustervectoraggregators.yaml | 12 + ...vability.kaasops.io_vectoraggregators.yaml | 12 + config/manager/kustomization.yaml | 8 +- config/rbac/role.yaml | 19 +- event_collector.Dockerfile | 37 +++ go.mod | 15 +- go.sum | 30 ++ .../templates/clusterrole.yaml | 1 + internal/buildinfo/info.go | 3 + internal/common/annotations.go | 4 +- internal/config/aggregator.go | 12 + internal/config/config.go | 22 ++ .../clustervectoraggregator_controller.go | 8 +- ...clustervectoraggregator_controller_test.go | 1 - internal/controller/pipeline_controller.go | 8 +- .../controller/pipeline_controller_test.go | 1 - internal/controller/suite_test.go | 9 - .../controller/vectoraggregator_controller.go | 9 +- .../vectoraggregator_controller_test.go | 2 - internal/evcollector/collector.go | 171 +++++++++++ internal/evcollector/config.go | 13 + internal/{k8sevents => evcollector}/event.go | 16 +- internal/evcollector/metrics.go | 24 ++ internal/k8sevents/manager.go | 84 ------ internal/k8sevents/watcher.go | 141 --------- internal/utils/k8s/k8s.go | 18 ++ internal/vector/aggregator/controller.go | 19 +- internal/vector/aggregator/event_collector.go | 279 ++++++++++++++++++ internal/vector/aggregator/service.go | 26 +- 37 files changed, 984 insertions(+), 312 deletions(-) create mode 100644 cmd/event_collector/main.go create mode 100644 cmd/evgen/main.go rename cmd/{ => manager}/main.go (97%) create mode 100644 event_collector.Dockerfile create mode 100644 internal/buildinfo/info.go create mode 100644 internal/evcollector/collector.go create mode 100644 internal/evcollector/config.go rename internal/{k8sevents => evcollector}/event.go (85%) create mode 100644 internal/evcollector/metrics.go delete mode 100644 internal/k8sevents/manager.go delete mode 100644 internal/k8sevents/watcher.go create mode 100644 internal/vector/aggregator/event_collector.go diff --git a/.github/workflows/build-image.yaml b/.github/workflows/build-image.yaml index ff8b6c7e..e83a3ade 100644 --- a/.github/workflows/build-image.yaml +++ b/.github/workflows/build-image.yaml @@ -30,10 +30,21 @@ jobs: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Build image and push to Docker Hub + - name: Build operator image and push to Docker Hub uses: docker/build-push-action@v2 with: context: . platforms: linux/amd64,linux/arm64 push: true - tags: ${{ github.repository }}:${{ steps.tag.outputs.TAG }},${{ github.repository }}:latest \ No newline at end of file + build-args: VERSION=${{ steps.tag.outputs.TAG }} + tags: ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:${{ steps.tag.outputs.TAG }},${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:latest + + - name: Build event-collector image and push to Docker Hub + uses: docker/build-push-action@v2 + with: + context: . + file: event_collector.Dockerfile + platforms: linux/amd64,linux/arm64 + push: true + build-args: VERSION=${{ steps.tag.outputs.TAG }} + tags: ${{ secrets.DOCKERHUB_USERNAME }}/event-collector:${{ steps.tag.outputs.TAG }},${{ secrets.DOCKERHUB_USERNAME }}/event-collector:latest \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 4ba18b68..8fcbeaff 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,6 +2,7 @@ FROM golang:1.22 AS builder ARG TARGETOS ARG TARGETARCH +ARG VERSION="dev" WORKDIR /workspace # Copy the Go Modules manifests @@ -12,7 +13,7 @@ COPY go.sum go.sum RUN go mod download # Copy the go source -COPY cmd/main.go cmd/main.go +COPY cmd/manager/main.go cmd/main.go COPY api/ api/ COPY internal/ internal/ @@ -21,7 +22,9 @@ COPY internal/ internal/ # was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO # the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore, # by leaving it empty we can ensure that the container and binary shipped on it will have the same platform. -RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager cmd/main.go +RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build \ + -ldflags="-X github.com/kaasops/vector-operator/internal/buildinfo.Version=${VERSION}" \ + -a -o manager cmd/main.go # Use distroless as minimal base image to package the manager binary # Refer to https://github.com/GoogleContainerTools/distroless for more details diff --git a/Makefile b/Makefile index ead1afaf..c05a018a 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,6 @@ # Image URL to use all building/pushing image targets IMG ?= controller:latest +IMG_COLLECTOR ?= collector:latest # ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. ENVTEST_K8S_VERSION = 1.31.0 @@ -80,7 +81,8 @@ lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes .PHONY: build build: manifests generate fmt vet ## Build manager binary. - go build -o bin/manager cmd/main.go + go build -o bin/manager cmd/manager/main.go + go build -o bin/event_collector cmd/event_collector/main.go .PHONY: run run: manifests generate fmt vet ## Run a controller from your host. @@ -92,10 +94,12 @@ run: manifests generate fmt vet ## Run a controller from your host. .PHONY: docker-build docker-build: ## Build docker image with the manager. $(CONTAINER_TOOL) build -t ${IMG} . + $(CONTAINER_TOOL) build -t ${IMG_COLLECTOR} -f event_collector.Dockerfile . .PHONY: docker-push docker-push: ## Push docker image with the manager. $(CONTAINER_TOOL) push ${IMG} + $(CONTAINER_TOOL) push ${IMG_COLLECTOR} # PLATFORMS defines the target platforms for the manager image be built to provide support to multiple # architectures. (i.e. make docker-buildx IMG=myregistry/mypoperator:0.0.1). To use this option you need to: diff --git a/api/v1alpha1/vector_common_types.go b/api/v1alpha1/vector_common_types.go index dd2a3d98..846259d6 100644 --- a/api/v1alpha1/vector_common_types.go +++ b/api/v1alpha1/vector_common_types.go @@ -137,5 +137,12 @@ type VectorAggregatorCommon struct { Replicas int32 `json:"replicas,omitempty"` // Selector defines a filter for the Vector Pipeline and Cluster Vector Pipeline by labels. // If not specified, all pipelines will be selected. - Selector *VectorSelectorSpec `json:"selector,omitempty"` + Selector *VectorSelectorSpec `json:"selector,omitempty"` + EventCollector EventCollector `json:"eventCollector,omitempty"` +} + +type EventCollector struct { + Image string `json:"image,omitempty"` + ImagePullPolicy v1.PullPolicy `json:"imagePullPolicy,omitempty"` + MaxBatchSize int32 `json:"maxBatchSize,omitempty"` } diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index f09fd3f2..002479b6 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -238,6 +238,21 @@ func (in *ConfigCheck) DeepCopy() *ConfigCheck { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EventCollector) DeepCopyInto(out *EventCollector) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EventCollector. +func (in *EventCollector) DeepCopy() *EventCollector { + if in == nil { + return nil + } + out := new(EventCollector) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Vector) DeepCopyInto(out *Vector) { *out = *in @@ -317,6 +332,7 @@ func (in *VectorAggregatorCommon) DeepCopyInto(out *VectorAggregatorCommon) { *out = new(VectorSelectorSpec) (*in).DeepCopyInto(*out) } + out.EventCollector = in.EventCollector } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VectorAggregatorCommon. diff --git a/cmd/event_collector/main.go b/cmd/event_collector/main.go new file mode 100644 index 00000000..ae2f07f0 --- /dev/null +++ b/cmd/event_collector/main.go @@ -0,0 +1,142 @@ +package main + +import ( + "context" + "errors" + "flag" + "fmt" + "github.com/fsnotify/fsnotify" + "github.com/kaasops/vector-operator/internal/buildinfo" + "github.com/kaasops/vector-operator/internal/evcollector" + "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/spf13/viper" + "k8s.io/client-go/kubernetes" + "log/slog" + "net" + "net/http" + "os" + "os/signal" + ctrl "sigs.k8s.io/controller-runtime" + "strings" + "syscall" +) + +func main() { + configPath := flag.String("config", "/etc/event-collector/config.json", "path to config file") // data is taken from a ConfigMap created by the Vector operator + port := flag.String("port", "8080", "port to listen on") + logLevel := flag.String("log-level", "info", "log level (debug, info, warn, error)") + flag.Parse() + + log := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: parseLogLevel(*logLevel)})) + log.Info("build info", "version", buildinfo.Version) + log.Info("starting kubernetes-events-collector") + + // kubernetes client + k8sCfg, err := ctrl.GetConfig() + if err != nil { + log.Error("unable to load kubernetes config", "error", err) + os.Exit(1) + } + log.Info("kubernetes config loaded") + + clientset, err := kubernetes.NewForConfig(k8sCfg) + if err != nil { + log.Error("unable to create clientset") + os.Exit(1) + } + + log.Info("kubernetes clientset created") + + // config + v := viper.New() + v.SetConfigFile(*configPath) + + if err := v.ReadInConfig(); err != nil { + log.Error("failed to read config file", "error", err) + os.Exit(1) + } + + var cfg evcollector.Config + store := make(map[string]*evcollector.Collector) + + setupCollector := func(svcName, svcNamespace, port, watchedNamespace string) { + host := fmt.Sprintf("%s.%s", svcName, svcNamespace) + addr := net.JoinHostPort(host, port) + + if oldC, ok := store[host]; ok { + if oldC.Addr == addr && oldC.Namespace == watchedNamespace { + return + } + oldC.Stop() + } + + c := evcollector.New(addr, watchedNamespace, cfg.MaxBatchSize, log, clientset.CoreV1().RESTClient()) + store[host] = c + c.Start() + } + + applyConfig := func() { + if err := v.Unmarshal(&cfg); err != nil { + log.Error("failed to unmarshal config", "error", err) + os.Exit(1) + } + + notModified := make(map[string]struct{}, len(store)) + for k := range store { + notModified[k] = struct{}{} + } + + for _, r := range cfg.Receivers { + setupCollector(r.ServiceName, r.ServiceNamespace, r.Port, r.WatchedNamespace) + delete(notModified, fmt.Sprintf("%s.%s", r.ServiceName, r.ServiceNamespace)) + } + + for k := range notModified { + store[k].Stop() + delete(store, k) + } + } + + applyConfig() + + log.Info("config loaded", "config", cfg) + + v.WatchConfig() + v.OnConfigChange(func(_ fsnotify.Event) { + log.Info("config file changed") + applyConfig() + }) + + ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) + defer stop() + + http.Handle("/metrics", promhttp.Handler()) + go func() { + if err = http.ListenAndServe(net.JoinHostPort("", *port), nil); err != nil && !errors.Is(http.ErrServerClosed, err) { + log.Error("failed to start http server", "error", err) + os.Exit(1) + } + }() + log.Info("starting http server on port " + *port) + + <-ctx.Done() + log.Info("shutting down") + + for k := range store { + store[k].Stop() + delete(store, k) + } +} + +func parseLogLevel(level string) slog.Level { + switch strings.ToLower(level) { + case "debug": + return slog.LevelDebug + case "warn": + return slog.LevelWarn + case "error": + return slog.LevelError + default: + return slog.LevelInfo + } +} diff --git a/cmd/evgen/main.go b/cmd/evgen/main.go new file mode 100644 index 00000000..21c50a48 --- /dev/null +++ b/cmd/evgen/main.go @@ -0,0 +1,88 @@ +package main + +import ( + "context" + "flag" + "fmt" + "k8s.io/apimachinery/pkg/util/rand" + ctrl "sigs.k8s.io/controller-runtime" + "sync" + "time" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" +) + +func main() { + numEvents := flag.Int("events", 300, "Number of events to create") + namespace := flag.String("namespace", "default", "Namespace where the events will be created") + workers := flag.Int("workers", 50, "Number of workers to create") + flag.Parse() + + if *workers <= 0 { + panic("Number of workers must be greater than 0") + } + + config, err := ctrl.GetConfig() + if err != nil { + panic(err) + } + + events := make(chan *corev1.Event) + + wg := sync.WaitGroup{} + + for i := 0; i < *workers; i++ { + go func() { + wg.Add(1) + defer wg.Done() + + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + panic(err) + } + + for event := range events { + _, err := clientset.CoreV1().Events(*namespace).Create(context.TODO(), event, metav1.CreateOptions{}) + if err != nil { + fmt.Printf("Error creating event: %v\n", err) + } + } + }() + } + + start := time.Now() + suffix := rand.String(3) + + for i := 0; i < *numEvents; i++ { + eventName := fmt.Sprintf("test-event-%s-%d", suffix, i) + + event := &corev1.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: eventName, + Namespace: *namespace, + }, + InvolvedObject: corev1.ObjectReference{ + Kind: "Pod", + Namespace: *namespace, + Name: "test-pod", + UID: "12345", + }, + Reason: "TestEvent", + Message: fmt.Sprintf("This is test event number %d", i+1), + Source: corev1.EventSource{ + Component: "event-generator", + }, + FirstTimestamp: metav1.Time{Time: time.Now()}, + LastTimestamp: metav1.Time{Time: time.Now()}, + Count: 1, + Type: "Normal", + } + events <- event + } + + fmt.Printf("Event generation completed, elapsed time: %s", time.Since(start)) + close(events) + wg.Wait() +} diff --git a/cmd/main.go b/cmd/manager/main.go similarity index 97% rename from cmd/main.go rename to cmd/manager/main.go index eb015ee5..d3df8177 100644 --- a/cmd/main.go +++ b/cmd/manager/main.go @@ -21,11 +21,10 @@ import ( "crypto/tls" "flag" "fmt" + "github.com/kaasops/vector-operator/internal/buildinfo" "os" "time" - "github.com/kaasops/vector-operator/internal/k8sevents" - monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -101,6 +100,7 @@ func main() { flag.Parse() ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) + setupLog.Info("build info", "version", buildinfo.Version) // if the enable-http2 flag is false (the default), http/2 should be disabled // due to its vulnerabilities. More specifically, disabling http/2 will @@ -212,8 +212,6 @@ func main() { clusterVectorAggregatorsPipelineEventCh := make(chan event.GenericEvent, 10) defer close(clusterVectorAggregatorsPipelineEventCh) - evCollector := k8sevents.NewEventsCollector(clientset, ctrl.Log.WithName("kubernetes-events-collector")) - if err = (&controller.PipelineReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), @@ -222,7 +220,6 @@ func main() { VectorAgentEventCh: vectorAgentsPipelineEventCh, VectorAggregatorsEventCh: vectorAggregatorsPipelineEventCh, ClusterVectorAggregatorsEventCh: clusterVectorAggregatorsPipelineEventCh, - EventsCollector: evCollector, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "VectorPipeline") os.Exit(1) @@ -237,7 +234,6 @@ func main() { Scheme: mgr.GetScheme(), ConfigCheckTimeout: configCheckTimeout, EventChan: vectorAggregatorsEventCh, - EventsCollector: evCollector, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "VectorAggregator") os.Exit(1) @@ -252,7 +248,6 @@ func main() { Scheme: mgr.GetScheme(), ConfigCheckTimeout: configCheckTimeout, EventChan: clusterVectorAggregatorsEventCh, - EventsCollector: evCollector, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "ClusterVectorAggregator") os.Exit(1) diff --git a/config/crd/bases/observability.kaasops.io_clustervectoraggregators.yaml b/config/crd/bases/observability.kaasops.io_clustervectoraggregators.yaml index 49463c7b..5b972dc0 100644 --- a/config/crd/bases/observability.kaasops.io_clustervectoraggregators.yaml +++ b/config/crd/bases/observability.kaasops.io_clustervectoraggregators.yaml @@ -2416,6 +2416,18 @@ spec: - name type: object type: array + eventCollector: + properties: + image: + type: string + imagePullPolicy: + description: PullPolicy describes a policy for if/when to pull + a container image + type: string + maxBatchSize: + format: int32 + type: integer + type: object host_aliases: description: |- HostAliases provides mapping between ip and hostnames, diff --git a/config/crd/bases/observability.kaasops.io_vectoraggregators.yaml b/config/crd/bases/observability.kaasops.io_vectoraggregators.yaml index f3d754d8..376ca231 100644 --- a/config/crd/bases/observability.kaasops.io_vectoraggregators.yaml +++ b/config/crd/bases/observability.kaasops.io_vectoraggregators.yaml @@ -2414,6 +2414,18 @@ spec: - name type: object type: array + eventCollector: + properties: + image: + type: string + imagePullPolicy: + description: PullPolicy describes a policy for if/when to pull + a container image + type: string + maxBatchSize: + format: int32 + type: integer + type: object host_aliases: description: |- HostAliases provides mapping between ip and hostnames, diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index cd2708b7..e8323367 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -1,8 +1,8 @@ resources: -- manager.yaml + - manager.yaml apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: -- name: controller - newName: example.com/vector-operator - newTag: v0.0.1 + - name: controller + newName: example.com/vector-operator + newTag: v0.0.1 \ No newline at end of file diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index b682bbad..782c44b5 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -7,15 +7,7 @@ rules: - apiGroups: - "" resources: - - events - - namespaces - - nodes - verbs: - - list - - watch -- apiGroups: - - "" - resources: + - configmaps - pods - secrets - serviceaccounts @@ -28,6 +20,15 @@ rules: - patch - update - watch +- apiGroups: + - "" + resources: + - events + - namespaces + - nodes + verbs: + - list + - watch - apiGroups: - "" resources: diff --git a/event_collector.Dockerfile b/event_collector.Dockerfile new file mode 100644 index 00000000..5ff5b747 --- /dev/null +++ b/event_collector.Dockerfile @@ -0,0 +1,37 @@ +# Build the manager binary +FROM golang:1.22 AS builder +ARG TARGETOS +ARG TARGETARCH +ARG VERSION="dev" + +WORKDIR /workspace +# Copy the Go Modules manifests +COPY go.mod go.mod +COPY go.sum go.sum +# cache deps before building and copying source so that we don't need to re-download as much +# and so that source changes don't invalidate our downloaded layer +RUN go mod download + +# Copy the go source +COPY cmd/event_collector/main.go cmd/main.go +COPY api/ api/ +COPY internal/ internal/ + +# Build +# the GOARCH has not a default value to allow the binary be built according to the host where the command +# was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO +# the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore, +# by leaving it empty we can ensure that the container and binary shipped on it will have the same platform. +RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build \ + -ldflags="-X github.com/kaasops/vector-operator/internal/buildinfo.Version=${VERSION}" \ + -a -o collector cmd/main.go + +# Use distroless as minimal base image to package the manager binary +# Refer to https://github.com/GoogleContainerTools/distroless for more details +FROM gcr.io/distroless/static:nonroot +WORKDIR / +COPY --from=builder /workspace/collector . +USER 65532:65532 +EXPOSE 8080 + +ENTRYPOINT ["/collector"] diff --git a/go.mod b/go.mod index cb759173..2e47d2db 100644 --- a/go.mod +++ b/go.mod @@ -4,11 +4,14 @@ go 1.22.0 require ( github.com/VictoriaMetrics/operator/api v0.0.0-20240816142248-64879fb1dd26 + github.com/fsnotify/fsnotify v1.7.0 github.com/go-logr/logr v1.4.2 github.com/mitchellh/mapstructure v1.5.0 github.com/onsi/ginkgo/v2 v2.19.0 github.com/onsi/gomega v1.33.1 github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.60.1 + github.com/prometheus/client_golang v1.19.1 + github.com/spf13/viper v1.19.0 github.com/stoewer/go-strcase v1.2.0 github.com/stretchr/testify v1.9.0 golang.org/x/sync v0.7.0 @@ -39,7 +42,6 @@ require ( github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect @@ -59,6 +61,7 @@ require ( github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af // indirect github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect @@ -66,21 +69,28 @@ require ( github.com/jpillora/backoff v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.17.8 // indirect + github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/alertmanager v0.27.0 // indirect - github.com/prometheus/client_golang v1.19.1 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/common/sigv4 v0.1.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect github.com/spf13/cobra v1.8.1 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/subosito/gotenv v1.6.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fastjson v1.6.4 // indirect github.com/valyala/fastrand v1.1.0 // indirect @@ -112,6 +122,7 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.31.0 // indirect k8s.io/apiserver v0.31.0 // indirect diff --git a/go.sum b/go.sum index 63bc6271..f749aa74 100644 --- a/go.sum +++ b/go.sum @@ -93,6 +93,8 @@ github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0 github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= @@ -200,6 +202,8 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1 github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= @@ -238,6 +242,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -259,6 +265,8 @@ github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -300,24 +308,44 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.30.0/go.mod h1:2rsYD01CKFrjjsvFxx75KlEUNpWNBY9JWD3K/7o2Cus= @@ -661,6 +689,8 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSP gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/helm/charts/vector-operator/templates/clusterrole.yaml b/helm/charts/vector-operator/templates/clusterrole.yaml index 09102c1f..b038c7b7 100644 --- a/helm/charts/vector-operator/templates/clusterrole.yaml +++ b/helm/charts/vector-operator/templates/clusterrole.yaml @@ -18,6 +18,7 @@ rules: - "" resources: - secrets + - configmaps - pods - pods/log - serviceaccounts diff --git a/internal/buildinfo/info.go b/internal/buildinfo/info.go new file mode 100644 index 00000000..77ffbb7f --- /dev/null +++ b/internal/buildinfo/info.go @@ -0,0 +1,3 @@ +package buildinfo + +var Version string diff --git a/internal/common/annotations.go b/internal/common/annotations.go index a8ac618d..32cedac3 100644 --- a/internal/common/annotations.go +++ b/internal/common/annotations.go @@ -1,7 +1,5 @@ package common const ( - AnnotationServiceName = "observability.kaasops.io/service-name" - AnnotationK8sEventsNamespace = "observability.kaasops.io/k8s-events-namespace" - AnnotationK8sEventsPort = "observability.kaasops.io/k8s-events-port" + AnnotationServiceName = "observability.kaasops.io/service-name" ) diff --git a/internal/config/aggregator.go b/internal/config/aggregator.go index 98aba528..ec469162 100644 --- a/internal/config/aggregator.go +++ b/internal/config/aggregator.go @@ -3,7 +3,9 @@ package config import ( "errors" "fmt" + "github.com/kaasops/vector-operator/internal/common" "github.com/kaasops/vector-operator/internal/pipeline" + "github.com/stoewer/go-strcase" corev1 "k8s.io/api/core/v1" "net" "strconv" @@ -52,6 +54,7 @@ func BuildAggregatorConfig(params VectorConfigParams, pipelines ...pipeline.Pipe Namespace: pipeline.GetNamespace(), SourceName: k, PipelineName: pipeline.GetName(), + ServiceName: getServiceName(pipeline.GetAnnotations()[common.AnnotationServiceName], params.AggregatorName, pipeline.GetName()), }) if err != nil { return nil, err @@ -74,6 +77,7 @@ func BuildAggregatorConfig(params VectorConfigParams, pipelines ...pipeline.Pipe Namespace: pipeline.GetNamespace(), SourceName: k, PipelineName: pipeline.GetName(), + ServiceName: getServiceName(pipeline.GetAnnotations()[common.AnnotationServiceName], params.AggregatorName, pipeline.GetName()), }) if err != nil { return nil, err @@ -115,6 +119,7 @@ func BuildAggregatorConfig(params VectorConfigParams, pipelines ...pipeline.Pipe Namespace: DefaultNamespace, SourceName: DefaultInternalMetricsSourceName, PipelineName: DefaultPipelineName, + ServiceName: getServiceName("", params.AggregatorName, DefaultPipelineName), }) if err != nil { return nil, err @@ -144,3 +149,10 @@ func extractProtocol(opts map[string]any) corev1.Protocol { } return protocol } + +func getServiceName(nameFromAnnotations, aggregatorName, pipelineName string) string { + if nameFromAnnotations != "" { + return nameFromAnnotations + } + return strcase.KebabCase(fmt.Sprintf("%s-aggregator-%s", aggregatorName, pipelineName)) +} diff --git a/internal/config/config.go b/internal/config/config.go index eacfaed2..7bab11e2 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -21,6 +21,7 @@ import ( "errors" "fmt" vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" + "github.com/kaasops/vector-operator/internal/evcollector" "github.com/kaasops/vector-operator/internal/vector/vectoragent" "github.com/mitchellh/mapstructure" "gopkg.in/yaml.v2" @@ -35,6 +36,7 @@ var ( ) type VectorConfigParams struct { + AggregatorName string ApiEnabled bool PlaygroundEnabled bool UseApiServerCache bool @@ -151,3 +153,23 @@ func (c *VectorConfig) GetSourcesServicePorts() map[SPGroup][]*ServicePort { } return m } + +func (c *VectorConfig) GetEventCollectorConfig(namespace string) *evcollector.Config { + items := make([]*evcollector.ReceiverParams, 0) + for _, s := range c.internal.servicePort { + if s.IsKubernetesEvents { + items = append(items, &evcollector.ReceiverParams{ + ServiceNamespace: namespace, + ServiceName: s.ServiceName, + WatchedNamespace: s.Namespace, + Port: strconv.Itoa(int(s.Port)), + }) + } + } + if len(items) == 0 { + return nil + } + return &evcollector.Config{ + Receivers: items, + } +} diff --git a/internal/controller/clustervectoraggregator_controller.go b/internal/controller/clustervectoraggregator_controller.go index f949551b..e83231a6 100644 --- a/internal/controller/clustervectoraggregator_controller.go +++ b/internal/controller/clustervectoraggregator_controller.go @@ -21,7 +21,6 @@ import ( "errors" "github.com/kaasops/vector-operator/internal/config" "github.com/kaasops/vector-operator/internal/config/configcheck" - "github.com/kaasops/vector-operator/internal/k8sevents" "github.com/kaasops/vector-operator/internal/pipeline" "github.com/kaasops/vector-operator/internal/utils/hash" "github.com/kaasops/vector-operator/internal/utils/k8s" @@ -49,7 +48,6 @@ type ClusterVectorAggregatorReconciler struct { client.Client Scheme *runtime.Scheme - EventsCollector *k8sevents.EventsCollector Clientset *kubernetes.Clientset ConfigCheckTimeout time.Duration EventChan chan event.GenericEvent @@ -61,6 +59,7 @@ type ClusterVectorAggregatorReconciler struct { // +kubebuilder:rbac:groups="",resources=serviceaccounts,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups="",resources=configmaps,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups="",resources=services,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups="",resources=pods,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups="",resources=pods/log,verbs=get;list @@ -87,7 +86,6 @@ func (r *ClusterVectorAggregatorReconciler) Reconcile(ctx context.Context, req c err := r.Get(ctx, req.NamespacedName, clusterAggregator) if err != nil { if api_errors.IsNotFound(err) { - r.EventsCollector.UnregisterByAggregatorID(req.String()) return ctrl.Result{}, nil } return ctrl.Result{}, err @@ -99,7 +97,7 @@ func (r *ClusterVectorAggregatorReconciler) Reconcile(ctx context.Context, req c func (r *ClusterVectorAggregatorReconciler) createOrUpdateVectorAggregator(ctx context.Context, client client.Client, clientset *kubernetes.Clientset, v *v1alpha1.ClusterVectorAggregator) (ctrl.Result, error) { log := log.FromContext(ctx).WithValues("VectorAggregator", v.Name) - vaCtrl := aggregator.NewController(v, client, clientset, r.EventsCollector) + vaCtrl := aggregator.NewController(v, client, clientset) if vaCtrl.Namespace == "" { if err := vaCtrl.SetFailedStatus(ctx, "spec.resourceNamespace is empty"); err != nil { return ctrl.Result{}, err @@ -117,6 +115,7 @@ func (r *ClusterVectorAggregatorReconciler) createOrUpdateVectorAggregator(ctx c } cfg, err := config.BuildAggregatorConfig(config.VectorConfigParams{ + AggregatorName: vaCtrl.Name, ApiEnabled: vaCtrl.Spec.Api.Enabled, PlaygroundEnabled: vaCtrl.Spec.Api.Playground, InternalMetrics: vaCtrl.Spec.InternalMetrics, @@ -187,6 +186,7 @@ func (r *ClusterVectorAggregatorReconciler) SetupWithManager(mgr ctrl.Manager) e Owns(&appsv1.Deployment{}). Owns(&corev1.Service{}). Owns(&corev1.Secret{}). + Owns(&corev1.ConfigMap{}). Owns(&corev1.ServiceAccount{}). Owns(&rbacv1.ClusterRole{}). Owns(&rbacv1.ClusterRoleBinding{}) diff --git a/internal/controller/clustervectoraggregator_controller_test.go b/internal/controller/clustervectoraggregator_controller_test.go index 1df6f6b5..75e9643f 100644 --- a/internal/controller/clustervectoraggregator_controller_test.go +++ b/internal/controller/clustervectoraggregator_controller_test.go @@ -72,7 +72,6 @@ var _ = Describe("ClusterVectorAggregator Controller", func() { controllerReconciler := &ClusterVectorAggregatorReconciler{ Client: k8sClient, Scheme: k8sClient.Scheme(), - EventsCollector: k8sEventsCollector, EventChan: make(chan event.GenericEvent, 1), ConfigCheckTimeout: configCheckTimeout, Clientset: clientset, diff --git a/internal/controller/pipeline_controller.go b/internal/controller/pipeline_controller.go index 9bf94437..eb481477 100644 --- a/internal/controller/pipeline_controller.go +++ b/internal/controller/pipeline_controller.go @@ -21,7 +21,6 @@ import ( "errors" "fmt" "github.com/kaasops/vector-operator/internal/config/configcheck" - "github.com/kaasops/vector-operator/internal/k8sevents" "github.com/kaasops/vector-operator/internal/vector/aggregator" "github.com/kaasops/vector-operator/internal/vector/vectoragent" "golang.org/x/sync/errgroup" @@ -50,7 +49,6 @@ type PipelineReconciler struct { VectorAgentEventCh chan event.GenericEvent VectorAggregatorsEventCh chan event.GenericEvent ClusterVectorAggregatorsEventCh chan event.GenericEvent - EventsCollector *k8sevents.EventsCollector } var ( @@ -171,8 +169,9 @@ func (r *PipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c if pipelineCR.GetNamespace() != "" { for _, vector := range vectorAggregators { eg.Go(func() error { - vaCtrl := aggregator.NewController(vector, r.Client, r.Clientset, r.EventsCollector) + vaCtrl := aggregator.NewController(vector, r.Client, r.Clientset) cfg, err := config.BuildAggregatorConfig(config.VectorConfigParams{ + AggregatorName: vaCtrl.Name, ApiEnabled: vaCtrl.Spec.Api.Enabled, PlaygroundEnabled: vaCtrl.Spec.Api.Playground, InternalMetrics: vaCtrl.Spec.InternalMetrics, @@ -215,8 +214,9 @@ func (r *PipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c for _, vector := range clusterVectorAggregators { eg.Go(func() error { - vaCtrl := aggregator.NewController(vector, r.Client, r.Clientset, r.EventsCollector) + vaCtrl := aggregator.NewController(vector, r.Client, r.Clientset) cfg, err := config.BuildAggregatorConfig(config.VectorConfigParams{ + AggregatorName: vaCtrl.Name, ApiEnabled: vaCtrl.Spec.Api.Enabled, PlaygroundEnabled: vaCtrl.Spec.Api.Playground, InternalMetrics: vaCtrl.Spec.InternalMetrics, diff --git a/internal/controller/pipeline_controller_test.go b/internal/controller/pipeline_controller_test.go index 022cb74e..d0e58d36 100644 --- a/internal/controller/pipeline_controller_test.go +++ b/internal/controller/pipeline_controller_test.go @@ -75,7 +75,6 @@ var _ = Describe("VectorPipeline Controller", func() { Clientset: clientset, ConfigCheckTimeout: configCheckTimeout, VectorAgentEventCh: make(chan event.GenericEvent, 1), - EventsCollector: k8sEventsCollector, } _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ diff --git a/internal/controller/suite_test.go b/internal/controller/suite_test.go index 3d9e8d48..bb5fd174 100644 --- a/internal/controller/suite_test.go +++ b/internal/controller/suite_test.go @@ -19,7 +19,6 @@ package controller import ( "context" "fmt" - "github.com/kaasops/vector-operator/internal/k8sevents" "path/filepath" "runtime" "testing" @@ -53,7 +52,6 @@ var ctx context.Context var cancel context.CancelFunc var clientset *kubernetes.Clientset var configCheckTimeout time.Duration -var k8sEventsCollector *k8sevents.EventsCollector func TestControllers(t *testing.T) { RegisterFailHandler(Fail) @@ -104,8 +102,6 @@ var _ = BeforeSuite(func() { clientset, err = kubernetes.NewForConfig(cfg) Expect(err).NotTo(HaveOccurred()) Expect(clientset).NotTo(BeNil()) - - k8sEventsCollector = k8sevents.NewEventsCollector(clientset, &FakeLogger{}) }) var _ = AfterSuite(func() { @@ -114,8 +110,3 @@ var _ = AfterSuite(func() { err := testEnv.Stop() Expect(err).NotTo(HaveOccurred()) }) - -type FakeLogger struct{} - -func (l *FakeLogger) Info(msg string, keysAndValues ...any) {} -func (l *FakeLogger) Error(err error, msg string, keysAndValues ...any) {} diff --git a/internal/controller/vectoraggregator_controller.go b/internal/controller/vectoraggregator_controller.go index 20750fb4..50417b0d 100644 --- a/internal/controller/vectoraggregator_controller.go +++ b/internal/controller/vectoraggregator_controller.go @@ -21,7 +21,6 @@ import ( "errors" "github.com/kaasops/vector-operator/internal/config" "github.com/kaasops/vector-operator/internal/config/configcheck" - "github.com/kaasops/vector-operator/internal/k8sevents" "github.com/kaasops/vector-operator/internal/pipeline" "github.com/kaasops/vector-operator/internal/utils/hash" "github.com/kaasops/vector-operator/internal/utils/k8s" @@ -55,7 +54,6 @@ type VectorAggregatorReconciler struct { client.Client Scheme *runtime.Scheme - EventsCollector *k8sevents.EventsCollector Clientset *kubernetes.Clientset ConfigCheckTimeout time.Duration EventChan chan event.GenericEvent @@ -113,7 +111,6 @@ func (r *VectorAggregatorReconciler) Reconcile(ctx context.Context, req ctrl.Req setAggregatorTypeMetaIfNeeded(vectorCR) if vectorCR.IsBeingDeleted() { - r.EventsCollector.UnregisterByAggregatorID(req.String()) if controllerutil.ContainsFinalizer(vectorCR, aggregatorFinalizerName) { if err := r.deleteVectorAggregator(ctx, vectorCR); err != nil { if !api_errors.IsNotFound(err) { @@ -152,6 +149,7 @@ func (r *VectorAggregatorReconciler) SetupWithManager(mgr ctrl.Manager) error { Owns(&appsv1.Deployment{}). Owns(&corev1.Service{}). Owns(&corev1.Secret{}). + Owns(&corev1.ConfigMap{}). Owns(&corev1.ServiceAccount{}). Owns(&rbacv1.ClusterRole{}). Owns(&rbacv1.ClusterRoleBinding{}) @@ -181,7 +179,7 @@ func listVectorAggregators(ctx context.Context, client client.Client) (vectors [ func (r *VectorAggregatorReconciler) createOrUpdateVectorAggregator(ctx context.Context, client client.Client, clientset *kubernetes.Clientset, v *v1alpha1.VectorAggregator) (ctrl.Result, error) { log := log.FromContext(ctx).WithValues("VectorAggregator", v.Name) - vaCtrl := aggregator.NewController(v, client, clientset, r.EventsCollector) + vaCtrl := aggregator.NewController(v, client, clientset) pipelines, err := pipeline.GetValidPipelines(ctx, vaCtrl.Client, pipeline.FilterPipelines{ Scope: pipeline.NamespacedPipeline, @@ -194,6 +192,7 @@ func (r *VectorAggregatorReconciler) createOrUpdateVectorAggregator(ctx context. } cfg, err := config.BuildAggregatorConfig(config.VectorConfigParams{ + AggregatorName: vaCtrl.Name, ApiEnabled: vaCtrl.Spec.Api.Enabled, PlaygroundEnabled: vaCtrl.Spec.Api.Playground, InternalMetrics: vaCtrl.Spec.InternalMetrics, @@ -268,7 +267,7 @@ func (r *VectorAggregatorReconciler) reconcileVectorAggregators(ctx context.Cont } func (r *VectorAggregatorReconciler) deleteVectorAggregator(ctx context.Context, v *v1alpha1.VectorAggregator) error { - return aggregator.NewController(v, r.Client, r.Clientset, r.EventsCollector).DeleteVectorAggregator(ctx) + return aggregator.NewController(v, r.Client, r.Clientset).DeleteVectorAggregator(ctx) } func setAggregatorTypeMetaIfNeeded(cr *v1alpha1.VectorAggregator) { diff --git a/internal/controller/vectoraggregator_controller_test.go b/internal/controller/vectoraggregator_controller_test.go index 0df94d92..d65b3f0e 100644 --- a/internal/controller/vectoraggregator_controller_test.go +++ b/internal/controller/vectoraggregator_controller_test.go @@ -73,7 +73,6 @@ var _ = Describe("VectorAggregator Controller", func() { Clientset: clientset, ConfigCheckTimeout: time.Second * 10, EventChan: make(chan event.GenericEvent, 1), - EventsCollector: k8sEventsCollector, } // remove finalizer _, err = controllerReconciler.Reconcile(ctx, reconcile.Request{ @@ -89,7 +88,6 @@ var _ = Describe("VectorAggregator Controller", func() { Clientset: clientset, ConfigCheckTimeout: time.Second * 10, EventChan: make(chan event.GenericEvent, 1), - EventsCollector: k8sEventsCollector, } _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ NamespacedName: typeNamespacedName, diff --git a/internal/evcollector/collector.go b/internal/evcollector/collector.go new file mode 100644 index 00000000..bb503a7c --- /dev/null +++ b/internal/evcollector/collector.go @@ -0,0 +1,171 @@ +package evcollector + +import ( + "context" + "github.com/kaasops/vector-operator/internal/vector/gen" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/fields" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/cache" + "time" +) + +type Logger interface { + Info(msg string, keysAndValues ...any) + Error(msg string, keysAndValues ...any) + Debug(msg string, keysAndValues ...any) +} + +type Collector struct { + Addr string + Namespace string + createdAt time.Time + stopCh chan struct{} + logger Logger + client rest.Interface + maxBatchSize int +} + +func New(addr, namespace string, maxBatchSize int32, logger Logger, client rest.Interface) *Collector { + c := Collector{ + Addr: addr, + createdAt: time.Now(), + logger: logger, + Namespace: namespace, + client: client, + maxBatchSize: int(maxBatchSize), + } + return &c +} + +func (c *Collector) Start() { + if c.stopCh != nil { + return + } + + c.stopCh = make(chan struct{}) + eventsCh := make(chan *corev1.Event) + + watchList := cache.NewListWatchFromClient(c.client, "events", c.Namespace, fields.Everything()) + _, ctrl := cache.NewInformerWithOptions(cache.InformerOptions{ + ListerWatcher: watchList, + ObjectType: &corev1.Event{}, + ResyncPeriod: 0, + Handler: cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj any) { + event := obj.(*corev1.Event) + eventsCh <- event + }, + UpdateFunc: func(_, obj interface{}) { + event := obj.(*corev1.Event) + eventsCh <- event + }, + }, + }) + go func() { + var conn *grpc.ClientConn + var vectorClient gen.VectorClient + var err error + var sending bool + var sentBatchCount int + + batch := make([]*corev1.Event, 0, c.maxBatchSize) + + ticker := time.NewTicker(1 * time.Second) + defer ticker.Stop() + + for { + select { + case <-c.stopCh: + if conn != nil { + _ = conn.Close() + } + return + + default: + if !sending { + select { + case event := <-eventsCh: + if eventTimestamp(event).Before(c.createdAt) || event == nil { + eventsSkipped.WithLabelValues(c.Addr, c.Namespace).Inc() + continue + } + eventsHandled.WithLabelValues(c.Addr, c.Namespace).Inc() + batch = append(batch, event) + if len(batch) == c.maxBatchSize { + sending = true + } else { + continue + } + case <-ticker.C: + if len(batch) > 0 { + sending = true + } else { + continue + } + case <-c.stopCh: + if conn != nil { + _ = conn.Close() + } + return + } + } + + if conn == nil { + for { + conn, err = grpc.NewClient(c.Addr, + grpc.WithTransportCredentials(insecure.NewCredentials()), + ) + if err != nil { + c.logger.Error("connect to address", "address", c.Addr, "error", err) + time.Sleep(5 * time.Second) + continue + } + vectorClient = gen.NewVectorClient(conn) + break + } + } + + _, err = vectorClient.PushEvents(context.Background(), k8sEventsToVectorEvents(batch)) + if err != nil { + c.logger.Error("send event", "address", c.Addr, "error", err) + _ = conn.Close() + conn = nil + time.Sleep(5 * time.Second) + continue + } + sentBatchCount++ + eventsProcessed.WithLabelValues(c.Addr, c.Namespace).Add(float64(len(batch))) + c.logger.Debug("batch sent", + "address", c.Addr, + "namespace", c.Namespace, + "count", len(batch), + "batch", sentBatchCount, + ) + batch = batch[:0] + sending = false + } + } + + }() + go ctrl.Run(c.stopCh) +} + +func (c *Collector) Stop() { + close(c.stopCh) +} + +func eventTimestamp(ev *corev1.Event) time.Time { + var ts time.Time + switch { + case ev.EventTime.Time != time.Time{}: + ts = ev.EventTime.Time + case ev.LastTimestamp.Time != time.Time{}: + ts = ev.LastTimestamp.Time + case ev.FirstTimestamp.Time != time.Time{}: + ts = ev.FirstTimestamp.Time + } + return ts +} diff --git a/internal/evcollector/config.go b/internal/evcollector/config.go new file mode 100644 index 00000000..bb20b5c9 --- /dev/null +++ b/internal/evcollector/config.go @@ -0,0 +1,13 @@ +package evcollector + +type ReceiverParams struct { + ServiceName string + ServiceNamespace string + Port string + WatchedNamespace string +} + +type Config struct { + MaxBatchSize int32 + Receivers []*ReceiverParams +} diff --git a/internal/k8sevents/event.go b/internal/evcollector/event.go similarity index 85% rename from internal/k8sevents/event.go rename to internal/evcollector/event.go index 284e7721..3ccfa258 100644 --- a/internal/k8sevents/event.go +++ b/internal/evcollector/event.go @@ -1,4 +1,4 @@ -package k8sevents +package evcollector import ( "github.com/kaasops/vector-operator/internal/vector/gen" @@ -48,6 +48,20 @@ func k8sEventToVectorLog(ev *corev1.Event) *gen.Log { } } +func k8sEventsToVectorEvents(list []*corev1.Event) *gen.PushEventsRequest { + events := make([]*gen.EventWrapper, 0, len(list)) + for _, event := range list { + events = append(events, &gen.EventWrapper{ + Event: &gen.EventWrapper_Log{ + Log: k8sEventToVectorLog(event), + }, + }) + } + return &gen.PushEventsRequest{ + Events: events, + } +} + func valueFromString(s string) *gen.Value { return &gen.Value{ Kind: &gen.Value_RawBytes{RawBytes: []byte(s)}, diff --git a/internal/evcollector/metrics.go b/internal/evcollector/metrics.go new file mode 100644 index 00000000..da61cd52 --- /dev/null +++ b/internal/evcollector/metrics.go @@ -0,0 +1,24 @@ +package evcollector + +import ( + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" +) + +var ( + eventsHandled = promauto.NewCounterVec(prometheus.CounterOpts{ + Namespace: "event_collector", + Name: "handled_events_total", + Help: "The total number of handled events", + }, []string{"service", "namespace"}) + eventsSkipped = promauto.NewCounterVec(prometheus.CounterOpts{ + Namespace: "event_collector", + Name: "skipped_events_total", + Help: "The total number of skipped events", + }, []string{"service", "namespace"}) + eventsProcessed = promauto.NewCounterVec(prometheus.CounterOpts{ + Namespace: "event_collector", + Name: "processed_events_total", + Help: "The total number of processed events", + }, []string{"service", "namespace"}) +) diff --git a/internal/k8sevents/manager.go b/internal/k8sevents/manager.go deleted file mode 100644 index 0f8f037e..00000000 --- a/internal/k8sevents/manager.go +++ /dev/null @@ -1,84 +0,0 @@ -package k8sevents - -import ( - "fmt" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" - "net" - "sync" -) - -type Logger interface { - Info(msg string, keysAndValues ...any) - Error(err error, msg string, keysAndValues ...any) -} - -type EventsCollector struct { - client rest.Interface - logger Logger - mx sync.Mutex - mp map[string]map[string]*watcher -} - -func NewEventsCollector(clientset *kubernetes.Clientset, logger Logger) *EventsCollector { - return &EventsCollector{ - mp: make(map[string]map[string]*watcher), - client: clientset.CoreV1().RESTClient(), - logger: logger, - } -} - -func (m *EventsCollector) RegisterSubscriber(aggregatorID, svcName, svcNamespace, port, namespace string) { - host := fmt.Sprintf("%s.%s", svcName, svcNamespace) - addr := net.JoinHostPort(host, port) - c := newWatcher(addr, namespace, m.logger) - - m.mx.Lock() - - if group, ok := m.mp[aggregatorID]; ok { - - if oldC, ok := group[host]; ok { - if oldC.addr == addr && oldC.namespace == namespace { - m.mx.Unlock() - return - } - oldC.close() - } - - group[host] = c - - } else { - group = make(map[string]*watcher) - group[host] = c - m.mp[aggregatorID] = group - } - - m.mx.Unlock() - - c.watchEvents(m.client) -} - -func (m *EventsCollector) UnregisterSubscriber(aggregatorID, svcName, svcNamespace string) { - host := fmt.Sprintf("%s.%s", svcName, svcNamespace) - m.mx.Lock() - defer m.mx.Unlock() - - if group, ok := m.mp[aggregatorID]; ok { - if v, ok := group[host]; ok { - v.close() - delete(m.mp, host) - } - } -} - -func (m *EventsCollector) UnregisterByAggregatorID(aggregatorID string) { - m.mx.Lock() - defer m.mx.Unlock() - if group, ok := m.mp[aggregatorID]; ok { - for host, w := range group { - w.close() - delete(m.mp, host) - } - delete(m.mp, aggregatorID) - } -} diff --git a/internal/k8sevents/watcher.go b/internal/k8sevents/watcher.go deleted file mode 100644 index 47549ff2..00000000 --- a/internal/k8sevents/watcher.go +++ /dev/null @@ -1,141 +0,0 @@ -package k8sevents - -import ( - "context" - "github.com/kaasops/vector-operator/internal/vector/gen" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/fields" - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/cache" - "time" -) - -type watcher struct { - addr string - namespace string - createdAt time.Time - stopCh chan struct{} - logger Logger -} - -func newWatcher(addr, namespace string, logger Logger) *watcher { - r := watcher{ - addr: addr, - createdAt: time.Now(), - logger: logger, - namespace: namespace, - } - return &r -} - -func (w *watcher) watchEvents(client rest.Interface) { - if w.stopCh != nil { - return - } - - w.stopCh = make(chan struct{}) - eventsCh := make(chan *corev1.Event) - - watchList := cache.NewListWatchFromClient(client, "events", w.namespace, fields.Everything()) - _, ctrl := cache.NewInformerWithOptions(cache.InformerOptions{ - ListerWatcher: watchList, - ObjectType: &corev1.Event{}, - ResyncPeriod: 0, - Handler: cache.ResourceEventHandlerFuncs{ - AddFunc: func(obj any) { - event := obj.(*corev1.Event) - eventsCh <- event - }, - UpdateFunc: func(_, obj interface{}) { - event := obj.(*corev1.Event) - eventsCh <- event - }, - }, - }) - go func() { - var conn *grpc.ClientConn - var vectorClient gen.VectorClient - var err error - var event *corev1.Event - var sending bool - - for { - select { - case <-w.stopCh: - if conn != nil { - _ = conn.Close() - } - return - - default: - if !sending { - select { - case event = <-eventsCh: - if eventTimestamp(event).Before(w.createdAt) || event == nil { - continue - } - sending = true - case <-w.stopCh: - if conn != nil { - _ = conn.Close() - } - return - } - } - - if conn == nil { - for { - conn, err = grpc.NewClient(w.addr, - grpc.WithTransportCredentials(insecure.NewCredentials()), - ) - if err != nil { - w.logger.Error(err, "connect to address", "address", w.addr) - time.Sleep(5 * time.Second) - continue - } - vectorClient = gen.NewVectorClient(conn) - break - } - } - - _, err = vectorClient.PushEvents(context.Background(), &gen.PushEventsRequest{ - Events: []*gen.EventWrapper{{ - Event: &gen.EventWrapper_Log{ - Log: k8sEventToVectorLog(event), - }, - }}, - }) - if err != nil { - w.logger.Error(err, "send event", "address", w.addr) - _ = conn.Close() - conn = nil - time.Sleep(5 * time.Second) - continue - } - - sending = false - } - } - - }() - go ctrl.Run(w.stopCh) -} - -func (w *watcher) close() { - close(w.stopCh) -} - -func eventTimestamp(ev *corev1.Event) time.Time { - var ts time.Time - switch { - case ev.EventTime.Time != time.Time{}: - ts = ev.EventTime.Time - case ev.LastTimestamp.Time != time.Time{}: - ts = ev.LastTimestamp.Time - case ev.FirstTimestamp.Time != time.Time{}: - ts = ev.FirstTimestamp.Time - } - return ts -} diff --git a/internal/utils/k8s/k8s.go b/internal/utils/k8s/k8s.go index 8eac9893..3eda1b22 100644 --- a/internal/utils/k8s/k8s.go +++ b/internal/utils/k8s/k8s.go @@ -54,6 +54,8 @@ func CreateOrUpdateResource(ctx context.Context, obj client.Object, c client.Cli return createOrUpdateDaemonSet(ctx, o, c) case *corev1.Secret: return createOrUpdateSecret(ctx, o, c) + case *corev1.ConfigMap: + return createOrUpdateConfigMap(ctx, o, c) case *corev1.Service: return createOrUpdateService(ctx, o, c) case *corev1.ServiceAccount: @@ -135,6 +137,22 @@ func createOrUpdateSecret(ctx context.Context, desired *corev1.Secret, c client. return nil } +func createOrUpdateConfigMap(ctx context.Context, desired *corev1.ConfigMap, c client.Client) error { + existing := desired.DeepCopy() + _, err := controllerutil.CreateOrUpdate(ctx, c, existing, func() error { + existing.Labels = desired.Labels + existing.Annotations = mergeMaps(desired.Annotations, existing.Annotations) + existing.OwnerReferences = desired.OwnerReferences + existing.Data = desired.Data + return nil + }) + if err != nil { + return fmt.Errorf("failed to create or update ConfigMap: %w", err) + } + existing.DeepCopyInto(desired) + return nil +} + func createOrUpdateService(ctx context.Context, desired *corev1.Service, c client.Client) error { existing := desired.DeepCopy() _, err := controllerutil.CreateOrUpdate(ctx, c, existing, func() error { diff --git a/internal/vector/aggregator/controller.go b/internal/vector/aggregator/controller.go index f7db4be1..50db1b3a 100644 --- a/internal/vector/aggregator/controller.go +++ b/internal/vector/aggregator/controller.go @@ -3,8 +3,8 @@ package aggregator import ( "context" vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" + "github.com/kaasops/vector-operator/internal/buildinfo" "github.com/kaasops/vector-operator/internal/config" - "github.com/kaasops/vector-operator/internal/k8sevents" "github.com/kaasops/vector-operator/internal/utils/k8s" monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" corev1 "k8s.io/api/core/v1" @@ -35,7 +35,6 @@ type Controller struct { ConfigBytes []byte Config *config.VectorConfig ClientSet *kubernetes.Clientset - EventsCollector *k8sevents.EventsCollector isClusterAggregator bool } @@ -43,13 +42,11 @@ func NewController( v Aggregator, c client.Client, cs *kubernetes.Clientset, - evCollector *k8sevents.EventsCollector, ) *Controller { ctrl := &Controller{ Client: c, VectorAggregator: v, ClientSet: cs, - EventsCollector: evCollector, } switch agg := v.(type) { @@ -107,6 +104,11 @@ func (ctrl *Controller) EnsureVectorAggregator(ctx context.Context) error { if err := ctrl.ensureVectorAggregatorDeployment(ctx); err != nil { return err } + + if err := ctrl.ensureEventCollector(ctx); err != nil { + return err + } + return nil } @@ -239,6 +241,15 @@ func (ctrl *Controller) setDefault() { corev1.ResourceCPU: resourcev1.MustParse("1000m"), } } + if ctrl.Spec.EventCollector.Image == "" { + ctrl.Spec.EventCollector.Image = "kaasops/event-collector:" + buildinfo.Version + } + if ctrl.Spec.EventCollector.ImagePullPolicy == "" { + ctrl.Spec.EventCollector.ImagePullPolicy = corev1.PullIfNotPresent + } + if ctrl.Spec.EventCollector.MaxBatchSize <= 0 { + ctrl.Spec.EventCollector.MaxBatchSize = 250 + } } func (ctrl *Controller) SetSuccessStatus(ctx context.Context, hash *uint32) error { diff --git a/internal/vector/aggregator/event_collector.go b/internal/vector/aggregator/event_collector.go new file mode 100644 index 00000000..edd87da8 --- /dev/null +++ b/internal/vector/aggregator/event_collector.go @@ -0,0 +1,279 @@ +package aggregator + +import ( + "context" + "encoding/json" + "github.com/kaasops/vector-operator/internal/evcollector" + "github.com/kaasops/vector-operator/internal/utils/k8s" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + api_errors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/utils/ptr" + "sigs.k8s.io/controller-runtime/pkg/log" +) + +func (ctrl *Controller) ensureEventCollector(ctx context.Context) error { + cfg := ctrl.Config.GetEventCollectorConfig(ctrl.Namespace) + if cfg == nil { + return ctrl.cleanupEventCollector(ctx) + } + cfg.MaxBatchSize = ctrl.Spec.EventCollector.MaxBatchSize + + // config + eventCollectorConfig, err := ctrl.createEventCollectorConfig(cfg) + if err != nil { + return err + } + + err = k8s.CreateOrUpdateResource(ctx, eventCollectorConfig, ctrl.Client) + if err != nil { + return err + } + + // rbac + if err := ctrl.ensureEventCollectorRBAC(ctx); err != nil { + return err + } + + // service + if err := k8s.CreateOrUpdateResource(ctx, ctrl.createEventCollectorService(), ctrl.Client); err != nil { + return err + } + + // deployment + if err := k8s.CreateOrUpdateResource(ctx, ctrl.createEventCollectorDeployment(), ctrl.Client); err != nil { + return err + } + + return nil +} + +func (ctrl *Controller) cleanupEventCollector(ctx context.Context) error { + if err := ctrl.Delete(ctx, ctrl.createEventCollectorDeployment()); err != nil && !api_errors.IsNotFound(err) { + return err + } + if err := ctrl.Delete(ctx, ctrl.createEventCollectorService()); err != nil && !api_errors.IsNotFound(err) { + return err + } + if err := ctrl.Delete(ctx, ctrl.createEventCollectorClusterRoleBinding()); err != nil && !api_errors.IsNotFound(err) { + return err + } + if err := ctrl.Delete(ctx, ctrl.createEventCollectorClusterRole()); err != nil && !api_errors.IsNotFound(err) { + return err + } + if err := ctrl.Delete(ctx, ctrl.createEventCollectorServiceAccount()); err != nil && !api_errors.IsNotFound(err) { + return err + } + cfg, err := ctrl.createEventCollectorConfig(nil) + if err != nil { + return err + } + if err := ctrl.Delete(ctx, cfg); err != nil && !api_errors.IsNotFound(err) { + return err + } + return nil +} + +func (ctrl *Controller) createEventCollectorService() *corev1.Service { + labels := ctrl.labelsForEventCollector() + annotations := ctrl.annotationsForVectorAggregator() + svc := &corev1.Service{ + ObjectMeta: ctrl.objectMetaVectorAggregator(labels, annotations, ctrl.Namespace), + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: "metrics", + Protocol: corev1.ProtocolTCP, + Port: 8080, + TargetPort: intstr.FromInt32(8080), + }, + }, + Selector: labels, + }, + } + svc.ObjectMeta.Name = ctrl.Name + "-event-collector" + return svc +} + +func (ctrl *Controller) createEventCollectorConfig(params *evcollector.Config) (*corev1.ConfigMap, error) { + labels := ctrl.labelsForEventCollector() + annotations := ctrl.annotationsForVectorAggregator() + bytes, err := json.Marshal(params) + if err != nil { + return nil, err + } + config := map[string]string{ + "config.json": string(bytes), + } + cfg := &corev1.ConfigMap{ + ObjectMeta: ctrl.objectMetaVectorAggregator(labels, annotations, ctrl.Namespace), + Data: config, + } + cfg.ObjectMeta.Name = ctrl.Name + "-event-collector" + return cfg, nil +} + +func (ctrl *Controller) createEventCollectorDeployment() *appsv1.Deployment { + labels := ctrl.labelsForEventCollector() + annotations := ctrl.annotationsForVectorAggregator() + if annotations == nil { + annotations = map[string]string{} + } + annotations["prometheus.io/scrape"] = "true" + annotations["prometheus.io/port"] = "8080" + containers := []corev1.Container{*ctrl.eventCollectorContainer()} + + deployment := &appsv1.Deployment{ + ObjectMeta: ctrl.objectMetaVectorAggregator(labels, annotations, ctrl.Namespace), + Spec: appsv1.DeploymentSpec{ + Selector: &metav1.LabelSelector{MatchLabels: labels}, + Replicas: ptr.To[int32](1), + Template: corev1.PodTemplateSpec{ + ObjectMeta: ctrl.objectMetaVectorAggregator(labels, annotations, ctrl.Namespace), + Spec: corev1.PodSpec{ + ServiceAccountName: ctrl.Name + "-event-collector", + Volumes: ctrl.generateEventCollectorVolume(), + SecurityContext: ctrl.Spec.SecurityContext, + ImagePullSecrets: ctrl.Spec.ImagePullSecrets, + Affinity: ctrl.Spec.Affinity, + RuntimeClassName: ctrl.Spec.RuntimeClassName, + SchedulerName: ctrl.Spec.SchedulerName, + Tolerations: ctrl.Spec.Tolerations, + PriorityClassName: ctrl.Spec.PodSecurityPolicyName, + HostNetwork: ctrl.Spec.HostNetwork, + HostAliases: ctrl.Spec.HostAliases, + Containers: containers, + }, + }, + }, + } + deployment.ObjectMeta.Name = ctrl.Name + "-event-collector" + return deployment +} + +func (ctrl *Controller) eventCollectorContainer() *corev1.Container { + return &corev1.Container{ + Name: "event-collector", + Image: ctrl.Spec.EventCollector.Image, + ImagePullPolicy: ctrl.Spec.EventCollector.ImagePullPolicy, + SecurityContext: ctrl.Spec.ContainerSecurityContext, + Args: []string{}, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "event-collector-config", + MountPath: "/etc/event-collector", + }, + }, + } +} + +func (ctrl *Controller) generateEventCollectorVolume() []corev1.Volume { + return append(ctrl.Spec.Volumes, corev1.Volume{ + Name: "event-collector-config", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: ctrl.Name + "-event-collector", + }, + }, + }, + }) +} + +// rbac + +func (ctrl *Controller) ensureEventCollectorRBAC(ctx context.Context) error { + log := log.FromContext(ctx).WithValues(ctrl.prefix()+"vector-aggregator-rbac", ctrl.Name) + + log.Info("start Reconcile Vector Aggregator RBAC") + + if err := ctrl.ensureEventCollectorServiceAccount(ctx); err != nil { + return err + } + if err := ctrl.ensureEventCollectorClusterRole(ctx); err != nil { + return err + } + if err := ctrl.ensureEventCollectorClusterRoleBinding(ctx); err != nil { + return err + } + return nil +} + +func (ctrl *Controller) ensureEventCollectorServiceAccount(ctx context.Context) error { + return k8s.CreateOrUpdateResource(ctx, ctrl.createEventCollectorServiceAccount(), ctrl.Client) +} + +func (ctrl *Controller) ensureEventCollectorClusterRole(ctx context.Context) error { + return k8s.CreateOrUpdateResource(ctx, ctrl.createEventCollectorClusterRole(), ctrl.Client) +} + +func (ctrl *Controller) ensureEventCollectorClusterRoleBinding(ctx context.Context) error { + return k8s.CreateOrUpdateResource(ctx, ctrl.createEventCollectorClusterRoleBinding(), ctrl.Client) +} + +func (ctrl *Controller) createEventCollectorClusterRole() *rbacv1.ClusterRole { + labels := ctrl.labelsForEventCollector() + annotations := ctrl.annotationsForVectorAggregator() + + clusterRole := &rbacv1.ClusterRole{ + ObjectMeta: ctrl.objectMetaVectorAggregator(labels, annotations, ""), + Rules: []rbacv1.PolicyRule{ + { + APIGroups: []string{""}, + Resources: []string{"events"}, + Verbs: []string{"list", "watch"}, + }, + }, + } + + clusterRole.ObjectMeta.Name = ctrl.Name + "-event-collector" + return clusterRole +} + +func (ctrl *Controller) createEventCollectorServiceAccount() *corev1.ServiceAccount { + labels := ctrl.labelsForEventCollector() + annotations := ctrl.annotationsForVectorAggregator() + + serviceAccount := &corev1.ServiceAccount{ + ObjectMeta: ctrl.objectMetaVectorAggregator(labels, annotations, ctrl.Namespace), + } + + serviceAccount.ObjectMeta.Name = ctrl.Name + "-event-collector" + + return serviceAccount +} + +func (ctrl *Controller) createEventCollectorClusterRoleBinding() *rbacv1.ClusterRoleBinding { + labels := ctrl.labelsForEventCollector() + annotations := ctrl.annotationsForVectorAggregator() + + clusterRoleBinding := &rbacv1.ClusterRoleBinding{ + ObjectMeta: ctrl.objectMetaVectorAggregator(labels, annotations, ""), + RoleRef: rbacv1.RoleRef{ + Kind: "ClusterRole", + APIGroup: "rbac.authorization.k8s.io", + Name: ctrl.Name + "-event-collector", + }, + Subjects: []rbacv1.Subject{ + { + Kind: "ServiceAccount", + Name: ctrl.Name + "-event-collector", + Namespace: ctrl.Namespace, + }, + }, + } + clusterRoleBinding.ObjectMeta.Name = ctrl.Name + "-event-collector" + return clusterRoleBinding +} + +func (ctrl *Controller) labelsForEventCollector() map[string]string { + return map[string]string{ + k8s.ManagedByLabelKey: "vector-operator", + k8s.NameLabelKey: "vector", + k8s.ComponentLabelKey: "EventCollector", + k8s.InstanceLabelKey: ctrl.Name, + } +} diff --git a/internal/vector/aggregator/service.go b/internal/vector/aggregator/service.go index 8bdfdf35..e19718b7 100644 --- a/internal/vector/aggregator/service.go +++ b/internal/vector/aggregator/service.go @@ -2,8 +2,6 @@ package aggregator import ( "context" - "fmt" - "github.com/kaasops/vector-operator/internal/common" "github.com/kaasops/vector-operator/internal/utils/k8s" "github.com/stoewer/go-strcase" corev1 "k8s.io/api/core/v1" @@ -12,7 +10,6 @@ import ( "maps" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" - "strconv" ) func (ctrl *Controller) ensureVectorAggregatorService(ctx context.Context) error { @@ -31,20 +28,8 @@ func (ctrl *Controller) ensureVectorAggregatorService(ctx context.Context) error if err := k8s.CreateOrUpdateResource(ctx, svc, ctrl.Client); err != nil { return err } - if svc.Annotations[common.AnnotationK8sEventsPort] != "" { - ctrl.EventsCollector.RegisterSubscriber( - ctrl.id, - svc.Name, - svc.Namespace, - svc.Annotations[common.AnnotationK8sEventsPort], - svc.Annotations[common.AnnotationK8sEventsNamespace], - ) - } } for _, svc := range existing { - if svc.Annotations[common.AnnotationK8sEventsPort] != "" { - ctrl.EventsCollector.UnregisterSubscriber(ctrl.id, svc.Name, svc.Namespace) - } if err := ctrl.Client.Delete(ctx, svc); err != nil { return err } @@ -67,11 +52,6 @@ func (ctrl *Controller) createVectorAggregatorServices() ([]*corev1.Service, err ports := make([]corev1.ServicePort, 0, len(list)) for _, sp := range list { - if sp.IsKubernetesEvents { - ann[common.AnnotationK8sEventsNamespace] = sp.Namespace - ann[common.AnnotationK8sEventsPort] = strconv.Itoa(int(sp.Port)) - } - ports = append(ports, corev1.ServicePort{ Name: strcase.KebabCase(sp.SourceName), Protocol: sp.Protocol, @@ -86,11 +66,7 @@ func (ctrl *Controller) createVectorAggregatorServices() ([]*corev1.Service, err Selector: labels, }, } - name := group.ServiceName - if name == "" { - name = strcase.KebabCase(fmt.Sprintf("%s-%s", svc.ObjectMeta.Name, group.PipelineName)) - } - svc.ObjectMeta.Name = name + svc.ObjectMeta.Name = group.ServiceName svcList = append(svcList, svc) } From 3f505a7da1b867bceb8e4c98b895607e1645a463 Mon Sep 17 00:00:00 2001 From: Aleksandr Aleksandrov Date: Mon, 13 Jan 2025 14:51:15 +0200 Subject: [PATCH 282/316] add retry mechanism for reconciling invalid vector pipelines --- cmd/manager/main.go | 22 +++++--- helm/charts/vector-operator/values.yaml | 2 + internal/controller/pipeline_controller.go | 59 +++++++++++++++------- 3 files changed, 58 insertions(+), 25 deletions(-) diff --git a/cmd/manager/main.go b/cmd/manager/main.go index d3df8177..2f8ea057 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -79,6 +79,8 @@ func main() { var watchNamespace string var watchLabel string var configCheckTimeout time.Duration + var enableReconciliationInvalidPipelines bool + var reconciliationRetryDelay time.Duration flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+ "Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.") @@ -93,6 +95,10 @@ func main() { flag.StringVar(&watchNamespace, "watch-namespace", "", "Namespace to filter the list of watched objects") flag.StringVar(&watchLabel, "watch-name", "", "Filter the list of watched objects by checking the app.kubernetes.io/managed-by label") flag.DurationVar(&configCheckTimeout, "configcheck-timeout", 300*time.Second, "configcheck timeout") + flag.BoolVar(&enableReconciliationInvalidPipelines, "enable-reconciliation-invalid-pipelines", false, + "Enable the reconciliation process for pipelines with invalid configurations") + flag.DurationVar(&reconciliationRetryDelay, "reconciliation-retry-delay", 30*time.Second, "Specify the delay before retrying the reconciliation process for pipelines") + opts := zap.Options{ Development: true, } @@ -213,13 +219,15 @@ func main() { defer close(clusterVectorAggregatorsPipelineEventCh) if err = (&controller.PipelineReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Clientset: clientset, - ConfigCheckTimeout: configCheckTimeout, - VectorAgentEventCh: vectorAgentsPipelineEventCh, - VectorAggregatorsEventCh: vectorAggregatorsPipelineEventCh, - ClusterVectorAggregatorsEventCh: clusterVectorAggregatorsPipelineEventCh, + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Clientset: clientset, + ConfigCheckTimeout: configCheckTimeout, + VectorAgentEventCh: vectorAgentsPipelineEventCh, + VectorAggregatorsEventCh: vectorAggregatorsPipelineEventCh, + ClusterVectorAggregatorsEventCh: clusterVectorAggregatorsPipelineEventCh, + EnableReconciliationInvalidPipelines: enableReconciliationInvalidPipelines, + ReconciliationInvalidPipelinesRetryDelay: reconciliationRetryDelay, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "VectorPipeline") os.Exit(1) diff --git a/helm/charts/vector-operator/values.yaml b/helm/charts/vector-operator/values.yaml index 1d43cb02..5877eb8e 100644 --- a/helm/charts/vector-operator/values.yaml +++ b/helm/charts/vector-operator/values.yaml @@ -70,6 +70,8 @@ annotations: {} args: # - "-watch-namespace=vector" # Namespace to filter the list of watched objects # - "-watch-name=vector-operator" # Filter the list of watched objects by checking the app.kubernetes.io/managed-by label +# - "-enable-reconciliation-invalid-pipelines=true" # Enable the reconciliation process for pipelines with invalid configurations +# - "-reconciliation-retry-delay=120s" # Specify the delay before retrying the reconciliation process for pipelines vector: enable: false diff --git a/internal/controller/pipeline_controller.go b/internal/controller/pipeline_controller.go index eb481477..b6d5cde3 100644 --- a/internal/controller/pipeline_controller.go +++ b/internal/controller/pipeline_controller.go @@ -24,7 +24,9 @@ import ( "github.com/kaasops/vector-operator/internal/vector/aggregator" "github.com/kaasops/vector-operator/internal/vector/vectoragent" "golang.org/x/sync/errgroup" + "reflect" "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/predicate" "time" "github.com/kaasops/vector-operator/api/v1alpha1" @@ -44,11 +46,13 @@ type PipelineReconciler struct { Scheme *runtime.Scheme // Temp. Wait this issue - https://github.com/kubernetes-sigs/controller-runtime/issues/452 - Clientset *kubernetes.Clientset - ConfigCheckTimeout time.Duration - VectorAgentEventCh chan event.GenericEvent - VectorAggregatorsEventCh chan event.GenericEvent - ClusterVectorAggregatorsEventCh chan event.GenericEvent + Clientset *kubernetes.Clientset + ConfigCheckTimeout time.Duration + VectorAgentEventCh chan event.GenericEvent + VectorAggregatorsEventCh chan event.GenericEvent + ClusterVectorAggregatorsEventCh chan event.GenericEvent + EnableReconciliationInvalidPipelines bool + ReconciliationInvalidPipelinesRetryDelay time.Duration } var ( @@ -103,13 +107,15 @@ func (r *PipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c return ctrl.Result{}, nil } - notChanged, err := pipeline.IsPipelineChanged(pipelineCR) - if err != nil { - return ctrl.Result{}, err - } - if notChanged { - log.Info("Pipeline has no changes. Finish Reconcile Pipeline") - return ctrl.Result{}, nil + if !r.EnableReconciliationInvalidPipelines || pipelineCR.IsValid() { + notChanged, err := pipeline.IsPipelineChanged(pipelineCR) + if err != nil { + return ctrl.Result{}, err + } + if notChanged { + log.Info("Pipeline has no changes. Finish Reconcile Pipeline") + return ctrl.Result{}, nil + } } p := &config.PipelineConfig{} @@ -263,7 +269,13 @@ func (r *PipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c if err := pipeline.SetFailedStatus(ctx, r.Client, pipelineCR, err.Error()); err != nil { return ctrl.Result{}, err } - return ctrl.Result{}, IgnoreBuildConfigFailed(err) + if errors.Is(err, ErrBuildConfigFailed) { + return ctrl.Result{}, nil + } + if r.EnableReconciliationInvalidPipelines { + return ctrl.Result{RequeueAfter: r.ReconciliationInvalidPipelinesRetryDelay}, nil + } + return ctrl.Result{}, nil } if err = pipeline.SetSuccessStatus(ctx, r.Client, pipelineCR); err != nil { @@ -307,12 +319,23 @@ func (r *PipelineReconciler) SetupWithManager(mgr ctrl.Manager) error { For(&v1alpha1.VectorPipeline{}). WithOptions(controller.Options{MaxConcurrentReconciles: 20}). Watches(&v1alpha1.ClusterVectorPipeline{}, &handler.EnqueueRequestForObject{}). + WithEventFilter(specAndAnnotationsPredicate). Complete(r) } -func IgnoreBuildConfigFailed(err error) error { - if errors.Is(err, ErrBuildConfigFailed) { - return nil - } - return err +var specAndAnnotationsPredicate = predicate.Funcs{ + UpdateFunc: func(e event.UpdateEvent) bool { + oldObject := e.ObjectOld.(client.Object) + newObject := e.ObjectNew.(client.Object) + + if oldObject.GetGeneration() != newObject.GetGeneration() { + return true + } + + if !reflect.DeepEqual(oldObject.GetAnnotations(), newObject.GetAnnotations()) { + return true + } + + return false + }, } From 3f3b6d1c73a15cb1ba2d7da5c439efcbaf893044 Mon Sep 17 00:00:00 2001 From: Aleksandr Aleksandrov Date: Mon, 13 Jan 2025 16:42:01 +0200 Subject: [PATCH 283/316] helm package Signed-off-by: Aleksandr Aleksandrov --- helm/charts/vector-operator/Chart.yaml | 4 +- ...y.kaasops.io_clustervectoraggregators.yaml | 12 +++ ...vability.kaasops.io_vectoraggregators.yaml | 12 +++ helm/index.yaml | 97 ++++++++++-------- helm/packages/vector-operator-0.4.tgz | Bin 0 -> 99991 bytes 5 files changed, 81 insertions(+), 44 deletions(-) create mode 100644 helm/packages/vector-operator-0.4.tgz diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index 0a15d008..6ab5a380 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: "0.3" +version: "0.4" # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.1.1" +appVersion: "v0.1.2" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectoraggregators.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectoraggregators.yaml index 49463c7b..5b972dc0 100644 --- a/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectoraggregators.yaml +++ b/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectoraggregators.yaml @@ -2416,6 +2416,18 @@ spec: - name type: object type: array + eventCollector: + properties: + image: + type: string + imagePullPolicy: + description: PullPolicy describes a policy for if/when to pull + a container image + type: string + maxBatchSize: + format: int32 + type: integer + type: object host_aliases: description: |- HostAliases provides mapping between ip and hostnames, diff --git a/helm/charts/vector-operator/crds/observability.kaasops.io_vectoraggregators.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_vectoraggregators.yaml index f3d754d8..376ca231 100644 --- a/helm/charts/vector-operator/crds/observability.kaasops.io_vectoraggregators.yaml +++ b/helm/charts/vector-operator/crds/observability.kaasops.io_vectoraggregators.yaml @@ -2414,6 +2414,18 @@ spec: - name type: object type: array + eventCollector: + properties: + image: + type: string + imagePullPolicy: + description: PullPolicy describes a policy for if/when to pull + a container image + type: string + maxBatchSize: + format: int32 + type: integer + type: object host_aliases: description: |- HostAliases provides mapping between ip and hostnames, diff --git a/helm/index.yaml b/helm/index.yaml index 76ea5f61..52ae2971 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.1.2 + created: "2025-01-13T16:40:57.735292+02:00" + description: A Helm chart to install Vector Operator + digest: e1fe0e96c146c7c275c181e727c8a60f21898cabe90629851a2920d2915f84b7 + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.4.tgz + version: "0.4" - apiVersion: v2 appVersion: v0.1.1 - created: "2024-10-09T11:39:24.733735+03:00" + created: "2025-01-13T16:40:57.733469+02:00" description: A Helm chart to install Vector Operator digest: a916c9e9f81bdbf9f734073fb453a6b67d7a724ed7ff4326d7884b136c103ce5 home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: "0.3" - apiVersion: v2 appVersion: v0.1.1 - created: "2024-10-09T11:39:24.73182+03:00" + created: "2025-01-13T16:40:57.73164+02:00" description: A Helm chart to install Vector Operator digest: 582d95c6f63134f6cd815bcb85adce5770e179de21a979ec76f91ab7d8531b45 home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: "0.2" - apiVersion: v2 appVersion: v0.1.1 - created: "2024-10-09T11:39:24.729714+03:00" + created: "2025-01-13T16:40:57.729673+02:00" description: A Helm chart to install Vector Operator digest: 3ac5a422f3f1861528f737a0fea077cad5f7b7516db3fe5d392c887d5d3459d5 home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.1.1 - apiVersion: v2 appVersion: v0.1.0 - created: "2024-10-09T11:39:24.727149+03:00" + created: "2025-01-13T16:40:57.72751+02:00" description: A Helm chart to install Vector Operator digest: 191ec4f83f11541df19680ce220992c84fb10210f0d54a5acc29361ccfd787bb home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.1.0 - apiVersion: v2 appVersion: pre-v0.1.0-r1 - created: "2024-10-09T11:39:24.724886+03:00" + created: "2025-01-13T16:40:57.725159+02:00" description: A Helm chart to install Vector Operator digest: 01bd2e347c5782127511a0a0fbbab72508cbe667664f2c9b615cabb80a4c40c7 home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.1.0-rc1 - apiVersion: v2 appVersion: v0.0.40 - created: "2024-10-09T11:39:24.720433+03:00" + created: "2025-01-13T16:40:57.721203+02:00" description: A Helm chart to install Vector Operator digest: c50e673e811b8d4c03ad45c92ce23b9bc54eef4665091e70fab9a4b7e4c6c3f1 home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.0.40 - apiVersion: v2 appVersion: v0.0.39 - created: "2024-10-09T11:39:24.719279+03:00" + created: "2025-01-13T16:40:57.720297+02:00" description: A Helm chart to install Vector Operator digest: e1de38c869bf896bfb9f5f615c329d3ccce3bd91cb64bb6fa1c783a40ea290a2 home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.0.39 - apiVersion: v2 appVersion: v0.0.38 - created: "2024-10-09T11:39:24.718339+03:00" + created: "2025-01-13T16:40:57.719229+02:00" description: A Helm chart to install Vector Operator digest: 565b148184400900f5572b06ceebaac6340af5e5fd1122b308bdbfcbc2d2040a home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.0.38 - apiVersion: v2 appVersion: v0.0.37 - created: "2024-10-09T11:39:24.717327+03:00" + created: "2025-01-13T16:40:57.718075+02:00" description: A Helm chart to install Vector Operator digest: 65e10ee46e6855ba95f51e21ca14abc4411191b260c6b96ec72c775e73c1e331 home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.0.37 - apiVersion: v2 appVersion: v0.0.36 - created: "2024-10-09T11:39:24.716307+03:00" + created: "2025-01-13T16:40:57.716628+02:00" description: A Helm chart to install Vector Operator digest: 972b6b4048b6d17b0616786e49915ef52cb7c9573cfb6eb359b6c19b66eabe31 home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.0.36 - apiVersion: v2 appVersion: v0.0.35 - created: "2024-10-09T11:39:24.715146+03:00" + created: "2025-01-13T16:40:57.715559+02:00" description: A Helm chart to install Vector Operator digest: d03ba759c42f2bd8d8f1df71702ee4f26a73b8bc28760ca7254af20d811cee8a home: https://github.com/kaasops/vector-operator @@ -146,7 +159,7 @@ entries: version: 0.0.35 - apiVersion: v2 appVersion: v0.0.34 - created: "2024-10-09T11:39:24.714207+03:00" + created: "2025-01-13T16:40:57.714656+02:00" description: A Helm chart to install Vector Operator digest: 01e9d488ee78c8603d821f96344edee568ebcb42049d586de37b8df39b372bd4 home: https://github.com/kaasops/vector-operator @@ -159,7 +172,7 @@ entries: version: 0.0.34 - apiVersion: v2 appVersion: v0.0.33 - created: "2024-10-09T11:39:24.713503+03:00" + created: "2025-01-13T16:40:57.713569+02:00" description: A Helm chart to install Vector Operator digest: fcde3c94a0fa6caa5f3d333226c95b7c85ede8489d46277e1222a868ed4ec8c3 home: https://github.com/kaasops/vector-operator @@ -172,7 +185,7 @@ entries: version: 0.0.33 - apiVersion: v2 appVersion: v0.0.32 - created: "2024-10-09T11:39:24.712414+03:00" + created: "2025-01-13T16:40:57.712382+02:00" description: A Helm chart to install Vector Operator digest: 26323037ec47f1703ea930a99ab4ec8fb93b44975ce969514ea68d4130017015 home: https://github.com/kaasops/vector-operator @@ -185,7 +198,7 @@ entries: version: 0.0.32 - apiVersion: v2 appVersion: v0.0.31 - created: "2024-10-09T11:39:24.711008+03:00" + created: "2025-01-13T16:40:57.711342+02:00" description: A Helm chart to install Vector Operator digest: 45b924c07a825e0f7cd3fb534a6ffd16604790d13be1aff59150c045474754e3 home: https://github.com/kaasops/vector-operator @@ -198,7 +211,7 @@ entries: version: 0.0.31 - apiVersion: v2 appVersion: v0.0.30 - created: "2024-10-09T11:39:24.709998+03:00" + created: "2025-01-13T16:40:57.710311+02:00" description: A Helm chart to install Vector Operator digest: 03beda549d15f50325028ea29af5f2065ac0b8adf3078bf7dc1312981aa5e7db home: https://github.com/kaasops/vector-operator @@ -211,7 +224,7 @@ entries: version: 0.0.30 - apiVersion: v2 appVersion: v0.0.29 - created: "2024-10-09T11:39:24.709287+03:00" + created: "2025-01-13T16:40:57.709291+02:00" description: A Helm chart to install Vector Operator digest: 0f025fc3a924b37b8c4131c4d8cfa437d2d4e557ab9476ed3e69a00232c7dca6 home: https://github.com/kaasops/vector-operator @@ -224,7 +237,7 @@ entries: version: 0.0.29 - apiVersion: v2 appVersion: v0.0.28 - created: "2024-10-09T11:39:24.708534+03:00" + created: "2025-01-13T16:40:57.70837+02:00" description: A Helm chart to install Vector Operator digest: af856d41314313e04f15e7143409a9c564c6ca610b0d2eaec3112add8573e668 home: https://github.com/kaasops/vector-operator @@ -237,7 +250,7 @@ entries: version: 0.0.28 - apiVersion: v2 appVersion: v0.0.27 - created: "2024-10-09T11:39:24.707333+03:00" + created: "2025-01-13T16:40:57.70743+02:00" description: A Helm chart to install Vector Operator digest: 631e2ff02bbd7f247cb486494fd2af60c57cc551066a6a3858226551bc1745a4 home: https://github.com/kaasops/vector-operator @@ -250,7 +263,7 @@ entries: version: 0.0.27 - apiVersion: v2 appVersion: v0.0.26 - created: "2024-10-09T11:39:24.706695+03:00" + created: "2025-01-13T16:40:57.706108+02:00" description: A Helm chart to install Vector Operator digest: 760a2833f4c1a33466982419b079ff18d996331ebacc40cf93b0f55229cdb7db home: https://github.com/kaasops/vector-operator @@ -263,7 +276,7 @@ entries: version: 0.0.26 - apiVersion: v2 appVersion: v0.0.25 - created: "2024-10-09T11:39:24.706048+03:00" + created: "2025-01-13T16:40:57.705173+02:00" description: A Helm chart to install Vector Operator digest: fd22b996b071b6d85740ccf76e85cb640fa717c2620748d206d3f4fdd44cbcc2 home: https://github.com/kaasops/vector-operator @@ -276,7 +289,7 @@ entries: version: 0.0.25 - apiVersion: v2 appVersion: v0.0.24 - created: "2024-10-09T11:39:24.705103+03:00" + created: "2025-01-13T16:40:57.704182+02:00" description: A Helm chart to install Vector Operator digest: ea257e60ecde063a0d1ed52ce5e3283245b8f0e2daba58ea3a5adb0ba82d7799 home: https://github.com/kaasops/vector-operator @@ -289,7 +302,7 @@ entries: version: 0.0.24 - apiVersion: v2 appVersion: v0.0.23 - created: "2024-10-09T11:39:24.704224+03:00" + created: "2025-01-13T16:40:57.703115+02:00" description: A Helm chart to install Vector Operator digest: 546d202b3b9263f789b88335263191098dfcabd5d8698105f37cad24d56a8ed0 home: https://github.com/kaasops/vector-operator @@ -302,7 +315,7 @@ entries: version: 0.0.23 - apiVersion: v2 appVersion: v0.0.22 - created: "2024-10-09T11:39:24.703203+03:00" + created: "2025-01-13T16:40:57.701907+02:00" description: A Helm chart to install Vector Operator digest: bf96ddc8ac61e9d6beb8bc763fbf3fa6025d950b29d70d80de6e8a0ea45e0411 home: https://github.com/kaasops/vector-operator @@ -315,7 +328,7 @@ entries: version: 0.0.22 - apiVersion: v2 appVersion: v0.0.21 - created: "2024-10-09T11:39:24.702489+03:00" + created: "2025-01-13T16:40:57.700841+02:00" description: A Helm chart to install Vector Operator digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d home: https://github.com/kaasops/vector-operator @@ -328,7 +341,7 @@ entries: version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2024-10-09T11:39:24.701078+03:00" + created: "2025-01-13T16:40:57.69978+02:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -341,7 +354,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2024-10-09T11:39:24.700584+03:00" + created: "2025-01-13T16:40:57.698863+02:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -354,7 +367,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2024-10-09T11:39:24.69997+03:00" + created: "2025-01-13T16:40:57.698401+02:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -367,7 +380,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2024-10-09T11:39:24.699408+03:00" + created: "2025-01-13T16:40:57.697925+02:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -380,7 +393,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2024-10-09T11:39:24.698883+03:00" + created: "2025-01-13T16:40:57.697448+02:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -393,7 +406,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2024-10-09T11:39:24.698273+03:00" + created: "2025-01-13T16:40:57.696808+02:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -406,7 +419,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2024-10-09T11:39:24.697277+03:00" + created: "2025-01-13T16:40:57.69605+02:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -419,7 +432,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2024-10-09T11:39:24.696713+03:00" + created: "2025-01-13T16:40:57.695578+02:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -432,7 +445,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2024-10-09T11:39:24.696119+03:00" + created: "2025-01-13T16:40:57.694874+02:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -445,7 +458,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2024-10-09T11:39:24.695569+03:00" + created: "2025-01-13T16:40:57.694212+02:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -458,7 +471,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2024-10-09T11:39:24.695047+03:00" + created: "2025-01-13T16:40:57.69374+02:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -471,7 +484,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2024-10-09T11:39:24.722197+03:00" + created: "2025-01-13T16:40:57.723132+02:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -484,7 +497,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2024-10-09T11:39:24.721843+03:00" + created: "2025-01-13T16:40:57.72277+02:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -497,7 +510,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2024-10-09T11:39:24.7215+03:00" + created: "2025-01-13T16:40:57.722156+02:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -510,7 +523,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2024-10-09T11:39:24.720958+03:00" + created: "2025-01-13T16:40:57.721709+02:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -523,7 +536,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2024-10-09T11:39:24.694551+03:00" + created: "2025-01-13T16:40:57.692998+02:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -534,4 +547,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2024-10-09T11:39:24.693244+03:00" +generated: "2025-01-13T16:40:57.690577+02:00" diff --git a/helm/packages/vector-operator-0.4.tgz b/helm/packages/vector-operator-0.4.tgz new file mode 100644 index 0000000000000000000000000000000000000000..2b2c24e572e81a130e9b9fdb776210f1c8e8ca4a GIT binary patch literal 99991 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 yc3>Qi>QNd|YKsJwF1x>}A?d+Atr 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 nx1npFmWlo6fBnDz&!HU3p&ZJgQ2D Date: Fri, 24 Jan 2025 10:37:15 +0200 Subject: [PATCH 284/316] Global options: expire_metrics_secs (#167) Add support for global option expire-metrics-secs --------- Signed-off-by: Aleksandr Aleksandrov Co-authored-by: an.makarov --- api/v1alpha1/vector_common_types.go | 3 + api/v1alpha1/zz_generated.deepcopy.go | 5 ++ ...y.kaasops.io_clustervectoraggregators.yaml | 5 ++ ...vability.kaasops.io_vectoraggregators.yaml | 5 ++ .../observability.kaasops.io_vectors.yaml | 5 ++ docs/specification.md | 4 ++ ...y.kaasops.io_clustervectoraggregators.yaml | 5 ++ ...vability.kaasops.io_vectoraggregators.yaml | 5 ++ .../observability.kaasops.io_vectors.yaml | 5 ++ internal/common/annotations.go | 1 + internal/config/config.go | 2 + internal/config/types.go | 10 +-- .../clustervectoraggregator_controller.go | 1 + internal/controller/pipeline_controller.go | 3 + internal/controller/vector_controller.go | 1 + .../controller/vectoraggregator_controller.go | 1 + internal/utils/compression/compression.go | 25 ++++++++ .../utils/compression/compression_test.go | 63 +++++++++++++++++++ internal/vector/aggregator/config.go | 46 +++++++++++++- internal/vector/aggregator/controller.go | 6 +- internal/vector/aggregator/deployment.go | 14 ++++- .../vector/vectoragent/vectoragent_config.go | 2 +- .../vectoragent/vectoragent_controller.go | 63 ++++++++++++++++--- 23 files changed, 260 insertions(+), 20 deletions(-) create mode 100644 internal/utils/compression/compression_test.go diff --git a/api/v1alpha1/vector_common_types.go b/api/v1alpha1/vector_common_types.go index 846259d6..332b0bd5 100644 --- a/api/v1alpha1/vector_common_types.go +++ b/api/v1alpha1/vector_common_types.go @@ -69,6 +69,9 @@ type VectorCommon struct { // The directory used for persisting Vector state, such as on-disk buffers, file checkpoints, and more. Please make sure the Vector project has write permissions to this directory. // https://vector.dev/docs/reference/configuration/global-options/#data_dir DataDir string `json:"dataDir,omitempty"` + // Vector will expire internal metrics that haven’t been emitted/updated in the configured interval (default 300 seconds). + // https://vector.dev/docs/reference/configuration/global-options/#expire_metrics_secs + ExpireMetricsSecs *int `json:"expireMetricsSecs,omitempty"` // Vector API params. Allows to interact with a running Vector instance. // https://vector.dev/docs/reference/api/ Api ApiSpec `json:"api,omitempty"` diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 002479b6..a74125d7 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -466,6 +466,11 @@ func (in *VectorCommon) DeepCopyInto(out *VectorCommon) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.ExpireMetricsSecs != nil { + in, out := &in.ExpireMetricsSecs, &out.ExpireMetricsSecs + *out = new(int) + **out = **in + } out.Api = in.Api if in.Volumes != nil { in, out := &in.Volumes, &out.Volumes diff --git a/config/crd/bases/observability.kaasops.io_clustervectoraggregators.yaml b/config/crd/bases/observability.kaasops.io_clustervectoraggregators.yaml index 5b972dc0..f048abd3 100644 --- a/config/crd/bases/observability.kaasops.io_clustervectoraggregators.yaml +++ b/config/crd/bases/observability.kaasops.io_clustervectoraggregators.yaml @@ -2428,6 +2428,11 @@ spec: format: int32 type: integer type: object + expireMetricsSecs: + description: |- + Vector will expire internal metrics that haven’t been emitted/updated in the configured interval (default 300 seconds). + https://vector.dev/docs/reference/configuration/global-options/#expire_metrics_secs + type: integer host_aliases: description: |- HostAliases provides mapping between ip and hostnames, diff --git a/config/crd/bases/observability.kaasops.io_vectoraggregators.yaml b/config/crd/bases/observability.kaasops.io_vectoraggregators.yaml index 376ca231..e0a9d48c 100644 --- a/config/crd/bases/observability.kaasops.io_vectoraggregators.yaml +++ b/config/crd/bases/observability.kaasops.io_vectoraggregators.yaml @@ -2426,6 +2426,11 @@ spec: format: int32 type: integer type: object + expireMetricsSecs: + description: |- + Vector will expire internal metrics that haven’t been emitted/updated in the configured interval (default 300 seconds). + https://vector.dev/docs/reference/configuration/global-options/#expire_metrics_secs + type: integer host_aliases: description: |- HostAliases provides mapping between ip and hostnames, diff --git a/config/crd/bases/observability.kaasops.io_vectors.yaml b/config/crd/bases/observability.kaasops.io_vectors.yaml index 0fbd4cb2..b88cd8a9 100644 --- a/config/crd/bases/observability.kaasops.io_vectors.yaml +++ b/config/crd/bases/observability.kaasops.io_vectors.yaml @@ -2434,6 +2434,11 @@ spec: - name type: object type: array + expireMetricsSecs: + description: |- + Vector will expire internal metrics that haven’t been emitted/updated in the configured interval (default 300 seconds). + https://vector.dev/docs/reference/configuration/global-options/#expire_metrics_secs + type: integer host_aliases: description: |- HostAliases provides mapping between ip and hostnames, diff --git a/docs/specification.md b/docs/specification.md index afb728dd..6cde131c 100644 --- a/docs/specification.md +++ b/docs/specification.md @@ -18,6 +18,10 @@
+ + + + diff --git a/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectoraggregators.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectoraggregators.yaml index 5b972dc0..59d03656 100644 --- a/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectoraggregators.yaml +++ b/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectoraggregators.yaml @@ -2298,6 +2298,11 @@ spec: The directory used for persisting Vector state, such as on-disk buffers, file checkpoints, and more. Please make sure the Vector project has write permissions to this directory. https://vector.dev/docs/reference/configuration/global-options/#data_dir type: string + expireMetricsSecs: + description: |- + Vector will expire internal metrics that haven’t been emitted/updated in the configured interval (default 300 seconds). + https://vector.dev/docs/reference/configuration/global-options/#expire_metrics_secs + type: integer env: description: Env that will be added to Vector pod items: diff --git a/helm/charts/vector-operator/crds/observability.kaasops.io_vectoraggregators.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_vectoraggregators.yaml index 376ca231..6ec8ced6 100644 --- a/helm/charts/vector-operator/crds/observability.kaasops.io_vectoraggregators.yaml +++ b/helm/charts/vector-operator/crds/observability.kaasops.io_vectoraggregators.yaml @@ -2296,6 +2296,11 @@ spec: The directory used for persisting Vector state, such as on-disk buffers, file checkpoints, and more. Please make sure the Vector project has write permissions to this directory. https://vector.dev/docs/reference/configuration/global-options/#data_dir type: string + expireMetricsSecs: + description: |- + Vector will expire internal metrics that haven’t been emitted/updated in the configured interval (default 300 seconds). + https://vector.dev/docs/reference/configuration/global-options/#expire_metrics_secs + type: integer env: description: Env that will be added to Vector pod items: diff --git a/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml index 0fbd4cb2..d93655dc 100644 --- a/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml +++ b/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml @@ -2314,6 +2314,11 @@ spec: The directory used for persisting Vector state, such as on-disk buffers, file checkpoints, and more. Please make sure the Vector project has write permissions to this directory. https://vector.dev/docs/reference/configuration/global-options/#data_dir type: string + expireMetricsSecs: + description: |- + Vector will expire internal metrics that haven’t been emitted/updated in the configured interval (default 300 seconds). + https://vector.dev/docs/reference/configuration/global-options/#expire_metrics_secs + type: integer env: description: Env that will be added to Vector pod items: diff --git a/internal/common/annotations.go b/internal/common/annotations.go index 32cedac3..4503849d 100644 --- a/internal/common/annotations.go +++ b/internal/common/annotations.go @@ -2,4 +2,5 @@ package common const ( AnnotationServiceName = "observability.kaasops.io/service-name" + AnnotationRestartedAt = "vector-operator.kaasops.io/restartedAt" ) diff --git a/internal/config/config.go b/internal/config/config.go index 7bab11e2..5d4c45d5 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -41,6 +41,7 @@ type VectorConfigParams struct { PlaygroundEnabled bool UseApiServerCache bool InternalMetrics bool + ExpireMetricsSecs *int } func newVectorConfig(p VectorConfigParams) *VectorConfig { @@ -62,6 +63,7 @@ func newVectorConfig(p VectorConfigParams) *VectorConfig { Transforms: transforms, Sinks: sinks, }, + ExpireMetricsSecs: p.ExpireMetricsSecs, } } diff --git a/internal/config/types.go b/internal/config/types.go index 2e541d0e..b7d7e2fb 100644 --- a/internal/config/types.go +++ b/internal/config/types.go @@ -18,14 +18,16 @@ package config import ( "fmt" + corev1 "k8s.io/api/core/v1" ) type VectorConfig struct { - DataDir string `yaml:"data_dir"` - Api *ApiSpec `yaml:"api"` - PipelineConfig `yaml:",inline"` - internal internalConfig `yaml:"-"` + DataDir string `yaml:"data_dir"` + ExpireMetricsSecs *int `yaml:"expire_metrics_secs,omitempty"` + Api *ApiSpec `yaml:"api"` + PipelineConfig `yaml:",inline"` + internal internalConfig `yaml:"-"` } type PipelineConfig struct { diff --git a/internal/controller/clustervectoraggregator_controller.go b/internal/controller/clustervectoraggregator_controller.go index e83231a6..b084d82e 100644 --- a/internal/controller/clustervectoraggregator_controller.go +++ b/internal/controller/clustervectoraggregator_controller.go @@ -119,6 +119,7 @@ func (r *ClusterVectorAggregatorReconciler) createOrUpdateVectorAggregator(ctx c ApiEnabled: vaCtrl.Spec.Api.Enabled, PlaygroundEnabled: vaCtrl.Spec.Api.Playground, InternalMetrics: vaCtrl.Spec.InternalMetrics, + ExpireMetricsSecs: vaCtrl.Spec.ExpireMetricsSecs, }, pipelines...) if err != nil { if err := vaCtrl.SetFailedStatus(ctx, err.Error()); err != nil { diff --git a/internal/controller/pipeline_controller.go b/internal/controller/pipeline_controller.go index b6d5cde3..d91398a3 100644 --- a/internal/controller/pipeline_controller.go +++ b/internal/controller/pipeline_controller.go @@ -145,6 +145,7 @@ func (r *PipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c PlaygroundEnabled: vaCtrl.Vector.Spec.Agent.Api.Playground, UseApiServerCache: vaCtrl.Vector.Spec.UseApiServerCache, InternalMetrics: vaCtrl.Vector.Spec.Agent.InternalMetrics, + ExpireMetricsSecs: vaCtrl.Vector.Spec.Agent.ExpireMetricsSecs, }, pipelineCR) if err != nil { return fmt.Errorf("agent %s/%s build config failed: %w: %w", vector.Namespace, vector.Name, ErrBuildConfigFailed, err) @@ -181,6 +182,7 @@ func (r *PipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c ApiEnabled: vaCtrl.Spec.Api.Enabled, PlaygroundEnabled: vaCtrl.Spec.Api.Playground, InternalMetrics: vaCtrl.Spec.InternalMetrics, + ExpireMetricsSecs: vaCtrl.Spec.ExpireMetricsSecs, }, pipelineCR) if err != nil { return fmt.Errorf("aggregator %s/%s build config failed: %w: %w", vector.Namespace, vector.Name, ErrBuildConfigFailed, err) @@ -226,6 +228,7 @@ func (r *PipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c ApiEnabled: vaCtrl.Spec.Api.Enabled, PlaygroundEnabled: vaCtrl.Spec.Api.Playground, InternalMetrics: vaCtrl.Spec.InternalMetrics, + ExpireMetricsSecs: vaCtrl.Spec.ExpireMetricsSecs, }, pipelineCR) if err != nil { return fmt.Errorf("cluster aggregator %s/%s build config failed: %w: %w", vector.Namespace, vector.Name, ErrBuildConfigFailed, err) diff --git a/internal/controller/vector_controller.go b/internal/controller/vector_controller.go index bc47b9fa..ea7e42be 100644 --- a/internal/controller/vector_controller.go +++ b/internal/controller/vector_controller.go @@ -194,6 +194,7 @@ func (r *VectorReconciler) createOrUpdateVector(ctx context.Context, client clie PlaygroundEnabled: vaCtrl.Vector.Spec.Agent.Api.Playground, UseApiServerCache: vaCtrl.Vector.Spec.UseApiServerCache, InternalMetrics: vaCtrl.Vector.Spec.Agent.InternalMetrics, + ExpireMetricsSecs: vaCtrl.Vector.Spec.Agent.ExpireMetricsSecs, }, pipelines...) if err != nil { if err := vaCtrl.SetFailedStatus(ctx, err.Error()); err != nil { diff --git a/internal/controller/vectoraggregator_controller.go b/internal/controller/vectoraggregator_controller.go index 50417b0d..2ac911db 100644 --- a/internal/controller/vectoraggregator_controller.go +++ b/internal/controller/vectoraggregator_controller.go @@ -196,6 +196,7 @@ func (r *VectorAggregatorReconciler) createOrUpdateVectorAggregator(ctx context. ApiEnabled: vaCtrl.Spec.Api.Enabled, PlaygroundEnabled: vaCtrl.Spec.Api.Playground, InternalMetrics: vaCtrl.Spec.InternalMetrics, + ExpireMetricsSecs: vaCtrl.Spec.ExpireMetricsSecs, }, pipelines...) if err != nil { if err := vaCtrl.SetFailedStatus(ctx, err.Error()); err != nil { diff --git a/internal/utils/compression/compression.go b/internal/utils/compression/compression.go index cae4b025..5b19fe75 100644 --- a/internal/utils/compression/compression.go +++ b/internal/utils/compression/compression.go @@ -20,3 +20,28 @@ func Compress(data []byte, log logr.Logger) []byte { return b.Bytes() } + +func Decompress(data []byte, log logr.Logger) []byte { + if len(data) == 0 { + return []byte{} + } + + reader := bytes.NewReader(data) + gz, err := gzip.NewReader(reader) + if err != nil { + log.Error(err, "Failed to create gzip reader for decompress") + return nil + } + defer func() { + if err := gz.Close(); err != nil { + log.Error(err, "Failed to close reader for decompress") + } + }() + + var result bytes.Buffer + if _, err := result.ReadFrom(gz); err != nil { + log.Error(err, "Failed to read decompressed data") + return nil + } + return result.Bytes() +} diff --git a/internal/utils/compression/compression_test.go b/internal/utils/compression/compression_test.go new file mode 100644 index 00000000..d7e9a508 --- /dev/null +++ b/internal/utils/compression/compression_test.go @@ -0,0 +1,63 @@ +package compression + +import ( + "bytes" + "testing" + + "github.com/go-logr/logr" + "github.com/go-logr/zapr" + "go.uber.org/zap" +) + +type testCase struct { + name string + data []byte +} + +var testCases = []testCase{ + { + name: "case1", + data: []byte("Hello, World"), + }, + { + name: "caseEmpty", + data: []byte(""), + }, + { + name: "caseSpecialCharacters", + data: []byte("!@#$%^&*()"), + }, +} + +func TestCompressAndDecompress(t *testing.T) { + var log logr.Logger + zapLog, _ := zap.NewProduction() + defer zapLog.Sync() + log = zapr.NewLogger(zapLog) + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + compressed := Compress(tc.data, log) + decompressed := Decompress(compressed, log) + + if !bytes.Equal(tc.data, decompressed) { + t.Errorf("Compression and Decompression failed: expected %s, got %s", tc.data, decompressed) + } + }) + } +} + +func TestDecompressInvalidInput(t *testing.T) { + var log logr.Logger + zapLog, _ := zap.NewProduction() + defer zapLog.Sync() + log = zapr.NewLogger(zapLog) + + invalidCompressedData := []byte("wrong data") + + decompressed := Decompress(invalidCompressedData, log) + + if decompressed != nil { + t.Errorf("Decompression should have failed and returned nil") + } +} diff --git a/internal/vector/aggregator/config.go b/internal/vector/aggregator/config.go index 48f0fe1d..0e7d91b7 100644 --- a/internal/vector/aggregator/config.go +++ b/internal/vector/aggregator/config.go @@ -4,18 +4,58 @@ import ( "context" "github.com/kaasops/vector-operator/internal/utils/compression" "github.com/kaasops/vector-operator/internal/utils/k8s" + "gopkg.in/yaml.v2" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/log" ) -func (ctrl *Controller) ensureVectorAggregatorConfig(ctx context.Context) error { +type globalOptions struct { + ExpireMetricsSecs *int `yaml:"expire_metrics_secs,omitempty"` +} + +func (ctrl *Controller) ensureVectorAggregatorConfig(ctx context.Context) (bool, error) { log := log.FromContext(ctx).WithValues(ctrl.prefix()+"vector-aggregator-secret", ctrl.Name) log.Info("start Reconcile Vector Aggregator Secret") + vectorAggregatorSecret, err := ctrl.createVectorAggregatorConfig(ctx) if err != nil { - return err + return false, err + } + + globalOptionsChanged := false + + var prevSecret corev1.Secret + key := types.NamespacedName{Namespace: vectorAggregatorSecret.Namespace, Name: vectorAggregatorSecret.Name} + if err = ctrl.Get(ctx, key, &prevSecret); err == nil { + // check that the global settings have not been changed + var prevConfig globalOptions + data := prevSecret.Data["config.json"] + if ctrl.Spec.CompressConfigFile { + data = compression.Decompress(data, log) + } + + err = yaml.Unmarshal(data, &prevConfig) + if err == nil { + var actualConfig globalOptions + err = yaml.Unmarshal(ctrl.ConfigBytes, &actualConfig) + if err == nil { + if actualConfig.ExpireMetricsSecs == nil && prevConfig.ExpireMetricsSecs != nil { + globalOptionsChanged = true + } + if actualConfig.ExpireMetricsSecs != nil && prevConfig.ExpireMetricsSecs == nil { + globalOptionsChanged = true + } + if actualConfig.ExpireMetricsSecs != nil && + prevConfig.ExpireMetricsSecs != nil && + *actualConfig.ExpireMetricsSecs != *prevConfig.ExpireMetricsSecs { + globalOptionsChanged = true + } + } + } } - return k8s.CreateOrUpdateResource(ctx, vectorAggregatorSecret, ctrl.Client) + + return globalOptionsChanged, k8s.CreateOrUpdateResource(ctx, vectorAggregatorSecret, ctrl.Client) } func (ctrl *Controller) createVectorAggregatorConfig(ctx context.Context) (*corev1.Secret, error) { diff --git a/internal/vector/aggregator/controller.go b/internal/vector/aggregator/controller.go index 50db1b3a..cd4ab0ff 100644 --- a/internal/vector/aggregator/controller.go +++ b/internal/vector/aggregator/controller.go @@ -2,6 +2,7 @@ package aggregator import ( "context" + vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" "github.com/kaasops/vector-operator/internal/buildinfo" "github.com/kaasops/vector-operator/internal/config" @@ -83,7 +84,8 @@ func (ctrl *Controller) EnsureVectorAggregator(ctx context.Context) error { return err } - if err := ctrl.ensureVectorAggregatorConfig(ctx); err != nil { + globalOptsChanged, err := ctrl.ensureVectorAggregatorConfig(ctx) + if err != nil { return err } @@ -101,7 +103,7 @@ func (ctrl *Controller) EnsureVectorAggregator(ctx context.Context) error { } } - if err := ctrl.ensureVectorAggregatorDeployment(ctx); err != nil { + if err := ctrl.ensureVectorAggregatorDeployment(ctx, globalOptsChanged); err != nil { return err } diff --git a/internal/vector/aggregator/deployment.go b/internal/vector/aggregator/deployment.go index bdbb63ea..9d640ae4 100644 --- a/internal/vector/aggregator/deployment.go +++ b/internal/vector/aggregator/deployment.go @@ -2,17 +2,27 @@ package aggregator import ( "context" + "github.com/kaasops/vector-operator/internal/common" "github.com/kaasops/vector-operator/internal/utils/k8s" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/log" + "time" ) -func (ctrl *Controller) ensureVectorAggregatorDeployment(ctx context.Context) error { +func (ctrl *Controller) ensureVectorAggregatorDeployment(ctx context.Context, globalOptsChanged bool) error { log := log.FromContext(ctx).WithValues(ctrl.prefix()+"vector-aggregator-deployment", ctrl.Name) log.Info("start Reconcile Vector Aggregator Deployment") - return k8s.CreateOrUpdateResource(ctx, ctrl.createVectorAggregatorDeployment(), ctrl.Client) + deployment := ctrl.createVectorAggregatorDeployment() + if globalOptsChanged { + // restart pods + if deployment.Spec.Template.Annotations == nil { + deployment.Spec.Template.Annotations = make(map[string]string) + } + deployment.Spec.Template.Annotations[common.AnnotationRestartedAt] = time.Now().Format(time.RFC3339) + } + return k8s.CreateOrUpdateResource(ctx, deployment, ctrl.Client) } func (ctrl *Controller) createVectorAggregatorDeployment() *appsv1.Deployment { diff --git a/internal/vector/vectoragent/vectoragent_config.go b/internal/vector/vectoragent/vectoragent_config.go index 66313e83..7be7e6c4 100644 --- a/internal/vector/vectoragent/vectoragent_config.go +++ b/internal/vector/vectoragent/vectoragent_config.go @@ -28,7 +28,7 @@ func (ctrl *Controller) createVectorAgentConfig(ctx context.Context) (*corev1.Se log := log.FromContext(ctx).WithValues("vector-agent-rbac", ctrl.Vector.Name) labels := ctrl.labelsForVectorAgent() annotations := ctrl.annotationsForVectorAgent() - var data []byte = ctrl.Config + var data = ctrl.Config if ctrl.Vector.Spec.Agent.CompressConfigFile { data = compression.Compress(ctrl.Config, log) diff --git a/internal/vector/vectoragent/vectoragent_controller.go b/internal/vector/vectoragent/vectoragent_controller.go index 6d77623f..e5071feb 100644 --- a/internal/vector/vectoragent/vectoragent_controller.go +++ b/internal/vector/vectoragent/vectoragent_controller.go @@ -18,14 +18,23 @@ package vectoragent import ( "context" - "k8s.io/utils/ptr" - + "github.com/kaasops/vector-operator/internal/common" + "github.com/kaasops/vector-operator/internal/utils/compression" "github.com/kaasops/vector-operator/internal/utils/k8s" monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + "gopkg.in/yaml.v2" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/log" + "time" ) +type globalOptions struct { + ExpireMetricsSecs *int `yaml:"expire_metrics_secs,omitempty"` +} + func (ctrl *Controller) EnsureVectorAgent(ctx context.Context, configOnly bool) error { log := log.FromContext(ctx).WithValues("vector-agent", ctrl.Vector.Name) log.Info("start Reconcile Vector Agent") @@ -35,7 +44,8 @@ func (ctrl *Controller) EnsureVectorAgent(ctx context.Context, configOnly bool) return err } - if err := ctrl.ensureVectorAgentConfig(ctx); err != nil { + globalOptsChanged, err := ctrl.ensureVectorAgentConfig(ctx) + if err != nil { return err } if !configOnly { @@ -55,7 +65,7 @@ func (ctrl *Controller) EnsureVectorAgent(ctx context.Context, configOnly bool) } } - if err := ctrl.ensureVectorAgentDaemonSet(ctx); err != nil { + if err := ctrl.ensureVectorAgentDaemonSet(ctx, globalOptsChanged); err != nil { return err } } @@ -108,25 +118,62 @@ func (ctrl *Controller) ensureVectorAgentService(ctx context.Context) error { return k8s.CreateOrUpdateResource(ctx, vectorAgentService, ctrl.Client) } -func (ctrl *Controller) ensureVectorAgentConfig(ctx context.Context) error { +func (ctrl *Controller) ensureVectorAgentConfig(ctx context.Context) (bool, error) { log := log.FromContext(ctx).WithValues("vector-agent-secret", ctrl.Vector.Name) log.Info("start Reconcile Vector Agent Secret") vectorAgentSecret, err := ctrl.createVectorAgentConfig(ctx) if err != nil { - return err + return false, err } - return k8s.CreateOrUpdateResource(ctx, vectorAgentSecret, ctrl.Client) + globalOptionsChanged := false + + var prevSecret corev1.Secret + key := types.NamespacedName{Namespace: vectorAgentSecret.Namespace, Name: vectorAgentSecret.Name} + if err = ctrl.Get(ctx, key, &prevSecret); err == nil { + // check that the global settings have not been changed + var prevConfig globalOptions + data := prevSecret.Data["agent.json"] + if ctrl.Vector.Spec.Agent.CompressConfigFile { + data = compression.Decompress(data, log) + } + err = yaml.Unmarshal(data, &prevConfig) + if err == nil { + var actualConfig globalOptions + err = yaml.Unmarshal(ctrl.Config, &actualConfig) + if err == nil { + if actualConfig.ExpireMetricsSecs == nil && prevConfig.ExpireMetricsSecs != nil { + globalOptionsChanged = true + } + if actualConfig.ExpireMetricsSecs != nil && prevConfig.ExpireMetricsSecs == nil { + globalOptionsChanged = true + } + if actualConfig.ExpireMetricsSecs != nil && + prevConfig.ExpireMetricsSecs != nil && + *actualConfig.ExpireMetricsSecs != *prevConfig.ExpireMetricsSecs { + globalOptionsChanged = true + } + } + } + } + + return globalOptionsChanged, k8s.CreateOrUpdateResource(ctx, vectorAgentSecret, ctrl.Client) } -func (ctrl *Controller) ensureVectorAgentDaemonSet(ctx context.Context) error { +func (ctrl *Controller) ensureVectorAgentDaemonSet(ctx context.Context, globalOptsChanged bool) error { log := log.FromContext(ctx).WithValues("vector-agent-daemon-set", ctrl.Vector.Name) log.Info("start Reconcile Vector Agent DaemonSet") vectorAgentDaemonSet := ctrl.createVectorAgentDaemonSet() + if globalOptsChanged { + if vectorAgentDaemonSet.Spec.Template.Annotations == nil { + vectorAgentDaemonSet.Spec.Template.Annotations = make(map[string]string) + } + vectorAgentDaemonSet.Spec.Template.Annotations[common.AnnotationRestartedAt] = time.Now().Format(time.RFC3339) + } return k8s.CreateOrUpdateResource(ctx, vectorAgentDaemonSet, ctrl.Client) } From 30abc56cf4f8509f85f67b698fa0de54850af3c5 Mon Sep 17 00:00:00 2001 From: aa1ex <115736127+aa1ex@users.noreply.github.com> Date: Fri, 24 Jan 2025 10:53:53 +0200 Subject: [PATCH 285/316] Add syslog source type (#169) Signed-off-by: Aleksandr Aleksandrov --- internal/config/vector_source_types.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/config/vector_source_types.go b/internal/config/vector_source_types.go index 1a88b9d9..aa0c21dd 100644 --- a/internal/config/vector_source_types.go +++ b/internal/config/vector_source_types.go @@ -38,6 +38,7 @@ const ( SocketType = "socket" SplunkHECType = "splunk_hec" StatsDType = "statsd" + SyslogType = "syslog" VectorType = "vector" kubernetesEventsType = "kubernetes_events" ) @@ -64,6 +65,7 @@ var aggregatorTypes = map[string]struct{}{ SocketType: {}, SplunkHECType: {}, StatsDType: {}, + SyslogType: {}, VectorType: {}, kubernetesEventsType: {}, } From f602215a3caf53871f11b71458fc0d9dfad9c2b4 Mon Sep 17 00:00:00 2001 From: aa1ex <115736127+aa1ex@users.noreply.github.com> Date: Fri, 24 Jan 2025 12:35:11 +0200 Subject: [PATCH 286/316] improve performance (#171) improve performance with global vector params --------- Signed-off-by: Aleksandr Aleksandrov --- api/v1alpha1/vector_common_types.go | 7 +- ...y.kaasops.io_clustervectoraggregators.yaml | 3 + ...vability.kaasops.io_vectoraggregators.yaml | 3 + .../observability.kaasops.io_vectors.yaml | 3 + internal/config/agent.go | 14 +-- internal/config/config.go | 7 +- internal/config/types.go | 22 +++-- .../clustervectoraggregator_controller.go | 2 +- internal/controller/pipeline_controller.go | 8 +- internal/controller/vector_controller.go | 19 ++-- .../controller/vectoraggregator_controller.go | 2 +- internal/utils/compression/compression.go | 25 ------ .../utils/compression/compression_test.go | 63 ------------- internal/vector/aggregator/config.go | 44 +-------- internal/vector/aggregator/controller.go | 16 +++- internal/vector/aggregator/deployment.go | 4 +- internal/vector/vectoragent/vectoragent.go | 10 ++- .../vector/vectoragent/vectoragent_config.go | 4 +- .../vectoragent/vectoragent_controller.go | 90 ++++++------------- .../vector/vectoragent/vectoragent_service.go | 11 +-- 20 files changed, 118 insertions(+), 239 deletions(-) delete mode 100644 internal/utils/compression/compression_test.go diff --git a/api/v1alpha1/vector_common_types.go b/api/v1alpha1/vector_common_types.go index 332b0bd5..ff47e893 100644 --- a/api/v1alpha1/vector_common_types.go +++ b/api/v1alpha1/vector_common_types.go @@ -3,9 +3,10 @@ package v1alpha1 import v1 "k8s.io/api/core/v1" type VectorCommonStatus struct { - ConfigCheckResult *bool `json:"configCheckResult,omitempty"` - Reason *string `json:"reason,omitempty"` - LastAppliedConfigHash *uint32 `json:"LastAppliedConfigHash,omitempty"` + ConfigCheckResult *bool `json:"configCheckResult,omitempty"` + Reason *string `json:"reason,omitempty"` + LastAppliedConfigHash *uint32 `json:"LastAppliedConfigHash,omitempty"` + LastAppliedGlobalConfigHash *uint32 `json:"LastAppliedGlobalConfigHash,omitempty"` } type VectorCommon struct { diff --git a/config/crd/bases/observability.kaasops.io_clustervectoraggregators.yaml b/config/crd/bases/observability.kaasops.io_clustervectoraggregators.yaml index f048abd3..2b552658 100644 --- a/config/crd/bases/observability.kaasops.io_clustervectoraggregators.yaml +++ b/config/crd/bases/observability.kaasops.io_clustervectoraggregators.yaml @@ -4973,6 +4973,9 @@ spec: LastAppliedConfigHash: format: int32 type: integer + LastAppliedGlobalConfigHash: + format: int32 + type: integer configCheckResult: type: boolean reason: diff --git a/config/crd/bases/observability.kaasops.io_vectoraggregators.yaml b/config/crd/bases/observability.kaasops.io_vectoraggregators.yaml index e0a9d48c..0e17eec4 100644 --- a/config/crd/bases/observability.kaasops.io_vectoraggregators.yaml +++ b/config/crd/bases/observability.kaasops.io_vectoraggregators.yaml @@ -4966,6 +4966,9 @@ spec: LastAppliedConfigHash: format: int32 type: integer + LastAppliedGlobalConfigHash: + format: int32 + type: integer configCheckResult: type: boolean reason: diff --git a/config/crd/bases/observability.kaasops.io_vectors.yaml b/config/crd/bases/observability.kaasops.io_vectors.yaml index b88cd8a9..26f1cda9 100644 --- a/config/crd/bases/observability.kaasops.io_vectors.yaml +++ b/config/crd/bases/observability.kaasops.io_vectors.yaml @@ -4985,6 +4985,9 @@ spec: LastAppliedConfigHash: format: int32 type: integer + LastAppliedGlobalConfigHash: + format: int32 + type: integer configCheckResult: type: boolean reason: diff --git a/internal/config/agent.go b/internal/config/agent.go index 0c9fbbdf..e98afb4a 100644 --- a/internal/config/agent.go +++ b/internal/config/agent.go @@ -10,20 +10,24 @@ import ( goyaml "sigs.k8s.io/yaml" ) -func BuildAgentConfig(p VectorConfigParams, pipelines ...pipeline.Pipeline) ([]byte, error) { +const ( + AgentApiPort = 8686 +) + +func BuildAgentConfig(p VectorConfigParams, pipelines ...pipeline.Pipeline) (*VectorConfig, []byte, error) { cfg, err := buildAgentConfig(p, pipelines...) if err != nil { - return nil, err + return nil, nil, err } yamlBytes, err := yaml.Marshal(cfg) if err != nil { - return nil, err + return nil, nil, err } jsonBytes, err := goyaml.YAMLToJSON(yamlBytes) if err != nil { - return nil, err + return nil, nil, err } - return jsonBytes, nil + return cfg, jsonBytes, nil } func buildAgentConfig(params VectorConfigParams, pipelines ...pipeline.Pipeline) (*VectorConfig, error) { diff --git a/internal/config/config.go b/internal/config/config.go index 5d4c45d5..e51f52fb 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -22,7 +22,6 @@ import ( "fmt" vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" "github.com/kaasops/vector-operator/internal/evcollector" - "github.com/kaasops/vector-operator/internal/vector/vectoragent" "github.com/mitchellh/mapstructure" "gopkg.in/yaml.v2" "net" @@ -50,7 +49,7 @@ func newVectorConfig(p VectorConfigParams) *VectorConfig { sinks := make(map[string]*Sink) api := &ApiSpec{ - Address: net.JoinHostPort("0.0.0.0", strconv.Itoa(vectoragent.ApiPort)), + Address: net.JoinHostPort("0.0.0.0", strconv.Itoa(AgentApiPort)), Enabled: p.ApiEnabled, Playground: p.PlaygroundEnabled, } @@ -63,7 +62,9 @@ func newVectorConfig(p VectorConfigParams) *VectorConfig { Transforms: transforms, Sinks: sinks, }, - ExpireMetricsSecs: p.ExpireMetricsSecs, + globalOptions: globalOptions{ + ExpireMetricsSecs: p.ExpireMetricsSecs, + }, } } diff --git a/internal/config/types.go b/internal/config/types.go index b7d7e2fb..a81fc54a 100644 --- a/internal/config/types.go +++ b/internal/config/types.go @@ -17,17 +17,23 @@ limitations under the License. package config import ( + "encoding/json" "fmt" + "github.com/kaasops/vector-operator/internal/utils/hash" corev1 "k8s.io/api/core/v1" ) +type globalOptions struct { + ExpireMetricsSecs *int `yaml:"expire_metrics_secs,omitempty"` +} + type VectorConfig struct { - DataDir string `yaml:"data_dir"` - ExpireMetricsSecs *int `yaml:"expire_metrics_secs,omitempty"` - Api *ApiSpec `yaml:"api"` - PipelineConfig `yaml:",inline"` - internal internalConfig `yaml:"-"` + DataDir string `yaml:"data_dir"` + globalOptions `yaml:",inline"` + Api *ApiSpec `yaml:"api"` + PipelineConfig `yaml:",inline"` + internal internalConfig `yaml:"-"` } type PipelineConfig struct { @@ -96,3 +102,9 @@ func (c *internalConfig) addServicePort(port *ServicePort) error { } return nil } + +func (c *VectorConfig) GetGlobalConfigHash() *uint32 { + bytes, _ := json.Marshal(c.globalOptions) + gHash := hash.Get(bytes) + return &gHash +} diff --git a/internal/controller/clustervectoraggregator_controller.go b/internal/controller/clustervectoraggregator_controller.go index b084d82e..7537ecfa 100644 --- a/internal/controller/clustervectoraggregator_controller.go +++ b/internal/controller/clustervectoraggregator_controller.go @@ -167,7 +167,7 @@ func (r *ClusterVectorAggregatorReconciler) createOrUpdateVectorAggregator(ctx c return ctrl.Result{}, err } - if err := vaCtrl.SetSuccessStatus(ctx, &cfgHash); err != nil { + if err := vaCtrl.SetSuccessStatus(ctx, &cfgHash, vaCtrl.Config.GetGlobalConfigHash()); err != nil { return ctrl.Result{}, err } diff --git a/internal/controller/pipeline_controller.go b/internal/controller/pipeline_controller.go index d91398a3..52b5d465 100644 --- a/internal/controller/pipeline_controller.go +++ b/internal/controller/pipeline_controller.go @@ -140,7 +140,7 @@ func (r *PipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c for _, vector := range vectorAgents { eg.Go(func() error { vaCtrl := vectoragent.NewController(vector, r.Client, r.Clientset) - byteConfig, err := config.BuildAgentConfig(config.VectorConfigParams{ + cfg, byteConfig, err := config.BuildAgentConfig(config.VectorConfigParams{ ApiEnabled: vaCtrl.Vector.Spec.Agent.Api.Enabled, PlaygroundEnabled: vaCtrl.Vector.Spec.Agent.Api.Playground, UseApiServerCache: vaCtrl.Vector.Spec.UseApiServerCache, @@ -151,9 +151,11 @@ func (r *PipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c return fmt.Errorf("agent %s/%s build config failed: %w: %w", vector.Namespace, vector.Name, ErrBuildConfigFailed, err) } - vaCtrl.Config = byteConfig + vaCtrl.Config = cfg + vaCtrl.ByteConfig = byteConfig + configCheck := configcheck.New( - vaCtrl.Config, + vaCtrl.ByteConfig, vaCtrl.Client, vaCtrl.ClientSet, &vaCtrl.Vector.Spec.Agent.VectorCommon, diff --git a/internal/controller/vector_controller.go b/internal/controller/vector_controller.go index ea7e42be..54961e39 100644 --- a/internal/controller/vector_controller.go +++ b/internal/controller/vector_controller.go @@ -86,7 +86,7 @@ func (r *VectorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr log.Error(err, "Failed to list vector instances") return ctrl.Result{}, err } - return r.reconcileVectors(ctx, r.Client, r.Clientset, false, vectors...) + return r.reconcileVectors(ctx, r.Client, r.Clientset, vectors...) } vectorCR, err := r.findVectorCustomResourceInstance(ctx, req) @@ -98,7 +98,7 @@ func (r *VectorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr log.Info("Vector CR not found. Ignoring since object must be deleted") return ctrl.Result{}, nil } - return r.createOrUpdateVector(ctx, r.Client, r.Clientset, vectorCR, false) + return r.createOrUpdateVector(ctx, r.Client, r.Clientset, vectorCR) } // SetupWithManager sets up the controller with the Manager. @@ -156,7 +156,7 @@ func (r *VectorReconciler) findVectorCustomResourceInstance(ctx context.Context, return vectorCR, nil } -func (r *VectorReconciler) reconcileVectors(ctx context.Context, client client.Client, clientset *kubernetes.Clientset, configOnly bool, vectors ...*v1alpha1.Vector) (ctrl.Result, error) { +func (r *VectorReconciler) reconcileVectors(ctx context.Context, client client.Client, clientset *kubernetes.Clientset, vectors ...*v1alpha1.Vector) (ctrl.Result, error) { if len(vectors) == 0 { return ctrl.Result{}, nil } @@ -166,14 +166,14 @@ func (r *VectorReconciler) reconcileVectors(ctx context.Context, client client.C continue } setAgentTypeMetaIfNeeded(vector) - if _, err := r.createOrUpdateVector(ctx, client, clientset, vector, configOnly); err != nil { + if _, err := r.createOrUpdateVector(ctx, client, clientset, vector); err != nil { return ctrl.Result{}, err } } return ctrl.Result{}, nil } -func (r *VectorReconciler) createOrUpdateVector(ctx context.Context, client client.Client, clientset *kubernetes.Clientset, v *v1alpha1.Vector, configOnly bool) (ctrl.Result, error) { +func (r *VectorReconciler) createOrUpdateVector(ctx context.Context, client client.Client, clientset *kubernetes.Clientset, v *v1alpha1.Vector) (ctrl.Result, error) { log := log.FromContext(ctx).WithValues("Vector", v.Name) // Init Controller for Vector Agent vaCtrl := vectoragent.NewController(v, client, clientset) @@ -189,7 +189,7 @@ func (r *VectorReconciler) createOrUpdateVector(ctx context.Context, client clie } // Get Config in Json ([]byte) - byteConfig, err := config.BuildAgentConfig(config.VectorConfigParams{ + cfg, byteConfig, err := config.BuildAgentConfig(config.VectorConfigParams{ ApiEnabled: vaCtrl.Vector.Spec.Agent.Api.Enabled, PlaygroundEnabled: vaCtrl.Vector.Spec.Agent.Api.Playground, UseApiServerCache: vaCtrl.Vector.Spec.UseApiServerCache, @@ -231,14 +231,15 @@ func (r *VectorReconciler) createOrUpdateVector(ctx context.Context, client clie } } - vaCtrl.Config = byteConfig + vaCtrl.ByteConfig = byteConfig + vaCtrl.Config = cfg // Start Reconcile Vector Agent - if err := vaCtrl.EnsureVectorAgent(ctx, configOnly); err != nil { + if err := vaCtrl.EnsureVectorAgent(ctx); err != nil { return ctrl.Result{}, err } - if err := vaCtrl.SetSuccessStatus(ctx, &cfgHash); err != nil { + if err := vaCtrl.SetSuccessStatus(ctx, &cfgHash, cfg.GetGlobalConfigHash()); err != nil { // TODO: Handle err: Operation cannot be fulfilled on vectors.observability.kaasops.io \"vector-sample\": the object has been modified; please apply your changes to the latest version and try again if api_errors.IsConflict(err) { return ctrl.Result{}, err diff --git a/internal/controller/vectoraggregator_controller.go b/internal/controller/vectoraggregator_controller.go index 2ac911db..e5296867 100644 --- a/internal/controller/vectoraggregator_controller.go +++ b/internal/controller/vectoraggregator_controller.go @@ -244,7 +244,7 @@ func (r *VectorAggregatorReconciler) createOrUpdateVectorAggregator(ctx context. return ctrl.Result{}, err } - if err := vaCtrl.SetSuccessStatus(ctx, &cfgHash); err != nil { + if err := vaCtrl.SetSuccessStatus(ctx, &cfgHash, vaCtrl.Config.GetGlobalConfigHash()); err != nil { return ctrl.Result{}, err } diff --git a/internal/utils/compression/compression.go b/internal/utils/compression/compression.go index 5b19fe75..cae4b025 100644 --- a/internal/utils/compression/compression.go +++ b/internal/utils/compression/compression.go @@ -20,28 +20,3 @@ func Compress(data []byte, log logr.Logger) []byte { return b.Bytes() } - -func Decompress(data []byte, log logr.Logger) []byte { - if len(data) == 0 { - return []byte{} - } - - reader := bytes.NewReader(data) - gz, err := gzip.NewReader(reader) - if err != nil { - log.Error(err, "Failed to create gzip reader for decompress") - return nil - } - defer func() { - if err := gz.Close(); err != nil { - log.Error(err, "Failed to close reader for decompress") - } - }() - - var result bytes.Buffer - if _, err := result.ReadFrom(gz); err != nil { - log.Error(err, "Failed to read decompressed data") - return nil - } - return result.Bytes() -} diff --git a/internal/utils/compression/compression_test.go b/internal/utils/compression/compression_test.go deleted file mode 100644 index d7e9a508..00000000 --- a/internal/utils/compression/compression_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package compression - -import ( - "bytes" - "testing" - - "github.com/go-logr/logr" - "github.com/go-logr/zapr" - "go.uber.org/zap" -) - -type testCase struct { - name string - data []byte -} - -var testCases = []testCase{ - { - name: "case1", - data: []byte("Hello, World"), - }, - { - name: "caseEmpty", - data: []byte(""), - }, - { - name: "caseSpecialCharacters", - data: []byte("!@#$%^&*()"), - }, -} - -func TestCompressAndDecompress(t *testing.T) { - var log logr.Logger - zapLog, _ := zap.NewProduction() - defer zapLog.Sync() - log = zapr.NewLogger(zapLog) - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - compressed := Compress(tc.data, log) - decompressed := Decompress(compressed, log) - - if !bytes.Equal(tc.data, decompressed) { - t.Errorf("Compression and Decompression failed: expected %s, got %s", tc.data, decompressed) - } - }) - } -} - -func TestDecompressInvalidInput(t *testing.T) { - var log logr.Logger - zapLog, _ := zap.NewProduction() - defer zapLog.Sync() - log = zapr.NewLogger(zapLog) - - invalidCompressedData := []byte("wrong data") - - decompressed := Decompress(invalidCompressedData, log) - - if decompressed != nil { - t.Errorf("Decompression should have failed and returned nil") - } -} diff --git a/internal/vector/aggregator/config.go b/internal/vector/aggregator/config.go index 0e7d91b7..d0593bb6 100644 --- a/internal/vector/aggregator/config.go +++ b/internal/vector/aggregator/config.go @@ -4,58 +4,20 @@ import ( "context" "github.com/kaasops/vector-operator/internal/utils/compression" "github.com/kaasops/vector-operator/internal/utils/k8s" - "gopkg.in/yaml.v2" corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/log" ) -type globalOptions struct { - ExpireMetricsSecs *int `yaml:"expire_metrics_secs,omitempty"` -} - -func (ctrl *Controller) ensureVectorAggregatorConfig(ctx context.Context) (bool, error) { +func (ctrl *Controller) ensureVectorAggregatorConfig(ctx context.Context) error { log := log.FromContext(ctx).WithValues(ctrl.prefix()+"vector-aggregator-secret", ctrl.Name) log.Info("start Reconcile Vector Aggregator Secret") vectorAggregatorSecret, err := ctrl.createVectorAggregatorConfig(ctx) if err != nil { - return false, err - } - - globalOptionsChanged := false - - var prevSecret corev1.Secret - key := types.NamespacedName{Namespace: vectorAggregatorSecret.Namespace, Name: vectorAggregatorSecret.Name} - if err = ctrl.Get(ctx, key, &prevSecret); err == nil { - // check that the global settings have not been changed - var prevConfig globalOptions - data := prevSecret.Data["config.json"] - if ctrl.Spec.CompressConfigFile { - data = compression.Decompress(data, log) - } - - err = yaml.Unmarshal(data, &prevConfig) - if err == nil { - var actualConfig globalOptions - err = yaml.Unmarshal(ctrl.ConfigBytes, &actualConfig) - if err == nil { - if actualConfig.ExpireMetricsSecs == nil && prevConfig.ExpireMetricsSecs != nil { - globalOptionsChanged = true - } - if actualConfig.ExpireMetricsSecs != nil && prevConfig.ExpireMetricsSecs == nil { - globalOptionsChanged = true - } - if actualConfig.ExpireMetricsSecs != nil && - prevConfig.ExpireMetricsSecs != nil && - *actualConfig.ExpireMetricsSecs != *prevConfig.ExpireMetricsSecs { - globalOptionsChanged = true - } - } - } + return err } - return globalOptionsChanged, k8s.CreateOrUpdateResource(ctx, vectorAggregatorSecret, ctrl.Client) + return k8s.CreateOrUpdateResource(ctx, vectorAggregatorSecret, ctrl.Client) } func (ctrl *Controller) createVectorAggregatorConfig(ctx context.Context) (*corev1.Secret, error) { diff --git a/internal/vector/aggregator/controller.go b/internal/vector/aggregator/controller.go index cd4ab0ff..ebb31cb5 100644 --- a/internal/vector/aggregator/controller.go +++ b/internal/vector/aggregator/controller.go @@ -84,8 +84,7 @@ func (ctrl *Controller) EnsureVectorAggregator(ctx context.Context) error { return err } - globalOptsChanged, err := ctrl.ensureVectorAggregatorConfig(ctx) - if err != nil { + if err = ctrl.ensureVectorAggregatorConfig(ctx); err != nil { return err } @@ -103,7 +102,7 @@ func (ctrl *Controller) EnsureVectorAggregator(ctx context.Context) error { } } - if err := ctrl.ensureVectorAggregatorDeployment(ctx, globalOptsChanged); err != nil { + if err := ctrl.ensureVectorAggregatorDeployment(ctx); err != nil { return err } @@ -254,11 +253,12 @@ func (ctrl *Controller) setDefault() { } } -func (ctrl *Controller) SetSuccessStatus(ctx context.Context, hash *uint32) error { +func (ctrl *Controller) SetSuccessStatus(ctx context.Context, hash, globCfgHash *uint32) error { var status = true ctrl.Status.ConfigCheckResult = &status ctrl.Status.Reason = nil ctrl.Status.LastAppliedConfigHash = hash + ctrl.Status.LastAppliedGlobalConfigHash = globCfgHash return k8s.UpdateStatus(ctx, ctrl.VectorAggregator, ctrl.Client) } @@ -320,3 +320,11 @@ func (ctrl *Controller) prefix() string { } return "" } + +func (ctrl *Controller) globalConfigChanged() bool { + globalCfgHash := ctrl.Config.GetGlobalConfigHash() + if ctrl.Status.LastAppliedGlobalConfigHash == nil { + return false + } + return *ctrl.Status.LastAppliedGlobalConfigHash != *globalCfgHash +} diff --git a/internal/vector/aggregator/deployment.go b/internal/vector/aggregator/deployment.go index 9d640ae4..805583fc 100644 --- a/internal/vector/aggregator/deployment.go +++ b/internal/vector/aggregator/deployment.go @@ -11,11 +11,11 @@ import ( "time" ) -func (ctrl *Controller) ensureVectorAggregatorDeployment(ctx context.Context, globalOptsChanged bool) error { +func (ctrl *Controller) ensureVectorAggregatorDeployment(ctx context.Context) error { log := log.FromContext(ctx).WithValues(ctrl.prefix()+"vector-aggregator-deployment", ctrl.Name) log.Info("start Reconcile Vector Aggregator Deployment") deployment := ctrl.createVectorAggregatorDeployment() - if globalOptsChanged { + if ctrl.globalConfigChanged() { // restart pods if deployment.Spec.Template.Annotations == nil { deployment.Spec.Template.Annotations = make(map[string]string) diff --git a/internal/vector/vectoragent/vectoragent.go b/internal/vector/vectoragent/vectoragent.go index 462b6332..08011bc4 100644 --- a/internal/vector/vectoragent/vectoragent.go +++ b/internal/vector/vectoragent/vectoragent.go @@ -18,8 +18,8 @@ package vectoragent import ( "context" - vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" + "github.com/kaasops/vector-operator/internal/config" "github.com/kaasops/vector-operator/internal/utils/k8s" "k8s.io/client-go/kubernetes" "sigs.k8s.io/controller-runtime/pkg/client" @@ -29,7 +29,8 @@ type Controller struct { client.Client Vector *vectorv1alpha1.Vector - Config []byte + ByteConfig []byte + Config *config.VectorConfig // Temp. Wait this issue - https://github.com/kubernetes-sigs/controller-runtime/issues/452 ClientSet *kubernetes.Clientset } @@ -44,11 +45,12 @@ func NewController(v *vectorv1alpha1.Vector, c client.Client, cs *kubernetes.Cli return ctrl } -func (ctrl *Controller) SetSuccessStatus(ctx context.Context, hash *uint32) error { +func (ctrl *Controller) SetSuccessStatus(ctx context.Context, cfgHash, globCfgHash *uint32) error { var status = true ctrl.Vector.Status.ConfigCheckResult = &status ctrl.Vector.Status.Reason = nil - ctrl.Vector.Status.LastAppliedConfigHash = hash + ctrl.Vector.Status.LastAppliedConfigHash = cfgHash + ctrl.Vector.Status.LastAppliedGlobalConfigHash = globCfgHash return k8s.UpdateStatus(ctx, ctrl.Vector, ctrl.Client) } diff --git a/internal/vector/vectoragent/vectoragent_config.go b/internal/vector/vectoragent/vectoragent_config.go index 7be7e6c4..ce6b1950 100644 --- a/internal/vector/vectoragent/vectoragent_config.go +++ b/internal/vector/vectoragent/vectoragent_config.go @@ -28,10 +28,10 @@ func (ctrl *Controller) createVectorAgentConfig(ctx context.Context) (*corev1.Se log := log.FromContext(ctx).WithValues("vector-agent-rbac", ctrl.Vector.Name) labels := ctrl.labelsForVectorAgent() annotations := ctrl.annotationsForVectorAgent() - var data = ctrl.Config + var data = ctrl.ByteConfig if ctrl.Vector.Spec.Agent.CompressConfigFile { - data = compression.Compress(ctrl.Config, log) + data = compression.Compress(data, log) } config := map[string][]byte{ "agent.json": data, diff --git a/internal/vector/vectoragent/vectoragent_controller.go b/internal/vector/vectoragent/vectoragent_controller.go index e5071feb..3b49c5b9 100644 --- a/internal/vector/vectoragent/vectoragent_controller.go +++ b/internal/vector/vectoragent/vectoragent_controller.go @@ -19,23 +19,15 @@ package vectoragent import ( "context" "github.com/kaasops/vector-operator/internal/common" - "github.com/kaasops/vector-operator/internal/utils/compression" "github.com/kaasops/vector-operator/internal/utils/k8s" monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" - "gopkg.in/yaml.v2" - corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/log" "time" ) -type globalOptions struct { - ExpireMetricsSecs *int `yaml:"expire_metrics_secs,omitempty"` -} - -func (ctrl *Controller) EnsureVectorAgent(ctx context.Context, configOnly bool) error { +func (ctrl *Controller) EnsureVectorAgent(ctx context.Context) error { log := log.FromContext(ctx).WithValues("vector-agent", ctrl.Vector.Name) log.Info("start Reconcile Vector Agent") @@ -44,31 +36,30 @@ func (ctrl *Controller) EnsureVectorAgent(ctx context.Context, configOnly bool) return err } - globalOptsChanged, err := ctrl.ensureVectorAgentConfig(ctx) - if err != nil { + if err = ctrl.ensureVectorAgentConfig(ctx); err != nil { return err } - if !configOnly { - if err := ctrl.ensureVectorAgentRBAC(ctx); err != nil { - return err - } - if ctrl.Vector.Spec.Agent.Api.Enabled { - if err := ctrl.ensureVectorAgentService(ctx); err != nil { - return err - } - } + if err := ctrl.ensureVectorAgentRBAC(ctx); err != nil { + return err + } - if ctrl.Vector.Spec.Agent.InternalMetrics && monitoringCRD { - if err := ctrl.ensureVectorAgentPodMonitor(ctx); err != nil { - return err - } + if ctrl.Vector.Spec.Agent.Api.Enabled { + if err := ctrl.ensureVectorAgentService(ctx); err != nil { + return err } + } - if err := ctrl.ensureVectorAgentDaemonSet(ctx, globalOptsChanged); err != nil { + if ctrl.Vector.Spec.Agent.InternalMetrics && monitoringCRD { + if err := ctrl.ensureVectorAgentPodMonitor(ctx); err != nil { return err } } + + if err := ctrl.ensureVectorAgentDaemonSet(ctx); err != nil { + return err + } + return nil } @@ -118,57 +109,26 @@ func (ctrl *Controller) ensureVectorAgentService(ctx context.Context) error { return k8s.CreateOrUpdateResource(ctx, vectorAgentService, ctrl.Client) } -func (ctrl *Controller) ensureVectorAgentConfig(ctx context.Context) (bool, error) { +func (ctrl *Controller) ensureVectorAgentConfig(ctx context.Context) error { log := log.FromContext(ctx).WithValues("vector-agent-secret", ctrl.Vector.Name) log.Info("start Reconcile Vector Agent Secret") vectorAgentSecret, err := ctrl.createVectorAgentConfig(ctx) if err != nil { - return false, err - } - - globalOptionsChanged := false - - var prevSecret corev1.Secret - key := types.NamespacedName{Namespace: vectorAgentSecret.Namespace, Name: vectorAgentSecret.Name} - if err = ctrl.Get(ctx, key, &prevSecret); err == nil { - // check that the global settings have not been changed - var prevConfig globalOptions - data := prevSecret.Data["agent.json"] - if ctrl.Vector.Spec.Agent.CompressConfigFile { - data = compression.Decompress(data, log) - } - err = yaml.Unmarshal(data, &prevConfig) - if err == nil { - var actualConfig globalOptions - err = yaml.Unmarshal(ctrl.Config, &actualConfig) - if err == nil { - if actualConfig.ExpireMetricsSecs == nil && prevConfig.ExpireMetricsSecs != nil { - globalOptionsChanged = true - } - if actualConfig.ExpireMetricsSecs != nil && prevConfig.ExpireMetricsSecs == nil { - globalOptionsChanged = true - } - if actualConfig.ExpireMetricsSecs != nil && - prevConfig.ExpireMetricsSecs != nil && - *actualConfig.ExpireMetricsSecs != *prevConfig.ExpireMetricsSecs { - globalOptionsChanged = true - } - } - } + return err } - return globalOptionsChanged, k8s.CreateOrUpdateResource(ctx, vectorAgentSecret, ctrl.Client) + return k8s.CreateOrUpdateResource(ctx, vectorAgentSecret, ctrl.Client) } -func (ctrl *Controller) ensureVectorAgentDaemonSet(ctx context.Context, globalOptsChanged bool) error { +func (ctrl *Controller) ensureVectorAgentDaemonSet(ctx context.Context) error { log := log.FromContext(ctx).WithValues("vector-agent-daemon-set", ctrl.Vector.Name) log.Info("start Reconcile Vector Agent DaemonSet") vectorAgentDaemonSet := ctrl.createVectorAgentDaemonSet() - if globalOptsChanged { + if ctrl.globalConfigChanged() { if vectorAgentDaemonSet.Spec.Template.Annotations == nil { vectorAgentDaemonSet.Spec.Template.Annotations = make(map[string]string) } @@ -228,3 +188,11 @@ func (ctrl *Controller) getControllerReference() []metav1.OwnerReference { }, } } + +func (ctrl *Controller) globalConfigChanged() bool { + globalCfgHash := ctrl.Config.GetGlobalConfigHash() + if ctrl.Vector.Status.LastAppliedGlobalConfigHash == nil { + return false + } + return *ctrl.Vector.Status.LastAppliedGlobalConfigHash != *globalCfgHash +} diff --git a/internal/vector/vectoragent/vectoragent_service.go b/internal/vector/vectoragent/vectoragent_service.go index 1d346274..f2a673f9 100644 --- a/internal/vector/vectoragent/vectoragent_service.go +++ b/internal/vector/vectoragent/vectoragent_service.go @@ -17,15 +17,12 @@ limitations under the License. package vectoragent import ( + "github.com/kaasops/vector-operator/internal/config" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/util/intstr" ) -const ( - ApiPort = 8686 -) - func (ctrl *Controller) createVectorAgentService() *corev1.Service { labels := ctrl.labelsForVectorAgent() annotations := ctrl.annotationsForVectorAgent() @@ -36,9 +33,9 @@ func (ctrl *Controller) createVectorAgentService() *corev1.Service { Ports: []corev1.ServicePort{ { Name: "api", - Protocol: corev1.Protocol("TCP"), - Port: ApiPort, - TargetPort: intstr.FromInt(ApiPort), + Protocol: "TCP", + Port: config.AgentApiPort, + TargetPort: intstr.FromInt(config.AgentApiPort), }, }, Selector: labels, From cc95b4aed52e7eb1c2293ac7f595b94164d2548c Mon Sep 17 00:00:00 2001 From: Vladimir <31961982+zvlb@users.noreply.github.com> Date: Fri, 24 Jan 2025 12:47:14 +0200 Subject: [PATCH 287/316] prepare v0.2.0 release (#172) Signed-off-by: zvlb --- api/v1alpha1/zz_generated.deepcopy.go | 5 + helm/charts/vector-operator/Chart.yaml | 4 +- ...y.kaasops.io_clustervectoraggregators.yaml | 13 ++- ...vability.kaasops.io_vectoraggregators.yaml | 13 ++- .../observability.kaasops.io_vectors.yaml | 13 ++- helm/index.yaml | 99 ++++++++++-------- helm/packages/vector-operator-0.5.tgz | Bin 0 -> 100587 bytes 7 files changed, 87 insertions(+), 60 deletions(-) create mode 100644 helm/packages/vector-operator-0.5.tgz diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index a74125d7..2f3f43c3 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -528,6 +528,11 @@ func (in *VectorCommonStatus) DeepCopyInto(out *VectorCommonStatus) { *out = new(uint32) **out = **in } + if in.LastAppliedGlobalConfigHash != nil { + in, out := &in.LastAppliedGlobalConfigHash, &out.LastAppliedGlobalConfigHash + *out = new(uint32) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VectorCommonStatus. diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index 6ab5a380..da2ba5b0 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: "0.4" +version: "0.5" # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.1.2" +appVersion: "v0.2.0" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectoraggregators.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectoraggregators.yaml index 59d03656..2b552658 100644 --- a/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectoraggregators.yaml +++ b/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectoraggregators.yaml @@ -2298,11 +2298,6 @@ spec: The directory used for persisting Vector state, such as on-disk buffers, file checkpoints, and more. Please make sure the Vector project has write permissions to this directory. https://vector.dev/docs/reference/configuration/global-options/#data_dir type: string - expireMetricsSecs: - description: |- - Vector will expire internal metrics that haven’t been emitted/updated in the configured interval (default 300 seconds). - https://vector.dev/docs/reference/configuration/global-options/#expire_metrics_secs - type: integer env: description: Env that will be added to Vector pod items: @@ -2433,6 +2428,11 @@ spec: format: int32 type: integer type: object + expireMetricsSecs: + description: |- + Vector will expire internal metrics that haven’t been emitted/updated in the configured interval (default 300 seconds). + https://vector.dev/docs/reference/configuration/global-options/#expire_metrics_secs + type: integer host_aliases: description: |- HostAliases provides mapping between ip and hostnames, @@ -4973,6 +4973,9 @@ spec: LastAppliedConfigHash: format: int32 type: integer + LastAppliedGlobalConfigHash: + format: int32 + type: integer configCheckResult: type: boolean reason: diff --git a/helm/charts/vector-operator/crds/observability.kaasops.io_vectoraggregators.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_vectoraggregators.yaml index 6ec8ced6..0e17eec4 100644 --- a/helm/charts/vector-operator/crds/observability.kaasops.io_vectoraggregators.yaml +++ b/helm/charts/vector-operator/crds/observability.kaasops.io_vectoraggregators.yaml @@ -2296,11 +2296,6 @@ spec: The directory used for persisting Vector state, such as on-disk buffers, file checkpoints, and more. Please make sure the Vector project has write permissions to this directory. https://vector.dev/docs/reference/configuration/global-options/#data_dir type: string - expireMetricsSecs: - description: |- - Vector will expire internal metrics that haven’t been emitted/updated in the configured interval (default 300 seconds). - https://vector.dev/docs/reference/configuration/global-options/#expire_metrics_secs - type: integer env: description: Env that will be added to Vector pod items: @@ -2431,6 +2426,11 @@ spec: format: int32 type: integer type: object + expireMetricsSecs: + description: |- + Vector will expire internal metrics that haven’t been emitted/updated in the configured interval (default 300 seconds). + https://vector.dev/docs/reference/configuration/global-options/#expire_metrics_secs + type: integer host_aliases: description: |- HostAliases provides mapping between ip and hostnames, @@ -4966,6 +4966,9 @@ spec: LastAppliedConfigHash: format: int32 type: integer + LastAppliedGlobalConfigHash: + format: int32 + type: integer configCheckResult: type: boolean reason: diff --git a/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml index d93655dc..26f1cda9 100644 --- a/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml +++ b/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml @@ -2314,11 +2314,6 @@ spec: The directory used for persisting Vector state, such as on-disk buffers, file checkpoints, and more. Please make sure the Vector project has write permissions to this directory. https://vector.dev/docs/reference/configuration/global-options/#data_dir type: string - expireMetricsSecs: - description: |- - Vector will expire internal metrics that haven’t been emitted/updated in the configured interval (default 300 seconds). - https://vector.dev/docs/reference/configuration/global-options/#expire_metrics_secs - type: integer env: description: Env that will be added to Vector pod items: @@ -2439,6 +2434,11 @@ spec: - name type: object type: array + expireMetricsSecs: + description: |- + Vector will expire internal metrics that haven’t been emitted/updated in the configured interval (default 300 seconds). + https://vector.dev/docs/reference/configuration/global-options/#expire_metrics_secs + type: integer host_aliases: description: |- HostAliases provides mapping between ip and hostnames, @@ -4985,6 +4985,9 @@ spec: LastAppliedConfigHash: format: int32 type: integer + LastAppliedGlobalConfigHash: + format: int32 + type: integer configCheckResult: type: boolean reason: diff --git a/helm/index.yaml b/helm/index.yaml index 52ae2971..c6aa162a 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.2.0 + created: "2025-01-24T12:45:43.817545+02:00" + description: A Helm chart to install Vector Operator + digest: f0e89cc2f3b641588e603107ba4aedc5f1ec585452c88bac46784226e56751e2 + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.5.tgz + version: "0.5" - apiVersion: v2 appVersion: v0.1.2 - created: "2025-01-13T16:40:57.735292+02:00" + created: "2025-01-24T12:45:43.814626+02:00" description: A Helm chart to install Vector Operator digest: e1fe0e96c146c7c275c181e727c8a60f21898cabe90629851a2920d2915f84b7 home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: "0.4" - apiVersion: v2 appVersion: v0.1.1 - created: "2025-01-13T16:40:57.733469+02:00" + created: "2025-01-24T12:45:43.812252+02:00" description: A Helm chart to install Vector Operator digest: a916c9e9f81bdbf9f734073fb453a6b67d7a724ed7ff4326d7884b136c103ce5 home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: "0.3" - apiVersion: v2 appVersion: v0.1.1 - created: "2025-01-13T16:40:57.73164+02:00" + created: "2025-01-24T12:45:43.810616+02:00" description: A Helm chart to install Vector Operator digest: 582d95c6f63134f6cd815bcb85adce5770e179de21a979ec76f91ab7d8531b45 home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: "0.2" - apiVersion: v2 appVersion: v0.1.1 - created: "2025-01-13T16:40:57.729673+02:00" + created: "2025-01-24T12:45:43.808481+02:00" description: A Helm chart to install Vector Operator digest: 3ac5a422f3f1861528f737a0fea077cad5f7b7516db3fe5d392c887d5d3459d5 home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: 0.1.1 - apiVersion: v2 appVersion: v0.1.0 - created: "2025-01-13T16:40:57.72751+02:00" + created: "2025-01-24T12:45:43.806382+02:00" description: A Helm chart to install Vector Operator digest: 191ec4f83f11541df19680ce220992c84fb10210f0d54a5acc29361ccfd787bb home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.1.0 - apiVersion: v2 appVersion: pre-v0.1.0-r1 - created: "2025-01-13T16:40:57.725159+02:00" + created: "2025-01-24T12:45:43.804482+02:00" description: A Helm chart to install Vector Operator digest: 01bd2e347c5782127511a0a0fbbab72508cbe667664f2c9b615cabb80a4c40c7 home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.1.0-rc1 - apiVersion: v2 appVersion: v0.0.40 - created: "2025-01-13T16:40:57.721203+02:00" + created: "2025-01-24T12:45:43.799763+02:00" description: A Helm chart to install Vector Operator digest: c50e673e811b8d4c03ad45c92ce23b9bc54eef4665091e70fab9a4b7e4c6c3f1 home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.0.40 - apiVersion: v2 appVersion: v0.0.39 - created: "2025-01-13T16:40:57.720297+02:00" + created: "2025-01-24T12:45:43.798734+02:00" description: A Helm chart to install Vector Operator digest: e1de38c869bf896bfb9f5f615c329d3ccce3bd91cb64bb6fa1c783a40ea290a2 home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.0.39 - apiVersion: v2 appVersion: v0.0.38 - created: "2025-01-13T16:40:57.719229+02:00" + created: "2025-01-24T12:45:43.797783+02:00" description: A Helm chart to install Vector Operator digest: 565b148184400900f5572b06ceebaac6340af5e5fd1122b308bdbfcbc2d2040a home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.0.38 - apiVersion: v2 appVersion: v0.0.37 - created: "2025-01-13T16:40:57.718075+02:00" + created: "2025-01-24T12:45:43.796838+02:00" description: A Helm chart to install Vector Operator digest: 65e10ee46e6855ba95f51e21ca14abc4411191b260c6b96ec72c775e73c1e331 home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.0.37 - apiVersion: v2 appVersion: v0.0.36 - created: "2025-01-13T16:40:57.716628+02:00" + created: "2025-01-24T12:45:43.795839+02:00" description: A Helm chart to install Vector Operator digest: 972b6b4048b6d17b0616786e49915ef52cb7c9573cfb6eb359b6c19b66eabe31 home: https://github.com/kaasops/vector-operator @@ -146,7 +159,7 @@ entries: version: 0.0.36 - apiVersion: v2 appVersion: v0.0.35 - created: "2025-01-13T16:40:57.715559+02:00" + created: "2025-01-24T12:45:43.794903+02:00" description: A Helm chart to install Vector Operator digest: d03ba759c42f2bd8d8f1df71702ee4f26a73b8bc28760ca7254af20d811cee8a home: https://github.com/kaasops/vector-operator @@ -159,7 +172,7 @@ entries: version: 0.0.35 - apiVersion: v2 appVersion: v0.0.34 - created: "2025-01-13T16:40:57.714656+02:00" + created: "2025-01-24T12:45:43.793995+02:00" description: A Helm chart to install Vector Operator digest: 01e9d488ee78c8603d821f96344edee568ebcb42049d586de37b8df39b372bd4 home: https://github.com/kaasops/vector-operator @@ -172,7 +185,7 @@ entries: version: 0.0.34 - apiVersion: v2 appVersion: v0.0.33 - created: "2025-01-13T16:40:57.713569+02:00" + created: "2025-01-24T12:45:43.793057+02:00" description: A Helm chart to install Vector Operator digest: fcde3c94a0fa6caa5f3d333226c95b7c85ede8489d46277e1222a868ed4ec8c3 home: https://github.com/kaasops/vector-operator @@ -185,7 +198,7 @@ entries: version: 0.0.33 - apiVersion: v2 appVersion: v0.0.32 - created: "2025-01-13T16:40:57.712382+02:00" + created: "2025-01-24T12:45:43.791828+02:00" description: A Helm chart to install Vector Operator digest: 26323037ec47f1703ea930a99ab4ec8fb93b44975ce969514ea68d4130017015 home: https://github.com/kaasops/vector-operator @@ -198,7 +211,7 @@ entries: version: 0.0.32 - apiVersion: v2 appVersion: v0.0.31 - created: "2025-01-13T16:40:57.711342+02:00" + created: "2025-01-24T12:45:43.79093+02:00" description: A Helm chart to install Vector Operator digest: 45b924c07a825e0f7cd3fb534a6ffd16604790d13be1aff59150c045474754e3 home: https://github.com/kaasops/vector-operator @@ -211,7 +224,7 @@ entries: version: 0.0.31 - apiVersion: v2 appVersion: v0.0.30 - created: "2025-01-13T16:40:57.710311+02:00" + created: "2025-01-24T12:45:43.7903+02:00" description: A Helm chart to install Vector Operator digest: 03beda549d15f50325028ea29af5f2065ac0b8adf3078bf7dc1312981aa5e7db home: https://github.com/kaasops/vector-operator @@ -224,7 +237,7 @@ entries: version: 0.0.30 - apiVersion: v2 appVersion: v0.0.29 - created: "2025-01-13T16:40:57.709291+02:00" + created: "2025-01-24T12:45:43.789174+02:00" description: A Helm chart to install Vector Operator digest: 0f025fc3a924b37b8c4131c4d8cfa437d2d4e557ab9476ed3e69a00232c7dca6 home: https://github.com/kaasops/vector-operator @@ -237,7 +250,7 @@ entries: version: 0.0.29 - apiVersion: v2 appVersion: v0.0.28 - created: "2025-01-13T16:40:57.70837+02:00" + created: "2025-01-24T12:45:43.788563+02:00" description: A Helm chart to install Vector Operator digest: af856d41314313e04f15e7143409a9c564c6ca610b0d2eaec3112add8573e668 home: https://github.com/kaasops/vector-operator @@ -250,7 +263,7 @@ entries: version: 0.0.28 - apiVersion: v2 appVersion: v0.0.27 - created: "2025-01-13T16:40:57.70743+02:00" + created: "2025-01-24T12:45:43.78765+02:00" description: A Helm chart to install Vector Operator digest: 631e2ff02bbd7f247cb486494fd2af60c57cc551066a6a3858226551bc1745a4 home: https://github.com/kaasops/vector-operator @@ -263,7 +276,7 @@ entries: version: 0.0.27 - apiVersion: v2 appVersion: v0.0.26 - created: "2025-01-13T16:40:57.706108+02:00" + created: "2025-01-24T12:45:43.786984+02:00" description: A Helm chart to install Vector Operator digest: 760a2833f4c1a33466982419b079ff18d996331ebacc40cf93b0f55229cdb7db home: https://github.com/kaasops/vector-operator @@ -276,7 +289,7 @@ entries: version: 0.0.26 - apiVersion: v2 appVersion: v0.0.25 - created: "2025-01-13T16:40:57.705173+02:00" + created: "2025-01-24T12:45:43.786014+02:00" description: A Helm chart to install Vector Operator digest: fd22b996b071b6d85740ccf76e85cb640fa717c2620748d206d3f4fdd44cbcc2 home: https://github.com/kaasops/vector-operator @@ -289,7 +302,7 @@ entries: version: 0.0.25 - apiVersion: v2 appVersion: v0.0.24 - created: "2025-01-13T16:40:57.704182+02:00" + created: "2025-01-24T12:45:43.78506+02:00" description: A Helm chart to install Vector Operator digest: ea257e60ecde063a0d1ed52ce5e3283245b8f0e2daba58ea3a5adb0ba82d7799 home: https://github.com/kaasops/vector-operator @@ -302,7 +315,7 @@ entries: version: 0.0.24 - apiVersion: v2 appVersion: v0.0.23 - created: "2025-01-13T16:40:57.703115+02:00" + created: "2025-01-24T12:45:43.78438+02:00" description: A Helm chart to install Vector Operator digest: 546d202b3b9263f789b88335263191098dfcabd5d8698105f37cad24d56a8ed0 home: https://github.com/kaasops/vector-operator @@ -315,7 +328,7 @@ entries: version: 0.0.23 - apiVersion: v2 appVersion: v0.0.22 - created: "2025-01-13T16:40:57.701907+02:00" + created: "2025-01-24T12:45:43.783193+02:00" description: A Helm chart to install Vector Operator digest: bf96ddc8ac61e9d6beb8bc763fbf3fa6025d950b29d70d80de6e8a0ea45e0411 home: https://github.com/kaasops/vector-operator @@ -328,7 +341,7 @@ entries: version: 0.0.22 - apiVersion: v2 appVersion: v0.0.21 - created: "2025-01-13T16:40:57.700841+02:00" + created: "2025-01-24T12:45:43.782232+02:00" description: A Helm chart to install Vector Operator digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d home: https://github.com/kaasops/vector-operator @@ -341,7 +354,7 @@ entries: version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2025-01-13T16:40:57.69978+02:00" + created: "2025-01-24T12:45:43.781237+02:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -354,7 +367,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2025-01-13T16:40:57.698863+02:00" + created: "2025-01-24T12:45:43.780657+02:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -367,7 +380,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2025-01-13T16:40:57.698401+02:00" + created: "2025-01-24T12:45:43.779948+02:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -380,7 +393,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2025-01-13T16:40:57.697925+02:00" + created: "2025-01-24T12:45:43.779348+02:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -393,7 +406,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2025-01-13T16:40:57.697448+02:00" + created: "2025-01-24T12:45:43.778797+02:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -406,7 +419,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2025-01-13T16:40:57.696808+02:00" + created: "2025-01-24T12:45:43.778389+02:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -419,7 +432,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2025-01-13T16:40:57.69605+02:00" + created: "2025-01-24T12:45:43.777912+02:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -432,7 +445,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2025-01-13T16:40:57.695578+02:00" + created: "2025-01-24T12:45:43.777297+02:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -445,7 +458,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2025-01-13T16:40:57.694874+02:00" + created: "2025-01-24T12:45:43.776764+02:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -458,7 +471,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2025-01-13T16:40:57.694212+02:00" + created: "2025-01-24T12:45:43.774668+02:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -471,7 +484,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2025-01-13T16:40:57.69374+02:00" + created: "2025-01-24T12:45:43.773999+02:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -484,7 +497,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2025-01-13T16:40:57.723132+02:00" + created: "2025-01-24T12:45:43.802383+02:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -497,7 +510,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2025-01-13T16:40:57.72277+02:00" + created: "2025-01-24T12:45:43.802027+02:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -510,7 +523,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2025-01-13T16:40:57.722156+02:00" + created: "2025-01-24T12:45:43.801492+02:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -523,7 +536,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2025-01-13T16:40:57.721709+02:00" + created: "2025-01-24T12:45:43.801043+02:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -536,7 +549,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2025-01-13T16:40:57.692998+02:00" + created: "2025-01-24T12:45:43.773443+02:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -547,4 +560,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2025-01-13T16:40:57.690577+02:00" +generated: "2025-01-24T12:45:43.772699+02:00" diff --git a/helm/packages/vector-operator-0.5.tgz b/helm/packages/vector-operator-0.5.tgz new file mode 100644 index 0000000000000000000000000000000000000000..c703919c7717180c05be39002da8a81d493ffd99 GIT binary patch literal 100587 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�YLC<7?$P?I+9evYcfLk>m}}j?B_$v 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%0DeOg2G6c1c35sehRrk3inWtBs-~>%zg8G}# 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 Date: Mon, 17 Feb 2025 10:15:25 +0000 Subject: [PATCH 288/316] FEATURE: Enable envFrom on agent (rebased to main) (#175) * rebase PR133 Signed-off-by: Robert van Veelen * Update specification * Fix typo --------- Signed-off-by: Robert van Veelen Co-authored-by: Christopher Forkner --- api/v1alpha1/vector_common_types.go | 2 + ...y.kaasops.io_clustervectoraggregators.yaml | 46 ++++++++++++++++++ ...vability.kaasops.io_vectoraggregators.yaml | 46 ++++++++++++++++++ .../observability.kaasops.io_vectors.yaml | 47 +++++++++++++++++++ docs/specification.md | 4 ++ .../vectoragent/vectoragent_daemonset.go | 9 +++- 6 files changed, 153 insertions(+), 1 deletion(-) diff --git a/api/v1alpha1/vector_common_types.go b/api/v1alpha1/vector_common_types.go index ff47e893..0ae12939 100644 --- a/api/v1alpha1/vector_common_types.go +++ b/api/v1alpha1/vector_common_types.go @@ -67,6 +67,8 @@ type VectorCommon struct { HostNetwork bool `json:"hostNetwork,omitempty"` // Env that will be added to Vector pod Env []v1.EnvVar `json:"env,omitempty"` + // Env that will be added to Vector pod from ConfigMap or Secret sources + EnvFrom []v1.EnvFromSource `json:"envFrom,omitempty"` // The directory used for persisting Vector state, such as on-disk buffers, file checkpoints, and more. Please make sure the Vector project has write permissions to this directory. // https://vector.dev/docs/reference/configuration/global-options/#data_dir DataDir string `json:"dataDir,omitempty"` diff --git a/config/crd/bases/observability.kaasops.io_clustervectoraggregators.yaml b/config/crd/bases/observability.kaasops.io_clustervectoraggregators.yaml index 2b552658..a4c871f2 100644 --- a/config/crd/bases/observability.kaasops.io_clustervectoraggregators.yaml +++ b/config/crd/bases/observability.kaasops.io_clustervectoraggregators.yaml @@ -2416,6 +2416,52 @@ spec: - name type: object type: array + envFrom: + description: Env that will be added to Vector pod from ConfigMap or + Secret sources + items: + description: EnvFromSource represents the source of a set of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: An optional identifier to prepend to each key in + the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array eventCollector: properties: image: diff --git a/config/crd/bases/observability.kaasops.io_vectoraggregators.yaml b/config/crd/bases/observability.kaasops.io_vectoraggregators.yaml index 0e17eec4..693d2c67 100644 --- a/config/crd/bases/observability.kaasops.io_vectoraggregators.yaml +++ b/config/crd/bases/observability.kaasops.io_vectoraggregators.yaml @@ -2414,6 +2414,52 @@ spec: - name type: object type: array + envFrom: + description: Env that will be added to Vector pod from ConfigMap or + Secret sources + items: + description: EnvFromSource represents the source of a set of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: An optional identifier to prepend to each key in + the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array eventCollector: properties: image: diff --git a/config/crd/bases/observability.kaasops.io_vectors.yaml b/config/crd/bases/observability.kaasops.io_vectors.yaml index 26f1cda9..8c19c4a9 100644 --- a/config/crd/bases/observability.kaasops.io_vectors.yaml +++ b/config/crd/bases/observability.kaasops.io_vectors.yaml @@ -2434,6 +2434,53 @@ spec: - name type: object type: array + envFrom: + description: Env that will be added to Vector pod from ConfigMap + or Secret sources + items: + description: EnvFromSource represents the source of a set of + ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: An optional identifier to prepend to each key + in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array expireMetricsSecs: description: |- Vector will expire internal metrics that haven’t been emitted/updated in the configured interval (default 300 seconds). diff --git a/docs/specification.md b/docs/specification.md index 6cde131c..25befd1b 100644 --- a/docs/specification.md +++ b/docs/specification.md @@ -98,6 +98,10 @@

+ + + +
agentagent image Image for Vector agent. timberio/vector:0.24.0-distroless-libc by default
dataDir DataDir for Vector Agent. `/vector-data-dir` by default
expireMetricsSecsExpireMetricsSecs 300 by default
api ApiSpecenv Env that will be added to Vector pod. By default - not set
envFromenvFrom that will be added to Vector pod. By default - not set
## Api Spec diff --git a/internal/vector/vectoragent/vectoragent_daemonset.go b/internal/vector/vectoragent/vectoragent_daemonset.go index f86706cd..eb17bea0 100644 --- a/internal/vector/vectoragent/vectoragent_daemonset.go +++ b/internal/vector/vectoragent/vectoragent_daemonset.go @@ -202,7 +202,7 @@ func (ctrl *Controller) generateVectorAgentEnvs() []corev1.EnvVar { } func (ctrl *Controller) VectorAgentContainer() *corev1.Container { - return &corev1.Container{ + container := &corev1.Container{ Name: ctrl.getNameVectorAgent(), Image: ctrl.Vector.Spec.Agent.Image, Args: []string{"--config-dir", "/etc/vector", "--watch-config"}, @@ -221,6 +221,13 @@ func (ctrl *Controller) VectorAgentContainer() *corev1.Container { SecurityContext: ctrl.Vector.Spec.Agent.ContainerSecurityContext, ImagePullPolicy: ctrl.Vector.Spec.Agent.ImagePullPolicy, } + + // Check if envFrom is provided and set it + if ctrl.Vector.Spec.Agent.EnvFrom != nil { + container.EnvFrom = ctrl.Vector.Spec.Agent.EnvFrom + } + + return container } func (ctrl *Controller) ConfigReloaderInitContainer() *corev1.Container { From d64270d63a33bc5e249b1a3f7a97d837bb009138 Mon Sep 17 00:00:00 2001 From: aa1ex <115736127+aa1ex@users.noreply.github.com> Date: Wed, 19 Feb 2025 09:49:22 +0200 Subject: [PATCH 289/316] envFrom for aggregator (#176) envFrom for aggregator Signed-off-by: Aleksandr Aleksandrov --- api/v1alpha1/zz_generated.deepcopy.go | 7 ++++ docs/secure-credential.md | 38 ++++++++++++++++++- internal/config/configcheck/configcheck.go | 2 + .../config/configcheck/configcheck_pod.go | 24 +++++++----- internal/vector/aggregator/deployment.go | 9 ++++- .../vectoragent/vectoragent_daemonset.go | 2 +- 6 files changed, 70 insertions(+), 12 deletions(-) diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 2f3f43c3..264c073b 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -466,6 +466,13 @@ func (in *VectorCommon) DeepCopyInto(out *VectorCommon) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.EnvFrom != nil { + in, out := &in.EnvFrom, &out.EnvFrom + *out = make([]v1.EnvFromSource, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } if in.ExpireMetricsSecs != nil { in, out := &in.ExpireMetricsSecs, &out.ExpireMetricsSecs *out = new(int) diff --git a/docs/secure-credential.md b/docs/secure-credential.md index 1fe68cdc..30a3767a 100644 --- a/docs/secure-credential.md +++ b/docs/secure-credential.md @@ -1,6 +1,42 @@ # Secure credential -If you want hidden credentials (like host/user/password for elasticsearch) you can use this scheme. +If you need to use sensitive credentials (such as host, username, or password for Elasticsearch), you can consider the following approaches: +- envFrom with secretRef (recommended) +- environment variables. + +## envFrom + +Create a secret: + +```yaml +apiVersion: v1 +kind: Secret +type: Opaque +metadata: + name: mysecret + namespace: vector +data: + ELASTIC_HOST: "base64_host" + ELASTIC_USER: "base64_user" + ELASTIC_PASSWORD: "base64_password" +``` + +Deploy CR Vector to Kubernetes by specifying a reference to the secret with Elastic parameters: +```yaml +apiVersion: observability.kaasops.io/v1alpha1 +kind: Vector +metadata: + name: example + namespace: vector +spec: + agent: + envFrom: + - secretRef: + name: mysecret +... +``` + +## Environment variables Deploy CR Vector to Kubernetes where set credentials for Elastic in ENVs: ```yaml diff --git a/internal/config/configcheck/configcheck.go b/internal/config/configcheck/configcheck.go index d6118ae5..f06f4683 100644 --- a/internal/config/configcheck/configcheck.go +++ b/internal/config/configcheck/configcheck.go @@ -51,6 +51,7 @@ type ConfigCheck struct { ImagePullPolicy corev1.PullPolicy ImagePullSecrets []corev1.LocalObjectReference Envs []corev1.EnvVar + EnvFrom []corev1.EnvFromSource Hash string Tolerations []corev1.Toleration Resources corev1.ResourceRequirements @@ -99,6 +100,7 @@ func New( ImagePullPolicy: vc.ImagePullPolicy, ImagePullSecrets: vc.ImagePullSecrets, Envs: env, + EnvFrom: vc.EnvFrom, Tolerations: tolerations, Resources: resources, SecurityContext: vc.SecurityContext, diff --git a/internal/config/configcheck/configcheck_pod.go b/internal/config/configcheck/configcheck_pod.go index eeb1bf40..ca58149a 100644 --- a/internal/config/configcheck/configcheck_pod.go +++ b/internal/config/configcheck/configcheck_pod.go @@ -30,6 +30,20 @@ func (cc *ConfigCheck) createVectorConfigCheckPod() *corev1.Pod { initContainers = append(initContainers, *cc.ConfigReloaderInitContainer()) } + container := corev1.Container{ + Name: "config-check", + Image: cc.Image, + Resources: cc.Resources, + Args: []string{"--require-healthy=false", "validate", "/etc/vector/*.json"}, + Env: cc.generateVectorConfigCheckEnvs(), + SecurityContext: cc.ContainerSecurityContext, + VolumeMounts: cc.generateVectorConfigCheckVolumeMounts(), + } + + if len(cc.EnvFrom) > 0 { + container.EnvFrom = cc.EnvFrom + } + pod := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: cc.getNameVectorConfigCheck(), @@ -45,15 +59,7 @@ func (cc *ConfigCheck) createVectorConfigCheckPod() *corev1.Pod { Tolerations: cc.Tolerations, InitContainers: initContainers, Containers: []corev1.Container{ - { - Name: "config-check", - Image: cc.Image, - Resources: cc.Resources, - Args: []string{"--require-healthy=false", "validate", "/etc/vector/*.json"}, - Env: cc.generateVectorConfigCheckEnvs(), - SecurityContext: cc.ContainerSecurityContext, - VolumeMounts: cc.generateVectorConfigCheckVolumeMounts(), - }, + container, }, RestartPolicy: "Never", }, diff --git a/internal/vector/aggregator/deployment.go b/internal/vector/aggregator/deployment.go index 805583fc..7006d290 100644 --- a/internal/vector/aggregator/deployment.go +++ b/internal/vector/aggregator/deployment.go @@ -70,7 +70,7 @@ func (ctrl *Controller) createVectorAggregatorDeployment() *appsv1.Deployment { } func (ctrl *Controller) VectorAggregatorContainer() *corev1.Container { - return &corev1.Container{ + container := &corev1.Container{ Name: ctrl.getNameVectorAggregator(), Image: ctrl.Spec.Image, Args: []string{"--config-dir", "/etc/vector", "--watch-config"}, @@ -89,6 +89,13 @@ func (ctrl *Controller) VectorAggregatorContainer() *corev1.Container { SecurityContext: ctrl.Spec.ContainerSecurityContext, ImagePullPolicy: ctrl.Spec.ImagePullPolicy, } + + // Check if envFrom is provided and set it + if len(ctrl.Spec.EnvFrom) > 0 { + container.EnvFrom = ctrl.Spec.EnvFrom + } + + return container } func (ctrl *Controller) ConfigReloaderInitContainer() *corev1.Container { diff --git a/internal/vector/vectoragent/vectoragent_daemonset.go b/internal/vector/vectoragent/vectoragent_daemonset.go index eb17bea0..3c0890da 100644 --- a/internal/vector/vectoragent/vectoragent_daemonset.go +++ b/internal/vector/vectoragent/vectoragent_daemonset.go @@ -223,7 +223,7 @@ func (ctrl *Controller) VectorAgentContainer() *corev1.Container { } // Check if envFrom is provided and set it - if ctrl.Vector.Spec.Agent.EnvFrom != nil { + if len(ctrl.Vector.Spec.Agent.EnvFrom) > 0 { container.EnvFrom = ctrl.Vector.Spec.Agent.EnvFrom } From 88c46b3cf7e92e1d2862d62622c740a28cddfdbb Mon Sep 17 00:00:00 2001 From: aa1ex <115736127+aa1ex@users.noreply.github.com> Date: Wed, 19 Feb 2025 10:02:30 +0200 Subject: [PATCH 290/316] Prepare v0.3.0 release Signed-off-by: Aleksandr Aleksandrov --- helm/charts/vector-operator/Chart.yaml | 4 +- ...y.kaasops.io_clustervectoraggregators.yaml | 46 ++++++++ ...vability.kaasops.io_vectoraggregators.yaml | 46 ++++++++ .../observability.kaasops.io_vectors.yaml | 47 ++++++++ helm/index.yaml | 101 ++++++++++-------- helm/packages/vector-operator-0.6.tgz | Bin 0 -> 102220 bytes 6 files changed, 198 insertions(+), 46 deletions(-) create mode 100644 helm/packages/vector-operator-0.6.tgz diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index da2ba5b0..57498177 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: "0.5" +version: "0.6" # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.2.0" +appVersion: "v0.3.0" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectoraggregators.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectoraggregators.yaml index 2b552658..a4c871f2 100644 --- a/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectoraggregators.yaml +++ b/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectoraggregators.yaml @@ -2416,6 +2416,52 @@ spec: - name type: object type: array + envFrom: + description: Env that will be added to Vector pod from ConfigMap or + Secret sources + items: + description: EnvFromSource represents the source of a set of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: An optional identifier to prepend to each key in + the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array eventCollector: properties: image: diff --git a/helm/charts/vector-operator/crds/observability.kaasops.io_vectoraggregators.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_vectoraggregators.yaml index 0e17eec4..693d2c67 100644 --- a/helm/charts/vector-operator/crds/observability.kaasops.io_vectoraggregators.yaml +++ b/helm/charts/vector-operator/crds/observability.kaasops.io_vectoraggregators.yaml @@ -2414,6 +2414,52 @@ spec: - name type: object type: array + envFrom: + description: Env that will be added to Vector pod from ConfigMap or + Secret sources + items: + description: EnvFromSource represents the source of a set of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: An optional identifier to prepend to each key in + the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array eventCollector: properties: image: diff --git a/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml index 26f1cda9..8c19c4a9 100644 --- a/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml +++ b/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml @@ -2434,6 +2434,53 @@ spec: - name type: object type: array + envFrom: + description: Env that will be added to Vector pod from ConfigMap + or Secret sources + items: + description: EnvFromSource represents the source of a set of + ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: An optional identifier to prepend to each key + in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array expireMetricsSecs: description: |- Vector will expire internal metrics that haven’t been emitted/updated in the configured interval (default 300 seconds). diff --git a/helm/index.yaml b/helm/index.yaml index c6aa162a..bb6e14c0 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.3.0 + created: "2025-02-19T09:56:20.757703+02:00" + description: A Helm chart to install Vector Operator + digest: 69262a286e22bfbf7571297f05d17dfc8f19e6215faa42f4dbdabea8a5610586 + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.6.tgz + version: "0.6" - apiVersion: v2 appVersion: v0.2.0 - created: "2025-01-24T12:45:43.817545+02:00" + created: "2025-02-19T09:56:20.756106+02:00" description: A Helm chart to install Vector Operator digest: f0e89cc2f3b641588e603107ba4aedc5f1ec585452c88bac46784226e56751e2 home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: "0.5" - apiVersion: v2 appVersion: v0.1.2 - created: "2025-01-24T12:45:43.814626+02:00" + created: "2025-02-19T09:56:20.753922+02:00" description: A Helm chart to install Vector Operator digest: e1fe0e96c146c7c275c181e727c8a60f21898cabe90629851a2920d2915f84b7 home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: "0.4" - apiVersion: v2 appVersion: v0.1.1 - created: "2025-01-24T12:45:43.812252+02:00" + created: "2025-02-19T09:56:20.751379+02:00" description: A Helm chart to install Vector Operator digest: a916c9e9f81bdbf9f734073fb453a6b67d7a724ed7ff4326d7884b136c103ce5 home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: "0.3" - apiVersion: v2 appVersion: v0.1.1 - created: "2025-01-24T12:45:43.810616+02:00" + created: "2025-02-19T09:56:20.749599+02:00" description: A Helm chart to install Vector Operator digest: 582d95c6f63134f6cd815bcb85adce5770e179de21a979ec76f91ab7d8531b45 home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: "0.2" - apiVersion: v2 appVersion: v0.1.1 - created: "2025-01-24T12:45:43.808481+02:00" + created: "2025-02-19T09:56:20.747632+02:00" description: A Helm chart to install Vector Operator digest: 3ac5a422f3f1861528f737a0fea077cad5f7b7516db3fe5d392c887d5d3459d5 home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: 0.1.1 - apiVersion: v2 appVersion: v0.1.0 - created: "2025-01-24T12:45:43.806382+02:00" + created: "2025-02-19T09:56:20.745628+02:00" description: A Helm chart to install Vector Operator digest: 191ec4f83f11541df19680ce220992c84fb10210f0d54a5acc29361ccfd787bb home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.1.0 - apiVersion: v2 appVersion: pre-v0.1.0-r1 - created: "2025-01-24T12:45:43.804482+02:00" + created: "2025-02-19T09:56:20.74337+02:00" description: A Helm chart to install Vector Operator digest: 01bd2e347c5782127511a0a0fbbab72508cbe667664f2c9b615cabb80a4c40c7 home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.1.0-rc1 - apiVersion: v2 appVersion: v0.0.40 - created: "2025-01-24T12:45:43.799763+02:00" + created: "2025-02-19T09:56:20.739263+02:00" description: A Helm chart to install Vector Operator digest: c50e673e811b8d4c03ad45c92ce23b9bc54eef4665091e70fab9a4b7e4c6c3f1 home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.0.40 - apiVersion: v2 appVersion: v0.0.39 - created: "2025-01-24T12:45:43.798734+02:00" + created: "2025-02-19T09:56:20.738282+02:00" description: A Helm chart to install Vector Operator digest: e1de38c869bf896bfb9f5f615c329d3ccce3bd91cb64bb6fa1c783a40ea290a2 home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.0.39 - apiVersion: v2 appVersion: v0.0.38 - created: "2025-01-24T12:45:43.797783+02:00" + created: "2025-02-19T09:56:20.737032+02:00" description: A Helm chart to install Vector Operator digest: 565b148184400900f5572b06ceebaac6340af5e5fd1122b308bdbfcbc2d2040a home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.0.38 - apiVersion: v2 appVersion: v0.0.37 - created: "2025-01-24T12:45:43.796838+02:00" + created: "2025-02-19T09:56:20.736017+02:00" description: A Helm chart to install Vector Operator digest: 65e10ee46e6855ba95f51e21ca14abc4411191b260c6b96ec72c775e73c1e331 home: https://github.com/kaasops/vector-operator @@ -146,7 +159,7 @@ entries: version: 0.0.37 - apiVersion: v2 appVersion: v0.0.36 - created: "2025-01-24T12:45:43.795839+02:00" + created: "2025-02-19T09:56:20.735035+02:00" description: A Helm chart to install Vector Operator digest: 972b6b4048b6d17b0616786e49915ef52cb7c9573cfb6eb359b6c19b66eabe31 home: https://github.com/kaasops/vector-operator @@ -159,7 +172,7 @@ entries: version: 0.0.36 - apiVersion: v2 appVersion: v0.0.35 - created: "2025-01-24T12:45:43.794903+02:00" + created: "2025-02-19T09:56:20.733883+02:00" description: A Helm chart to install Vector Operator digest: d03ba759c42f2bd8d8f1df71702ee4f26a73b8bc28760ca7254af20d811cee8a home: https://github.com/kaasops/vector-operator @@ -172,7 +185,7 @@ entries: version: 0.0.35 - apiVersion: v2 appVersion: v0.0.34 - created: "2025-01-24T12:45:43.793995+02:00" + created: "2025-02-19T09:56:20.732656+02:00" description: A Helm chart to install Vector Operator digest: 01e9d488ee78c8603d821f96344edee568ebcb42049d586de37b8df39b372bd4 home: https://github.com/kaasops/vector-operator @@ -185,7 +198,7 @@ entries: version: 0.0.34 - apiVersion: v2 appVersion: v0.0.33 - created: "2025-01-24T12:45:43.793057+02:00" + created: "2025-02-19T09:56:20.731639+02:00" description: A Helm chart to install Vector Operator digest: fcde3c94a0fa6caa5f3d333226c95b7c85ede8489d46277e1222a868ed4ec8c3 home: https://github.com/kaasops/vector-operator @@ -198,7 +211,7 @@ entries: version: 0.0.33 - apiVersion: v2 appVersion: v0.0.32 - created: "2025-01-24T12:45:43.791828+02:00" + created: "2025-02-19T09:56:20.730624+02:00" description: A Helm chart to install Vector Operator digest: 26323037ec47f1703ea930a99ab4ec8fb93b44975ce969514ea68d4130017015 home: https://github.com/kaasops/vector-operator @@ -211,7 +224,7 @@ entries: version: 0.0.32 - apiVersion: v2 appVersion: v0.0.31 - created: "2025-01-24T12:45:43.79093+02:00" + created: "2025-02-19T09:56:20.729509+02:00" description: A Helm chart to install Vector Operator digest: 45b924c07a825e0f7cd3fb534a6ffd16604790d13be1aff59150c045474754e3 home: https://github.com/kaasops/vector-operator @@ -224,7 +237,7 @@ entries: version: 0.0.31 - apiVersion: v2 appVersion: v0.0.30 - created: "2025-01-24T12:45:43.7903+02:00" + created: "2025-02-19T09:56:20.728294+02:00" description: A Helm chart to install Vector Operator digest: 03beda549d15f50325028ea29af5f2065ac0b8adf3078bf7dc1312981aa5e7db home: https://github.com/kaasops/vector-operator @@ -237,7 +250,7 @@ entries: version: 0.0.30 - apiVersion: v2 appVersion: v0.0.29 - created: "2025-01-24T12:45:43.789174+02:00" + created: "2025-02-19T09:56:20.727336+02:00" description: A Helm chart to install Vector Operator digest: 0f025fc3a924b37b8c4131c4d8cfa437d2d4e557ab9476ed3e69a00232c7dca6 home: https://github.com/kaasops/vector-operator @@ -250,7 +263,7 @@ entries: version: 0.0.29 - apiVersion: v2 appVersion: v0.0.28 - created: "2025-01-24T12:45:43.788563+02:00" + created: "2025-02-19T09:56:20.726352+02:00" description: A Helm chart to install Vector Operator digest: af856d41314313e04f15e7143409a9c564c6ca610b0d2eaec3112add8573e668 home: https://github.com/kaasops/vector-operator @@ -263,7 +276,7 @@ entries: version: 0.0.28 - apiVersion: v2 appVersion: v0.0.27 - created: "2025-01-24T12:45:43.78765+02:00" + created: "2025-02-19T09:56:20.725119+02:00" description: A Helm chart to install Vector Operator digest: 631e2ff02bbd7f247cb486494fd2af60c57cc551066a6a3858226551bc1745a4 home: https://github.com/kaasops/vector-operator @@ -276,7 +289,7 @@ entries: version: 0.0.27 - apiVersion: v2 appVersion: v0.0.26 - created: "2025-01-24T12:45:43.786984+02:00" + created: "2025-02-19T09:56:20.724135+02:00" description: A Helm chart to install Vector Operator digest: 760a2833f4c1a33466982419b079ff18d996331ebacc40cf93b0f55229cdb7db home: https://github.com/kaasops/vector-operator @@ -289,7 +302,7 @@ entries: version: 0.0.26 - apiVersion: v2 appVersion: v0.0.25 - created: "2025-01-24T12:45:43.786014+02:00" + created: "2025-02-19T09:56:20.723163+02:00" description: A Helm chart to install Vector Operator digest: fd22b996b071b6d85740ccf76e85cb640fa717c2620748d206d3f4fdd44cbcc2 home: https://github.com/kaasops/vector-operator @@ -302,7 +315,7 @@ entries: version: 0.0.25 - apiVersion: v2 appVersion: v0.0.24 - created: "2025-01-24T12:45:43.78506+02:00" + created: "2025-02-19T09:56:20.722095+02:00" description: A Helm chart to install Vector Operator digest: ea257e60ecde063a0d1ed52ce5e3283245b8f0e2daba58ea3a5adb0ba82d7799 home: https://github.com/kaasops/vector-operator @@ -315,7 +328,7 @@ entries: version: 0.0.24 - apiVersion: v2 appVersion: v0.0.23 - created: "2025-01-24T12:45:43.78438+02:00" + created: "2025-02-19T09:56:20.720591+02:00" description: A Helm chart to install Vector Operator digest: 546d202b3b9263f789b88335263191098dfcabd5d8698105f37cad24d56a8ed0 home: https://github.com/kaasops/vector-operator @@ -328,7 +341,7 @@ entries: version: 0.0.23 - apiVersion: v2 appVersion: v0.0.22 - created: "2025-01-24T12:45:43.783193+02:00" + created: "2025-02-19T09:56:20.719567+02:00" description: A Helm chart to install Vector Operator digest: bf96ddc8ac61e9d6beb8bc763fbf3fa6025d950b29d70d80de6e8a0ea45e0411 home: https://github.com/kaasops/vector-operator @@ -341,7 +354,7 @@ entries: version: 0.0.22 - apiVersion: v2 appVersion: v0.0.21 - created: "2025-01-24T12:45:43.782232+02:00" + created: "2025-02-19T09:56:20.718502+02:00" description: A Helm chart to install Vector Operator digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d home: https://github.com/kaasops/vector-operator @@ -354,7 +367,7 @@ entries: version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2025-01-24T12:45:43.781237+02:00" + created: "2025-02-19T09:56:20.717257+02:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -367,7 +380,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2025-01-24T12:45:43.780657+02:00" + created: "2025-02-19T09:56:20.716622+02:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -380,7 +393,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2025-01-24T12:45:43.779948+02:00" + created: "2025-02-19T09:56:20.716172+02:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -393,7 +406,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2025-01-24T12:45:43.779348+02:00" + created: "2025-02-19T09:56:20.715718+02:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -406,7 +419,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2025-01-24T12:45:43.778797+02:00" + created: "2025-02-19T09:56:20.715263+02:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -419,7 +432,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2025-01-24T12:45:43.778389+02:00" + created: "2025-02-19T09:56:20.714213+02:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -432,7 +445,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2025-01-24T12:45:43.777912+02:00" + created: "2025-02-19T09:56:20.713442+02:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -445,7 +458,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2025-01-24T12:45:43.777297+02:00" + created: "2025-02-19T09:56:20.712934+02:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -458,7 +471,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2025-01-24T12:45:43.776764+02:00" + created: "2025-02-19T09:56:20.712257+02:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -471,7 +484,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2025-01-24T12:45:43.774668+02:00" + created: "2025-02-19T09:56:20.711541+02:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -484,7 +497,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2025-01-24T12:45:43.773999+02:00" + created: "2025-02-19T09:56:20.711063+02:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -497,7 +510,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2025-01-24T12:45:43.802383+02:00" + created: "2025-02-19T09:56:20.741113+02:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -510,7 +523,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2025-01-24T12:45:43.802027+02:00" + created: "2025-02-19T09:56:20.740763+02:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -523,7 +536,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2025-01-24T12:45:43.801492+02:00" + created: "2025-02-19T09:56:20.740302+02:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -536,7 +549,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2025-01-24T12:45:43.801043+02:00" + created: "2025-02-19T09:56:20.739807+02:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -549,7 +562,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2025-01-24T12:45:43.773443+02:00" + created: "2025-02-19T09:56:20.710372+02:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -560,4 +573,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2025-01-24T12:45:43.772699+02:00" +generated: "2025-02-19T09:56:20.708875+02:00" diff --git a/helm/packages/vector-operator-0.6.tgz b/helm/packages/vector-operator-0.6.tgz new file mode 100644 index 0000000000000000000000000000000000000000..de9d7729cc7666655e9f16267e55bcbd9446599e GIT binary patch literal 102220 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

#ae0V|GD$7Ty0mRutl|L!aK>?y6bZ2I-ZH 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

I;?`FB1=u8`yvr~iPxoig5~PUKJ~)UH@UxzT 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 literal 0 HcmV?d00001 From 9723fbfa57ff703122e7673ba803751d87caf087 Mon Sep 17 00:00:00 2001 From: Andrei Makarov <49369886+P0lskay@users.noreply.github.com> Date: Tue, 18 Mar 2025 11:26:48 +0300 Subject: [PATCH 291/316] Fix specification in docs for vector spec (#179) --- docs/specification.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/specification.md b/docs/specification.md index 25befd1b..27dcee54 100644 --- a/docs/specification.md +++ b/docs/specification.md @@ -10,7 +10,7 @@ # Vector Spec - + From e08e31b24a660fc86192d7f19bf06c7ff0f25e40 Mon Sep 17 00:00:00 2001 From: "an.makarov" Date: Sat, 12 Apr 2025 17:32:36 +0300 Subject: [PATCH 292/316] Add the ability to define custom labels for ConfigCheck pod Fixes #182 Update labels definition for all resources Fixes #182 Update manifests and code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations Update documentation Add individual method for matchLabels Update description for labels field in CRDs Fix specification.md Update CRDs in helm chart Create new function MergeLabels for merging of labels :) --- api/v1alpha1/vector_common_types.go | 6 ++++++ api/v1alpha1/zz_generated.deepcopy.go | 14 +++++++++++++ ...y.kaasops.io_clustervectoraggregators.yaml | 12 +++++++++++ ...vability.kaasops.io_vectoraggregators.yaml | 12 +++++++++++ .../observability.kaasops.io_vectors.yaml | 12 +++++++++++ docs/specification.md | 10 +++++++++- ...y.kaasops.io_clustervectoraggregators.yaml | 12 +++++++++++ ...vability.kaasops.io_vectoraggregators.yaml | 12 +++++++++++ .../observability.kaasops.io_vectors.yaml | 12 +++++++++++ internal/config/configcheck/configcheck.go | 14 +++++++++---- .../config/configcheck/configcheck_config.go | 2 +- .../config/configcheck/configcheck_pod.go | 2 +- .../config/configcheck/configcheck_rbac.go | 2 +- internal/utils/k8s/label.go | 20 +++++++++++++++++++ internal/vector/aggregator/controller.go | 10 +++++++++- internal/vector/aggregator/deployment.go | 6 ++++-- internal/vector/aggregator/podmonitor.go | 4 +++- internal/vector/aggregator/service.go | 8 +++++--- .../vectoragent/vectoragent_controller.go | 16 +++++++++++++-- .../vectoragent/vectoragent_daemonset.go | 3 ++- .../vectoragent/vectoragent_podmonitor.go | 3 ++- .../vector/vectoragent/vectoragent_service.go | 3 ++- 22 files changed, 175 insertions(+), 20 deletions(-) diff --git a/api/v1alpha1/vector_common_types.go b/api/v1alpha1/vector_common_types.go index 0ae12939..c1cc3c1f 100644 --- a/api/v1alpha1/vector_common_types.go +++ b/api/v1alpha1/vector_common_types.go @@ -24,6 +24,9 @@ type VectorCommon struct { // Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. // +optional Annotations map[string]string `json:"annotations,omitempty"` + // Labels is additional labels that will be added to Vector pod, service, podmonitor, etc. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels + // +optional + Labels map[string]string `json:"labels,omitempty"` // Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ // if not specified - default setting will be used // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Resources",xDescriptors="urn:alm:descriptor:com.tectonic.ui:resourceRequirements" @@ -132,6 +135,9 @@ type ConfigCheck struct { // Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. // +optional Annotations map[string]string `json:"annotations,omitempty"` + // Labels is additional labels that will be added to ConfigCheck pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels + // +optional + Labels map[string]string `json:"labels,omitempty"` } type VectorSelectorSpec struct { diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 264c073b..88f3346a 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -226,6 +226,13 @@ func (in *ConfigCheck) DeepCopyInto(out *ConfigCheck) { (*out)[key] = val } } + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigCheck. @@ -424,6 +431,13 @@ func (in *VectorCommon) DeepCopyInto(out *VectorCommon) { (*out)[key] = val } } + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } in.Resources.DeepCopyInto(&out.Resources) if in.Affinity != nil { in, out := &in.Affinity, &out.Affinity diff --git a/config/crd/bases/observability.kaasops.io_clustervectoraggregators.yaml b/config/crd/bases/observability.kaasops.io_clustervectoraggregators.yaml index a4c871f2..d56d8627 100644 --- a/config/crd/bases/observability.kaasops.io_clustervectoraggregators.yaml +++ b/config/crd/bases/observability.kaasops.io_clustervectoraggregators.yaml @@ -1938,6 +1938,12 @@ spec: Image - docker image settings for Vector Agent if no specified operator uses default config version type: string + labels: + additionalProperties: + type: string + description: 'Labels is additional labels that will be added to + ConfigCheck pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels' + type: object resources: description: |- Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ @@ -2539,6 +2545,12 @@ spec: internalMetrics: description: Enable internal metrics exporter type: boolean + labels: + additionalProperties: + type: string + description: 'Labels is additional labels that will be added to Vector + pod, service, podmonitor, etc. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels' + type: object livenessProbe: description: Periodic probe of container liveness. Container will be restarted if the probe fails. diff --git a/config/crd/bases/observability.kaasops.io_vectoraggregators.yaml b/config/crd/bases/observability.kaasops.io_vectoraggregators.yaml index 693d2c67..64976644 100644 --- a/config/crd/bases/observability.kaasops.io_vectoraggregators.yaml +++ b/config/crd/bases/observability.kaasops.io_vectoraggregators.yaml @@ -1936,6 +1936,12 @@ spec: Image - docker image settings for Vector Agent if no specified operator uses default config version type: string + labels: + additionalProperties: + type: string + description: 'Labels is additional labels that will be added to + ConfigCheck pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels' + type: object resources: description: |- Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ @@ -2537,6 +2543,12 @@ spec: internalMetrics: description: Enable internal metrics exporter type: boolean + labels: + additionalProperties: + type: string + description: 'Labels is additional labels that will be added to Vector + pod, service, podmonitor, etc. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels' + type: object livenessProbe: description: Periodic probe of container liveness. Container will be restarted if the probe fails. diff --git a/config/crd/bases/observability.kaasops.io_vectors.yaml b/config/crd/bases/observability.kaasops.io_vectors.yaml index 8c19c4a9..53577da1 100644 --- a/config/crd/bases/observability.kaasops.io_vectors.yaml +++ b/config/crd/bases/observability.kaasops.io_vectors.yaml @@ -1951,6 +1951,12 @@ spec: Image - docker image settings for Vector Agent if no specified operator uses default config version type: string + labels: + additionalProperties: + type: string + description: 'Labels is additional labels that will be added + to ConfigCheck pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels' + type: object resources: description: |- Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ @@ -2546,6 +2552,12 @@ spec: internalMetrics: description: Enable internal metrics exporter type: boolean + labels: + additionalProperties: + type: string + description: 'Labels is additional labels that will be added to + Vector pod, service, podmonitor, etc. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels' + type: object livenessProbe: description: Periodic probe of container liveness. Container will be restarted if the probe fails. diff --git a/docs/specification.md b/docs/specification.md index 27dcee54..7c307e5e 100644 --- a/docs/specification.md +++ b/docs/specification.md @@ -10,7 +10,7 @@ # Vector Spec
agentagent image Image for Vector agent. timberio/vector:0.24.0-distroless-libc by default
- + @@ -102,6 +102,14 @@ + + + + + + + +
agentagent image Image for Vector agent. timberio/vector:0.24.0-distroless-libc by default
envFrom envFrom that will be added to Vector pod. By default - not set
annotationsAnnotations that will be added to Vector pod, service, podmonitor, etc. By default - not set
labelsAdditional labels that will be added to Vector pod, service, podmonitor, etc. By default - not set
## Api Spec diff --git a/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectoraggregators.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectoraggregators.yaml index a4c871f2..d56d8627 100644 --- a/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectoraggregators.yaml +++ b/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectoraggregators.yaml @@ -1938,6 +1938,12 @@ spec: Image - docker image settings for Vector Agent if no specified operator uses default config version type: string + labels: + additionalProperties: + type: string + description: 'Labels is additional labels that will be added to + ConfigCheck pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels' + type: object resources: description: |- Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ @@ -2539,6 +2545,12 @@ spec: internalMetrics: description: Enable internal metrics exporter type: boolean + labels: + additionalProperties: + type: string + description: 'Labels is additional labels that will be added to Vector + pod, service, podmonitor, etc. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels' + type: object livenessProbe: description: Periodic probe of container liveness. Container will be restarted if the probe fails. diff --git a/helm/charts/vector-operator/crds/observability.kaasops.io_vectoraggregators.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_vectoraggregators.yaml index 693d2c67..64976644 100644 --- a/helm/charts/vector-operator/crds/observability.kaasops.io_vectoraggregators.yaml +++ b/helm/charts/vector-operator/crds/observability.kaasops.io_vectoraggregators.yaml @@ -1936,6 +1936,12 @@ spec: Image - docker image settings for Vector Agent if no specified operator uses default config version type: string + labels: + additionalProperties: + type: string + description: 'Labels is additional labels that will be added to + ConfigCheck pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels' + type: object resources: description: |- Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ @@ -2537,6 +2543,12 @@ spec: internalMetrics: description: Enable internal metrics exporter type: boolean + labels: + additionalProperties: + type: string + description: 'Labels is additional labels that will be added to Vector + pod, service, podmonitor, etc. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels' + type: object livenessProbe: description: Periodic probe of container liveness. Container will be restarted if the probe fails. diff --git a/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml index 8c19c4a9..53577da1 100644 --- a/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml +++ b/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml @@ -1951,6 +1951,12 @@ spec: Image - docker image settings for Vector Agent if no specified operator uses default config version type: string + labels: + additionalProperties: + type: string + description: 'Labels is additional labels that will be added + to ConfigCheck pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels' + type: object resources: description: |- Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ @@ -2546,6 +2552,12 @@ spec: internalMetrics: description: Enable internal metrics exporter type: boolean + labels: + additionalProperties: + type: string + description: 'Labels is additional labels that will be added to + Vector pod, service, podmonitor, etc. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels' + type: object livenessProbe: description: Periodic probe of container liveness. Container will be restarted if the probe fails. diff --git a/internal/config/configcheck/configcheck.go b/internal/config/configcheck/configcheck.go index f06f4683..dc4cca23 100644 --- a/internal/config/configcheck/configcheck.go +++ b/internal/config/configcheck/configcheck.go @@ -19,10 +19,11 @@ package configcheck import ( "context" "errors" - api_errors "k8s.io/apimachinery/pkg/api/errors" "math/rand" "time" + api_errors "k8s.io/apimachinery/pkg/api/errors" + "github.com/kaasops/vector-operator/internal/utils/k8s" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -62,6 +63,7 @@ type ConfigCheck struct { ConfigReloaderResources corev1.ResourceRequirements ConfigCheckTimeout time.Duration Annotations map[string]string + Labels map[string]string } func New( @@ -110,6 +112,7 @@ func New( ConfigReloaderResources: vc.ConfigReloaderResources, ConfigCheckTimeout: timeout, Annotations: vc.ConfigCheck.Annotations, + Labels: vc.ConfigCheck.Labels, Initiator: initiator, } } @@ -170,12 +173,15 @@ func (cc *ConfigCheck) ensureVectorConfigCheckServiceAccount(ctx context.Context return k8s.CreateOrUpdateResource(ctx, vectorAgentServiceAccount, cc.Client) } -func labelsForVectorConfigCheck() map[string]string { - return map[string]string{ +func (cc *ConfigCheck) labelsForVectorConfigCheck() map[string]string { + basicLabels := map[string]string{ k8s.ManagedByLabelKey: "vector-operator", k8s.NameLabelKey: "vector-configcheck", k8s.ComponentLabelKey: "ConfigCheck", } + labels := k8s.MergeLabels(basicLabels, cc.Labels) + + return labels } func (cc *ConfigCheck) annotationsForVectorConfigCheck() map[string]string { @@ -286,7 +292,7 @@ func (cc *ConfigCheck) CleanAll(ctx context.Context) error { } func (cc *ConfigCheck) configCheckListOpts() (client.ListOptions, error) { - configCheckLabels := labelsForVectorConfigCheck() + configCheckLabels := cc.labelsForVectorConfigCheck() var requirements []labels.Requirement for k, v := range configCheckLabels { r, err := labels.NewRequirement(k, "==", []string{v}) diff --git a/internal/config/configcheck/configcheck_config.go b/internal/config/configcheck/configcheck_config.go index b7846150..3d3a7107 100644 --- a/internal/config/configcheck/configcheck_config.go +++ b/internal/config/configcheck/configcheck_config.go @@ -27,7 +27,7 @@ import ( func (cc *ConfigCheck) createVectorConfigCheckConfig(ctx context.Context) (*corev1.Secret, error) { log := log.FromContext(ctx).WithValues("Vector ConfigCheck", cc.Initiator) - labels := labelsForVectorConfigCheck() + labels := cc.labelsForVectorConfigCheck() var data = cc.Config if cc.CompressedConfig { diff --git a/internal/config/configcheck/configcheck_pod.go b/internal/config/configcheck/configcheck_pod.go index ca58149a..2456d7db 100644 --- a/internal/config/configcheck/configcheck_pod.go +++ b/internal/config/configcheck/configcheck_pod.go @@ -22,7 +22,7 @@ import ( ) func (cc *ConfigCheck) createVectorConfigCheckPod() *corev1.Pod { - labels := labelsForVectorConfigCheck() + labels := cc.labelsForVectorConfigCheck() annotations := cc.annotationsForVectorConfigCheck() var initContainers []corev1.Container diff --git a/internal/config/configcheck/configcheck_rbac.go b/internal/config/configcheck/configcheck_rbac.go index 0b4fb80b..ba0c2e31 100644 --- a/internal/config/configcheck/configcheck_rbac.go +++ b/internal/config/configcheck/configcheck_rbac.go @@ -22,7 +22,7 @@ import ( ) func (cc *ConfigCheck) createVectorConfigCheckServiceAccount() *corev1.ServiceAccount { - labels := labelsForVectorConfigCheck() + labels := cc.labelsForVectorConfigCheck() serviceAccount := &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ diff --git a/internal/utils/k8s/label.go b/internal/utils/k8s/label.go index b9b86563..72c63ebc 100644 --- a/internal/utils/k8s/label.go +++ b/internal/utils/k8s/label.go @@ -37,3 +37,23 @@ const ( // https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#pod-selector PodName string = "statefulset.kubernetes.io/pod-name" ) + +// MergeLabels merges two sets of Kubernetes labels, with the source (src) labels +// being merged into the destination (dst) labels. If a key exists in both maps, +// the destination value is preserved. +func MergeLabels(dst, src map[string]string) map[string]string { + if dst == nil { + dst = make(map[string]string) + } + + if src == nil { + return dst + } + + for k, v := range src { + if _, ok := dst[k]; !ok { + dst[k] = v + } + } + return dst +} \ No newline at end of file diff --git a/internal/vector/aggregator/controller.go b/internal/vector/aggregator/controller.go index ebb31cb5..7c8f9649 100644 --- a/internal/vector/aggregator/controller.go +++ b/internal/vector/aggregator/controller.go @@ -269,7 +269,7 @@ func (ctrl *Controller) SetFailedStatus(ctx context.Context, reason string) erro return k8s.UpdateStatus(ctx, ctrl.VectorAggregator, ctrl.Client) } -func (ctrl *Controller) labelsForVectorAggregator() map[string]string { +func (ctrl *Controller) matchLabelsForVectorAggregator() map[string]string { return map[string]string{ k8s.ManagedByLabelKey: "vector-operator", k8s.NameLabelKey: "vector", @@ -278,6 +278,14 @@ func (ctrl *Controller) labelsForVectorAggregator() map[string]string { } } +func (ctrl *Controller) labelsForVectorAggregator() map[string]string { + basicLabels := ctrl.matchLabelsForVectorAggregator() + + labels := k8s.MergeLabels(basicLabels, ctrl.Spec.Labels) + + return labels +} + func (ctrl *Controller) annotationsForVectorAggregator() map[string]string { return ctrl.Spec.Annotations } diff --git a/internal/vector/aggregator/deployment.go b/internal/vector/aggregator/deployment.go index 7006d290..d7c8bc82 100644 --- a/internal/vector/aggregator/deployment.go +++ b/internal/vector/aggregator/deployment.go @@ -2,13 +2,14 @@ package aggregator import ( "context" + "time" + "github.com/kaasops/vector-operator/internal/common" "github.com/kaasops/vector-operator/internal/utils/k8s" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/log" - "time" ) func (ctrl *Controller) ensureVectorAggregatorDeployment(ctx context.Context) error { @@ -27,6 +28,7 @@ func (ctrl *Controller) ensureVectorAggregatorDeployment(ctx context.Context) er func (ctrl *Controller) createVectorAggregatorDeployment() *appsv1.Deployment { labels := ctrl.labelsForVectorAggregator() + matchLabels := ctrl.matchLabelsForVectorAggregator() annotations := ctrl.annotationsForVectorAggregator() var initContainers []corev1.Container var containers []corev1.Container @@ -43,7 +45,7 @@ func (ctrl *Controller) createVectorAggregatorDeployment() *appsv1.Deployment { deployment := &appsv1.Deployment{ ObjectMeta: ctrl.objectMetaVectorAggregator(labels, annotations, ctrl.Namespace), Spec: appsv1.DeploymentSpec{ - Selector: &metav1.LabelSelector{MatchLabels: labels}, + Selector: &metav1.LabelSelector{MatchLabels: matchLabels}, Replicas: &ctrl.Spec.Replicas, Template: corev1.PodTemplateSpec{ ObjectMeta: ctrl.objectMetaVectorAggregator(labels, annotations, ctrl.Namespace), diff --git a/internal/vector/aggregator/podmonitor.go b/internal/vector/aggregator/podmonitor.go index 48ec9ea4..cab43e4a 100644 --- a/internal/vector/aggregator/podmonitor.go +++ b/internal/vector/aggregator/podmonitor.go @@ -2,6 +2,7 @@ package aggregator import ( "context" + "github.com/kaasops/vector-operator/internal/utils/k8s" monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -17,6 +18,7 @@ func (ctrl *Controller) ensureVectorAggregatorPodMonitor(ctx context.Context) er func (ctrl *Controller) createVectorAggregatorPodMonitor() *monitorv1.PodMonitor { labels := ctrl.labelsForVectorAggregator() + matchLabels := ctrl.matchLabelsForVectorAggregator() annotations := ctrl.annotationsForVectorAggregator() podmonitor := &monitorv1.PodMonitor{ @@ -29,7 +31,7 @@ func (ctrl *Controller) createVectorAggregatorPodMonitor() *monitorv1.PodMonitor }, }, Selector: metav1.LabelSelector{ - MatchLabels: labels, + MatchLabels: matchLabels, }, }, } diff --git a/internal/vector/aggregator/service.go b/internal/vector/aggregator/service.go index e19718b7..a0b0cae4 100644 --- a/internal/vector/aggregator/service.go +++ b/internal/vector/aggregator/service.go @@ -2,12 +2,13 @@ package aggregator import ( "context" + "maps" + "github.com/kaasops/vector-operator/internal/utils/k8s" "github.com/stoewer/go-strcase" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/util/intstr" - "maps" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" ) @@ -39,6 +40,7 @@ func (ctrl *Controller) ensureVectorAggregatorService(ctx context.Context) error func (ctrl *Controller) createVectorAggregatorServices() ([]*corev1.Service, error) { labels := ctrl.labelsForVectorAggregator() + matchLabels := ctrl.matchLabelsForVectorAggregator() annotations := ctrl.annotationsForVectorAggregator() if annotations == nil { annotations = make(map[string]string) @@ -63,7 +65,7 @@ func (ctrl *Controller) createVectorAggregatorServices() ([]*corev1.Service, err ObjectMeta: ctrl.objectMetaVectorAggregator(labels, ann, ctrl.Namespace), Spec: corev1.ServiceSpec{ Ports: ports, - Selector: labels, + Selector: matchLabels, }, } svc.ObjectMeta.Name = group.ServiceName @@ -94,7 +96,7 @@ func (ctrl *Controller) getExistingServices(ctx context.Context) (map[string]*co svcList := corev1.ServiceList{} opts := &client.ListOptions{ Namespace: ctrl.Namespace, - LabelSelector: labels.Set(ctrl.labelsForVectorAggregator()).AsSelector(), + LabelSelector: labels.Set(ctrl.matchLabelsForVectorAggregator()).AsSelector(), } err := ctrl.Client.List(ctx, &svcList, opts) if err != nil { diff --git a/internal/vector/vectoragent/vectoragent_controller.go b/internal/vector/vectoragent/vectoragent_controller.go index 3b49c5b9..b85b74dc 100644 --- a/internal/vector/vectoragent/vectoragent_controller.go +++ b/internal/vector/vectoragent/vectoragent_controller.go @@ -18,13 +18,16 @@ package vectoragent import ( "context" + "time" + + "time" + "github.com/kaasops/vector-operator/internal/common" "github.com/kaasops/vector-operator/internal/utils/k8s" monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/log" - "time" ) func (ctrl *Controller) EnsureVectorAgent(ctx context.Context) error { @@ -148,7 +151,7 @@ func (ctrl *Controller) ensureVectorAgentPodMonitor(ctx context.Context) error { return k8s.CreateOrUpdateResource(ctx, vectorAgentPodMonitor, ctrl.Client) } -func (ctrl *Controller) labelsForVectorAgent() map[string]string { +func (ctrl *Controller) matchLabelsForVectorAgent() map[string]string { return map[string]string{ k8s.ManagedByLabelKey: "vector-operator", k8s.NameLabelKey: "vector", @@ -157,6 +160,15 @@ func (ctrl *Controller) labelsForVectorAgent() map[string]string { } } +func (ctrl *Controller) labelsForVectorAgent() map[string]string { + basicLabels := ctrl.matchLabelsForVectorAgent() + + + labels := k8s.MergeLabels(basicLabels, ctrl.Vector.Spec.Agent.Labels) + + return labels +} + func (ctrl *Controller) annotationsForVectorAgent() map[string]string { return ctrl.Vector.Spec.Agent.Annotations } diff --git a/internal/vector/vectoragent/vectoragent_daemonset.go b/internal/vector/vectoragent/vectoragent_daemonset.go index 3c0890da..4627a8a2 100644 --- a/internal/vector/vectoragent/vectoragent_daemonset.go +++ b/internal/vector/vectoragent/vectoragent_daemonset.go @@ -24,6 +24,7 @@ import ( func (ctrl *Controller) createVectorAgentDaemonSet() *appsv1.DaemonSet { labels := ctrl.labelsForVectorAgent() + matchLabels := ctrl.matchLabelsForVectorAgent() annotations := ctrl.annotationsForVectorAgent() var initContainers []corev1.Container var containers []corev1.Container @@ -40,7 +41,7 @@ func (ctrl *Controller) createVectorAgentDaemonSet() *appsv1.DaemonSet { daemonset := &appsv1.DaemonSet{ ObjectMeta: ctrl.objectMetaVectorAgent(labels, annotations, ctrl.Vector.Namespace), Spec: appsv1.DaemonSetSpec{ - Selector: &metav1.LabelSelector{MatchLabels: labels}, + Selector: &metav1.LabelSelector{MatchLabels: matchLabels}, Template: corev1.PodTemplateSpec{ ObjectMeta: ctrl.objectMetaVectorAgent(labels, annotations, ctrl.Vector.Namespace), Spec: corev1.PodSpec{ diff --git a/internal/vector/vectoragent/vectoragent_podmonitor.go b/internal/vector/vectoragent/vectoragent_podmonitor.go index afbee177..dd090779 100644 --- a/internal/vector/vectoragent/vectoragent_podmonitor.go +++ b/internal/vector/vectoragent/vectoragent_podmonitor.go @@ -8,6 +8,7 @@ import ( func (ctrl *Controller) createVectorAgentPodMonitor() *monitorv1.PodMonitor { labels := ctrl.labelsForVectorAgent() + matchLabels := ctrl.matchLabelsForVectorAgent() annotations := ctrl.annotationsForVectorAgent() podmonitor := &monitorv1.PodMonitor{ @@ -20,7 +21,7 @@ func (ctrl *Controller) createVectorAgentPodMonitor() *monitorv1.PodMonitor { }, }, Selector: metav1.LabelSelector{ - MatchLabels: labels, + MatchLabels: matchLabels, }, }, } diff --git a/internal/vector/vectoragent/vectoragent_service.go b/internal/vector/vectoragent/vectoragent_service.go index f2a673f9..0d18d526 100644 --- a/internal/vector/vectoragent/vectoragent_service.go +++ b/internal/vector/vectoragent/vectoragent_service.go @@ -25,6 +25,7 @@ import ( func (ctrl *Controller) createVectorAgentService() *corev1.Service { labels := ctrl.labelsForVectorAgent() + matchLabels := ctrl.matchLabelsForVectorAgent() annotations := ctrl.annotationsForVectorAgent() return &corev1.Service{ @@ -38,7 +39,7 @@ func (ctrl *Controller) createVectorAgentService() *corev1.Service { TargetPort: intstr.FromInt(config.AgentApiPort), }, }, - Selector: labels, + Selector: matchLabels, }, } } From ee773e533a9f2c0f8741e2e09cdf1b057d3f02d7 Mon Sep 17 00:00:00 2001 From: Andrei Makarov <49369886+P0lskay@users.noreply.github.com> Date: Sun, 27 Apr 2025 21:48:03 +0300 Subject: [PATCH 293/316] Fix imports in vectoragent_controller.go --- internal/vector/vectoragent/vectoragent_controller.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/vector/vectoragent/vectoragent_controller.go b/internal/vector/vectoragent/vectoragent_controller.go index b85b74dc..3a27e41b 100644 --- a/internal/vector/vectoragent/vectoragent_controller.go +++ b/internal/vector/vectoragent/vectoragent_controller.go @@ -18,7 +18,6 @@ package vectoragent import ( "context" - "time" "time" From ceeb0721b5feaa60a21d2d113a11bfdfba5907a3 Mon Sep 17 00:00:00 2001 From: Hornwind Date: Fri, 20 Jun 2025 16:51:15 +0300 Subject: [PATCH 294/316] chore: bump vector version Signed-off-by: Hornwind --- README.md | 8 ++++---- config/samples/observability_v1alpha1_vector.yaml | 3 +-- docs/aggregator.md | 8 ++++---- docs/journald-logs.md | 2 +- docs/kubernetes-events.md | 8 ++++---- docs/specification.md | 2 +- helm/charts/vector-operator/values.yaml | 2 +- internal/vector/aggregator/controller.go | 2 +- internal/vector/vectoragent/vectoragent_default.go | 2 +- 9 files changed, 18 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 8c5fe131..5d84379e 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Connect us in Telegram - https://t.me/+Y0PzGa1d5DFiYThi - Collect journald services logs [doc](https://github.com/kaasops/vector-operator/blob/main/docs/journald-logs.md) -## Configuration Examples +## Configuration Examples Configuration for CR Vector: ```yaml apiVersion: observability.kaasops.io/v1alpha1 @@ -49,7 +49,7 @@ metadata: spec: agent: service: true - image: "timberio/vector:0.24.0-distroless-libc" + image: "timberio/vector:0.47.0-distroless-libc" ``` Configuration for CR VectorPipeline: @@ -98,8 +98,8 @@ spec: ### How it works This project aims to follow the Kubernetes [Operator pattern](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/) -It uses [Controllers](https://kubernetes.io/docs/concepts/architecture/controller/) -which provides a reconcile function responsible for synchronizing resources untile the desired state is reached on the cluster +It uses [Controllers](https://kubernetes.io/docs/concepts/architecture/controller/) +which provides a reconcile function responsible for synchronizing resources untile the desired state is reached on the cluster ### Test It Out 1. Install the CRDs into the cluster: diff --git a/config/samples/observability_v1alpha1_vector.yaml b/config/samples/observability_v1alpha1_vector.yaml index 6322c289..78e8fa91 100644 --- a/config/samples/observability_v1alpha1_vector.yaml +++ b/config/samples/observability_v1alpha1_vector.yaml @@ -5,7 +5,7 @@ metadata: namespace: vector spec: agent: - image: "timberio/vector:0.28.1-debian" + image: "timberio/vector:0.47.0-debian" internalMetrics: false api: enabled: true @@ -19,4 +19,3 @@ spec: requests: cpu: 10m memory: 20Mi - diff --git a/docs/aggregator.md b/docs/aggregator.md index 11448f55..96a6279a 100644 --- a/docs/aggregator.md +++ b/docs/aggregator.md @@ -1,6 +1,6 @@ # Aggregator -The operator allows deploying Vector in the cluster as an aggregator for remote processing, [more details about this](https://vector.dev/docs/setup/going-to-prod/arch/aggregator/). +The operator allows deploying Vector in the cluster as an aggregator for remote processing, [more details about this](https://vector.dev/docs/setup/going-to-prod/arch/aggregator/). Two types of resources are available for deploying aggregators in the cluster: - VectorAggregator - ClusterVectorAggregator @@ -17,7 +17,7 @@ metadata: name: vectorAggregator1 namespace: vector spec: - image: timberio/vector:0.40.0-debian + image: timberio/vector:0.47.0-debian api: enabled: true replicas: 1 @@ -62,7 +62,7 @@ kind: ClusterVectorAggregator metadata: name: clusterVectorAggregator1 spec: - image: timberio/vector:0.40.0-debian + image: timberio/vector:0.47.0-debian resourceNamespace: default api: enabled: true @@ -105,4 +105,4 @@ spec: user: elastic password: test-password strategy: basic -``` \ No newline at end of file +``` diff --git a/docs/journald-logs.md b/docs/journald-logs.md index b921a2c3..95e18a09 100644 --- a/docs/journald-logs.md +++ b/docs/journald-logs.md @@ -4,7 +4,7 @@ If you want collect service journald logs from node you can use example. > Type `journald` in source block work only in ClusterVectorPipeline. In VectorPipeline can use only `kubernetes_logs` type -> If you want collect journald logs, needs to use vector-agent container with journalctl. `timberio/vector:0.26.0-debian` - for example +> If you want collect journald logs, needs to use vector-agent container with journalctl. `timberio/vector:0.47.0-debian` - for example ```yaml diff --git a/docs/kubernetes-events.md b/docs/kubernetes-events.md index 4a9c4013..c415ff23 100644 --- a/docs/kubernetes-events.md +++ b/docs/kubernetes-events.md @@ -1,7 +1,7 @@ # [EXPERIMENTAL] Kubernetes events The operator allows organizing the collection of events from the Kubernetes cluster in which it is deployed. -To do this, you need to deploy an aggregator and a pipeline. +To do this, you need to deploy an aggregator and a pipeline. The operator allows collecting events from the entire cluster or from a specific namespace. ## Namespace event collection @@ -13,7 +13,7 @@ metadata: name: vectorAggregator1 namespace: vector spec: - image: timberio/vector:0.40.0-debian + image: timberio/vector:0.47.0-debian api: enabled: true replicas: 1 @@ -53,7 +53,7 @@ kind: ClusterVectorAggregator metadata: name: clusterVectorAggregator1 spec: - image: timberio/vector:0.40.0-debian + image: timberio/vector:0.47.0-debian resourceNamespace: default api: enabled: true @@ -83,4 +83,4 @@ spec: codec: "json" inputs: - source-test -``` \ No newline at end of file +``` diff --git a/docs/specification.md b/docs/specification.md index 7c307e5e..4c1b4316 100644 --- a/docs/specification.md +++ b/docs/specification.md @@ -12,7 +12,7 @@ agent image - Image for Vector agent. timberio/vector:0.24.0-distroless-libc by default + Image for Vector agent. timberio/vector:0.47.0-distroless-libc by default dataDir diff --git a/helm/charts/vector-operator/values.yaml b/helm/charts/vector-operator/values.yaml index 5877eb8e..b25269b4 100644 --- a/helm/charts/vector-operator/values.yaml +++ b/helm/charts/vector-operator/values.yaml @@ -78,7 +78,7 @@ vector: name: "vector" useApiServerCache: false # agent: - # image: timberio/vector:0.24.0-distroless-libc + # image: timberio/vector:0.47.0-distroless-libc # env: # - name: "testenv" # value: "testvalues" diff --git a/internal/vector/aggregator/controller.go b/internal/vector/aggregator/controller.go index 7c8f9649..b2471b6e 100644 --- a/internal/vector/aggregator/controller.go +++ b/internal/vector/aggregator/controller.go @@ -125,7 +125,7 @@ func (ctrl *Controller) DeleteVectorAggregator(ctx context.Context) error { func (ctrl *Controller) setDefault() { if ctrl.Spec.Image == "" { - ctrl.Spec.Image = "timberio/vector:0.28.1-distroless-libc" + ctrl.Spec.Image = "timberio/vector:0.47.0-distroless-libc" } if ctrl.Spec.Resources.Requests == nil { diff --git a/internal/vector/vectoragent/vectoragent_default.go b/internal/vector/vectoragent/vectoragent_default.go index 896311e9..b5b746be 100644 --- a/internal/vector/vectoragent/vectoragent_default.go +++ b/internal/vector/vectoragent/vectoragent_default.go @@ -28,7 +28,7 @@ func (ctrl *Controller) SetDefault() { ctrl.Vector.Spec.Agent = new(v1alpha1.VectorAgent) } if ctrl.Vector.Spec.Agent.Image == "" { - ctrl.Vector.Spec.Agent.Image = "timberio/vector:0.28.1-distroless-libc" + ctrl.Vector.Spec.Agent.Image = "timberio/vector:0.47.0-distroless-libc" } if ctrl.Vector.Spec.Agent.Resources.Requests == nil { From e9cadcf14d93f09fb146e05769d37f30960a8e2c Mon Sep 17 00:00:00 2001 From: Sergey Kacheev Date: Tue, 8 Jul 2025 19:17:08 +0300 Subject: [PATCH 295/316] fix: add dual stack support --- internal/config/config.go | 2 +- internal/config/default.go | 11 +++++++++-- internal/vector/aggregator/deployment.go | 3 ++- internal/vector/vectoragent/vectoragent_daemonset.go | 3 ++- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/internal/config/config.go b/internal/config/config.go index e51f52fb..eaaa52ce 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -49,7 +49,7 @@ func newVectorConfig(p VectorConfigParams) *VectorConfig { sinks := make(map[string]*Sink) api := &ApiSpec{ - Address: net.JoinHostPort("0.0.0.0", strconv.Itoa(AgentApiPort)), + Address: net.JoinHostPort(net.IPv6zero.String(), strconv.Itoa(AgentApiPort)), Enabled: p.ApiEnabled, Playground: p.PlaygroundEnabled, } diff --git a/internal/config/default.go b/internal/config/default.go index 380a154a..77662703 100644 --- a/internal/config/default.go +++ b/internal/config/default.go @@ -1,6 +1,9 @@ package config -import "fmt" +import ( + "net" + "strconv" +) const ( // types @@ -12,6 +15,7 @@ const ( DefaultSinkName = "defaultSink" DefaultInternalMetricsSourceName = "internalMetricsSource" DefaultInternalMetricsSinkName = "internalMetricsSink" + DefaultInternalMetricsSinkPort = 9598 DefaultAggregatorSourcePort = 8989 DefaultNamespace = "default" DefaultPipelineName = "default-pipeline" @@ -26,7 +30,7 @@ var ( Name: DefaultSourceName, Type: VectorType, Options: map[string]any{ - "address": fmt.Sprintf("0.0.0.0:%d", DefaultAggregatorSourcePort), + "address": net.JoinHostPort(net.IPv6zero.String(), strconv.Itoa(DefaultAggregatorSourcePort)), }, } defaultSink = &Sink{ @@ -63,5 +67,8 @@ var ( Name: DefaultInternalMetricsSinkName, Type: PrometheusExporterType, Inputs: []string{DefaultInternalMetricsSourceName}, + Options: map[string]any{ + "address": net.JoinHostPort(net.IPv6zero.String(), strconv.Itoa(DefaultInternalMetricsSinkPort)), + }, } ) diff --git a/internal/vector/aggregator/deployment.go b/internal/vector/aggregator/deployment.go index 7006d290..2600bec3 100644 --- a/internal/vector/aggregator/deployment.go +++ b/internal/vector/aggregator/deployment.go @@ -3,6 +3,7 @@ package aggregator import ( "context" "github.com/kaasops/vector-operator/internal/common" + "github.com/kaasops/vector-operator/internal/config" "github.com/kaasops/vector-operator/internal/utils/k8s" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -78,7 +79,7 @@ func (ctrl *Controller) VectorAggregatorContainer() *corev1.Container { Ports: []corev1.ContainerPort{ { Name: "prom-exporter", - ContainerPort: 9598, + ContainerPort: config.DefaultInternalMetricsSinkPort, Protocol: "TCP", }, }, diff --git a/internal/vector/vectoragent/vectoragent_daemonset.go b/internal/vector/vectoragent/vectoragent_daemonset.go index 3c0890da..ab91b4ec 100644 --- a/internal/vector/vectoragent/vectoragent_daemonset.go +++ b/internal/vector/vectoragent/vectoragent_daemonset.go @@ -17,6 +17,7 @@ limitations under the License. package vectoragent import ( + "github.com/kaasops/vector-operator/internal/config" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -210,7 +211,7 @@ func (ctrl *Controller) VectorAgentContainer() *corev1.Container { Ports: []corev1.ContainerPort{ { Name: "prom-exporter", - ContainerPort: 9598, + ContainerPort: config.DefaultInternalMetricsSinkPort, Protocol: "TCP", }, }, From c2f2947e5284c8314eba91e2d0219e718d6fa588 Mon Sep 17 00:00:00 2001 From: Sergey Kacheev Date: Tue, 8 Jul 2025 21:28:37 +0300 Subject: [PATCH 296/316] chore: update docs/specification.md --- docs/specification.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/specification.md b/docs/specification.md index 25befd1b..b42ae719 100644 --- a/docs/specification.md +++ b/docs/specification.md @@ -111,7 +111,7 @@ address - The network address to which the API should bind. If you’re running Vector in a Docker container, make sure to bind to 0.0.0.0. Otherwise the API will not be exposed outside the container. By default - 0.0.0.0:8686 + The network address to which the API should bind. Uses dual-stack IPv6/IPv4 binding (::) by default, which accepts connections on both IPv4 and IPv6. By default - [::]:8686 enabled From c9b2aead07c2a6d728a272216596cb8bc7ddaf5b Mon Sep 17 00:00:00 2001 From: Hornwind Date: Thu, 28 Aug 2025 16:25:47 +0300 Subject: [PATCH 297/316] bump vector 0.48.0 version Signed-off-by: Hornwind --- .gitignore | 1 + README.md | 2 +- config/samples/observability_v1alpha1_vector.yaml | 2 +- docs/aggregator.md | 4 ++-- docs/journald-logs.md | 2 +- docs/kubernetes-events.md | 4 ++-- docs/specification.md | 2 +- helm/charts/vector-operator/values.yaml | 2 +- internal/vector/aggregator/controller.go | 2 +- internal/vector/vectoragent/vectoragent_default.go | 2 +- 10 files changed, 12 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index ececd2db..37afa32b 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ testbin/* # editor and IDE paraphernalia .idea +.kilocode *.swp *.swo *~ diff --git a/README.md b/README.md index 5d84379e..7128d523 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ metadata: spec: agent: service: true - image: "timberio/vector:0.47.0-distroless-libc" + image: "timberio/vector:0.48.0-distroless-libc" ``` Configuration for CR VectorPipeline: diff --git a/config/samples/observability_v1alpha1_vector.yaml b/config/samples/observability_v1alpha1_vector.yaml index 78e8fa91..37ea236b 100644 --- a/config/samples/observability_v1alpha1_vector.yaml +++ b/config/samples/observability_v1alpha1_vector.yaml @@ -5,7 +5,7 @@ metadata: namespace: vector spec: agent: - image: "timberio/vector:0.47.0-debian" + image: "timberio/vector:0.48.0-debian" internalMetrics: false api: enabled: true diff --git a/docs/aggregator.md b/docs/aggregator.md index 96a6279a..24650b7b 100644 --- a/docs/aggregator.md +++ b/docs/aggregator.md @@ -17,7 +17,7 @@ metadata: name: vectorAggregator1 namespace: vector spec: - image: timberio/vector:0.47.0-debian + image: timberio/vector:0.48.0-debian api: enabled: true replicas: 1 @@ -62,7 +62,7 @@ kind: ClusterVectorAggregator metadata: name: clusterVectorAggregator1 spec: - image: timberio/vector:0.47.0-debian + image: timberio/vector:0.48.0-debian resourceNamespace: default api: enabled: true diff --git a/docs/journald-logs.md b/docs/journald-logs.md index 95e18a09..3f9881c2 100644 --- a/docs/journald-logs.md +++ b/docs/journald-logs.md @@ -4,7 +4,7 @@ If you want collect service journald logs from node you can use example. > Type `journald` in source block work only in ClusterVectorPipeline. In VectorPipeline can use only `kubernetes_logs` type -> If you want collect journald logs, needs to use vector-agent container with journalctl. `timberio/vector:0.47.0-debian` - for example +> If you want collect journald logs, needs to use vector-agent container with journalctl. `timberio/vector:0.48.0-debian` - for example ```yaml diff --git a/docs/kubernetes-events.md b/docs/kubernetes-events.md index c415ff23..5b38a229 100644 --- a/docs/kubernetes-events.md +++ b/docs/kubernetes-events.md @@ -13,7 +13,7 @@ metadata: name: vectorAggregator1 namespace: vector spec: - image: timberio/vector:0.47.0-debian + image: timberio/vector:0.48.0-debian api: enabled: true replicas: 1 @@ -53,7 +53,7 @@ kind: ClusterVectorAggregator metadata: name: clusterVectorAggregator1 spec: - image: timberio/vector:0.47.0-debian + image: timberio/vector:0.48.0-debian resourceNamespace: default api: enabled: true diff --git a/docs/specification.md b/docs/specification.md index 4c1b4316..7bf74791 100644 --- a/docs/specification.md +++ b/docs/specification.md @@ -12,7 +12,7 @@ agent image - Image for Vector agent. timberio/vector:0.47.0-distroless-libc by default + Image for Vector agent. timberio/vector:0.48.0-distroless-libc by default dataDir diff --git a/helm/charts/vector-operator/values.yaml b/helm/charts/vector-operator/values.yaml index b25269b4..e07a2d37 100644 --- a/helm/charts/vector-operator/values.yaml +++ b/helm/charts/vector-operator/values.yaml @@ -78,7 +78,7 @@ vector: name: "vector" useApiServerCache: false # agent: - # image: timberio/vector:0.47.0-distroless-libc + # image: timberio/vector:0.48.0-distroless-libc # env: # - name: "testenv" # value: "testvalues" diff --git a/internal/vector/aggregator/controller.go b/internal/vector/aggregator/controller.go index b2471b6e..09dae11a 100644 --- a/internal/vector/aggregator/controller.go +++ b/internal/vector/aggregator/controller.go @@ -125,7 +125,7 @@ func (ctrl *Controller) DeleteVectorAggregator(ctx context.Context) error { func (ctrl *Controller) setDefault() { if ctrl.Spec.Image == "" { - ctrl.Spec.Image = "timberio/vector:0.47.0-distroless-libc" + ctrl.Spec.Image = "timberio/vector:0.48.0-distroless-libc" } if ctrl.Spec.Resources.Requests == nil { diff --git a/internal/vector/vectoragent/vectoragent_default.go b/internal/vector/vectoragent/vectoragent_default.go index b5b746be..d537a0e0 100644 --- a/internal/vector/vectoragent/vectoragent_default.go +++ b/internal/vector/vectoragent/vectoragent_default.go @@ -28,7 +28,7 @@ func (ctrl *Controller) SetDefault() { ctrl.Vector.Spec.Agent = new(v1alpha1.VectorAgent) } if ctrl.Vector.Spec.Agent.Image == "" { - ctrl.Vector.Spec.Agent.Image = "timberio/vector:0.47.0-distroless-libc" + ctrl.Vector.Spec.Agent.Image = "timberio/vector:0.48.0-distroless-libc" } if ctrl.Vector.Spec.Agent.Resources.Requests == nil { From 03aa48a72fed8b90542a7226d4fd60384d3a5da8 Mon Sep 17 00:00:00 2001 From: "an.makarov" Date: Sun, 31 Aug 2025 12:13:32 +0300 Subject: [PATCH 298/316] PodSecurityPolicyName is used to set PriorityClassName Fixes #183 --- docs/specification.md | 6 +----- internal/vector/aggregator/deployment.go | 2 +- internal/vector/aggregator/event_collector.go | 3 ++- internal/vector/vectoragent/vectoragent_daemonset.go | 2 +- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/docs/specification.md b/docs/specification.md index 4c1b4316..c2197ff8 100644 --- a/docs/specification.md +++ b/docs/specification.md @@ -10,7 +10,7 @@ # Vector Spec - + @@ -66,10 +66,6 @@ - - - - diff --git a/internal/vector/aggregator/deployment.go b/internal/vector/aggregator/deployment.go index d7c8bc82..fec3cb8b 100644 --- a/internal/vector/aggregator/deployment.go +++ b/internal/vector/aggregator/deployment.go @@ -58,7 +58,7 @@ func (ctrl *Controller) createVectorAggregatorDeployment() *appsv1.Deployment { RuntimeClassName: ctrl.Spec.RuntimeClassName, SchedulerName: ctrl.Spec.SchedulerName, Tolerations: ctrl.Spec.Tolerations, - PriorityClassName: ctrl.Spec.PodSecurityPolicyName, + PriorityClassName: ctrl.Spec.PriorityClassName, HostNetwork: ctrl.Spec.HostNetwork, HostAliases: ctrl.Spec.HostAliases, InitContainers: initContainers, diff --git a/internal/vector/aggregator/event_collector.go b/internal/vector/aggregator/event_collector.go index edd87da8..8ca92994 100644 --- a/internal/vector/aggregator/event_collector.go +++ b/internal/vector/aggregator/event_collector.go @@ -3,6 +3,7 @@ package aggregator import ( "context" "encoding/json" + "github.com/kaasops/vector-operator/internal/evcollector" "github.com/kaasops/vector-operator/internal/utils/k8s" appsv1 "k8s.io/api/apps/v1" @@ -142,7 +143,7 @@ func (ctrl *Controller) createEventCollectorDeployment() *appsv1.Deployment { RuntimeClassName: ctrl.Spec.RuntimeClassName, SchedulerName: ctrl.Spec.SchedulerName, Tolerations: ctrl.Spec.Tolerations, - PriorityClassName: ctrl.Spec.PodSecurityPolicyName, + PriorityClassName: ctrl.Spec.PriorityClassName, HostNetwork: ctrl.Spec.HostNetwork, HostAliases: ctrl.Spec.HostAliases, Containers: containers, diff --git a/internal/vector/vectoragent/vectoragent_daemonset.go b/internal/vector/vectoragent/vectoragent_daemonset.go index 4627a8a2..9c36f1d3 100644 --- a/internal/vector/vectoragent/vectoragent_daemonset.go +++ b/internal/vector/vectoragent/vectoragent_daemonset.go @@ -53,7 +53,7 @@ func (ctrl *Controller) createVectorAgentDaemonSet() *appsv1.DaemonSet { RuntimeClassName: ctrl.Vector.Spec.Agent.RuntimeClassName, SchedulerName: ctrl.Vector.Spec.Agent.SchedulerName, Tolerations: ctrl.Vector.Spec.Agent.Tolerations, - PriorityClassName: ctrl.Vector.Spec.Agent.PodSecurityPolicyName, + PriorityClassName: ctrl.Vector.Spec.Agent.PriorityClassName, HostNetwork: ctrl.Vector.Spec.Agent.HostNetwork, HostAliases: ctrl.Vector.Spec.Agent.HostAliases, InitContainers: initContainers, From 9fb6e0907bc22bbc96a604c94f8519641d5bd269 Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Thu, 2 Oct 2025 13:57:58 +0300 Subject: [PATCH 299/316] mark vp false if unmarshal failed --- internal/controller/pipeline_controller.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/internal/controller/pipeline_controller.go b/internal/controller/pipeline_controller.go index 52b5d465..adf9ca0d 100644 --- a/internal/controller/pipeline_controller.go +++ b/internal/controller/pipeline_controller.go @@ -20,14 +20,15 @@ import ( "context" "errors" "fmt" + "reflect" + "time" + "github.com/kaasops/vector-operator/internal/config/configcheck" "github.com/kaasops/vector-operator/internal/vector/aggregator" "github.com/kaasops/vector-operator/internal/vector/vectoragent" "golang.org/x/sync/errgroup" - "reflect" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/predicate" - "time" "github.com/kaasops/vector-operator/api/v1alpha1" "github.com/kaasops/vector-operator/internal/config" @@ -120,8 +121,12 @@ func (r *PipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c p := &config.PipelineConfig{} if err := config.UnmarshalJson(pipelineCR.GetSpec(), p); err != nil { - return ctrl.Result{}, fmt.Errorf("failed to unmarshal pipeline %s: %w", pipelineCR.GetName(), err) + if err := pipeline.SetFailedStatus(ctx, r.Client, pipelineCR, fmt.Sprintf("Failed to unmarshal vector pipeline %s", err.Error())); err != nil { + return ctrl.Result{}, fmt.Errorf("failed to set pipeline status %s: %w", pipelineCR.GetName(), err) + } + return ctrl.Result{}, nil } + pipelineVectorRole, err := p.VectorRole() if err != nil { if err := pipeline.SetFailedStatus(ctx, r.Client, pipelineCR, err.Error()); err != nil { From b02ad36856c9b9ffeb47ee27574f33ccf6a43d2d Mon Sep 17 00:00:00 2001 From: Denis Khachyan Date: Fri, 3 Oct 2025 10:36:58 +0300 Subject: [PATCH 300/316] update helm chart version --- helm/charts/vector-operator/Chart.yaml | 4 +- helm/index.yaml | 103 ++++++++++++++----------- helm/packages/vector-operator-0.7.tgz | Bin 0 -> 102636 bytes 3 files changed, 60 insertions(+), 47 deletions(-) create mode 100644 helm/packages/vector-operator-0.7.tgz diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index 57498177..e5e6b83d 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: "0.6" +version: "0.7" # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.3.0" +appVersion: "v0.3.2" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/index.yaml b/helm/index.yaml index bb6e14c0..a7cbb4ae 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.3.2 + created: "2025-10-03T10:36:38.56653949+03:00" + description: A Helm chart to install Vector Operator + digest: 67fbdd5181070c542bc7b52457ff15962d6b1dcefe495939f076703f71cd0bde + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.7.tgz + version: "0.7" - apiVersion: v2 appVersion: v0.3.0 - created: "2025-02-19T09:56:20.757703+02:00" + created: "2025-10-03T10:36:38.563952954+03:00" description: A Helm chart to install Vector Operator digest: 69262a286e22bfbf7571297f05d17dfc8f19e6215faa42f4dbdabea8a5610586 home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: "0.6" - apiVersion: v2 appVersion: v0.2.0 - created: "2025-02-19T09:56:20.756106+02:00" + created: "2025-10-03T10:36:38.561220759+03:00" description: A Helm chart to install Vector Operator digest: f0e89cc2f3b641588e603107ba4aedc5f1ec585452c88bac46784226e56751e2 home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: "0.5" - apiVersion: v2 appVersion: v0.1.2 - created: "2025-02-19T09:56:20.753922+02:00" + created: "2025-10-03T10:36:38.558942896+03:00" description: A Helm chart to install Vector Operator digest: e1fe0e96c146c7c275c181e727c8a60f21898cabe90629851a2920d2915f84b7 home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: "0.4" - apiVersion: v2 appVersion: v0.1.1 - created: "2025-02-19T09:56:20.751379+02:00" + created: "2025-10-03T10:36:38.556103174+03:00" description: A Helm chart to install Vector Operator digest: a916c9e9f81bdbf9f734073fb453a6b67d7a724ed7ff4326d7884b136c103ce5 home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: "0.3" - apiVersion: v2 appVersion: v0.1.1 - created: "2025-02-19T09:56:20.749599+02:00" + created: "2025-10-03T10:36:38.553791079+03:00" description: A Helm chart to install Vector Operator digest: 582d95c6f63134f6cd815bcb85adce5770e179de21a979ec76f91ab7d8531b45 home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: "0.2" - apiVersion: v2 appVersion: v0.1.1 - created: "2025-02-19T09:56:20.747632+02:00" + created: "2025-10-03T10:36:38.551206192+03:00" description: A Helm chart to install Vector Operator digest: 3ac5a422f3f1861528f737a0fea077cad5f7b7516db3fe5d392c887d5d3459d5 home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: 0.1.1 - apiVersion: v2 appVersion: v0.1.0 - created: "2025-02-19T09:56:20.745628+02:00" + created: "2025-10-03T10:36:38.548594217+03:00" description: A Helm chart to install Vector Operator digest: 191ec4f83f11541df19680ce220992c84fb10210f0d54a5acc29361ccfd787bb home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.1.0 - apiVersion: v2 appVersion: pre-v0.1.0-r1 - created: "2025-02-19T09:56:20.74337+02:00" + created: "2025-10-03T10:36:38.545927541+03:00" description: A Helm chart to install Vector Operator digest: 01bd2e347c5782127511a0a0fbbab72508cbe667664f2c9b615cabb80a4c40c7 home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.1.0-rc1 - apiVersion: v2 appVersion: v0.0.40 - created: "2025-02-19T09:56:20.739263+02:00" + created: "2025-10-03T10:36:38.540964374+03:00" description: A Helm chart to install Vector Operator digest: c50e673e811b8d4c03ad45c92ce23b9bc54eef4665091e70fab9a4b7e4c6c3f1 home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.0.40 - apiVersion: v2 appVersion: v0.0.39 - created: "2025-02-19T09:56:20.738282+02:00" + created: "2025-10-03T10:36:38.540022481+03:00" description: A Helm chart to install Vector Operator digest: e1de38c869bf896bfb9f5f615c329d3ccce3bd91cb64bb6fa1c783a40ea290a2 home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.0.39 - apiVersion: v2 appVersion: v0.0.38 - created: "2025-02-19T09:56:20.737032+02:00" + created: "2025-10-03T10:36:38.538992202+03:00" description: A Helm chart to install Vector Operator digest: 565b148184400900f5572b06ceebaac6340af5e5fd1122b308bdbfcbc2d2040a home: https://github.com/kaasops/vector-operator @@ -146,7 +159,7 @@ entries: version: 0.0.38 - apiVersion: v2 appVersion: v0.0.37 - created: "2025-02-19T09:56:20.736017+02:00" + created: "2025-10-03T10:36:38.537860579+03:00" description: A Helm chart to install Vector Operator digest: 65e10ee46e6855ba95f51e21ca14abc4411191b260c6b96ec72c775e73c1e331 home: https://github.com/kaasops/vector-operator @@ -159,7 +172,7 @@ entries: version: 0.0.37 - apiVersion: v2 appVersion: v0.0.36 - created: "2025-02-19T09:56:20.735035+02:00" + created: "2025-10-03T10:36:38.536510534+03:00" description: A Helm chart to install Vector Operator digest: 972b6b4048b6d17b0616786e49915ef52cb7c9573cfb6eb359b6c19b66eabe31 home: https://github.com/kaasops/vector-operator @@ -172,7 +185,7 @@ entries: version: 0.0.36 - apiVersion: v2 appVersion: v0.0.35 - created: "2025-02-19T09:56:20.733883+02:00" + created: "2025-10-03T10:36:38.535507349+03:00" description: A Helm chart to install Vector Operator digest: d03ba759c42f2bd8d8f1df71702ee4f26a73b8bc28760ca7254af20d811cee8a home: https://github.com/kaasops/vector-operator @@ -185,7 +198,7 @@ entries: version: 0.0.35 - apiVersion: v2 appVersion: v0.0.34 - created: "2025-02-19T09:56:20.732656+02:00" + created: "2025-10-03T10:36:38.534580922+03:00" description: A Helm chart to install Vector Operator digest: 01e9d488ee78c8603d821f96344edee568ebcb42049d586de37b8df39b372bd4 home: https://github.com/kaasops/vector-operator @@ -198,7 +211,7 @@ entries: version: 0.0.34 - apiVersion: v2 appVersion: v0.0.33 - created: "2025-02-19T09:56:20.731639+02:00" + created: "2025-10-03T10:36:38.533635005+03:00" description: A Helm chart to install Vector Operator digest: fcde3c94a0fa6caa5f3d333226c95b7c85ede8489d46277e1222a868ed4ec8c3 home: https://github.com/kaasops/vector-operator @@ -211,7 +224,7 @@ entries: version: 0.0.33 - apiVersion: v2 appVersion: v0.0.32 - created: "2025-02-19T09:56:20.730624+02:00" + created: "2025-10-03T10:36:38.532397053+03:00" description: A Helm chart to install Vector Operator digest: 26323037ec47f1703ea930a99ab4ec8fb93b44975ce969514ea68d4130017015 home: https://github.com/kaasops/vector-operator @@ -224,7 +237,7 @@ entries: version: 0.0.32 - apiVersion: v2 appVersion: v0.0.31 - created: "2025-02-19T09:56:20.729509+02:00" + created: "2025-10-03T10:36:38.531477197+03:00" description: A Helm chart to install Vector Operator digest: 45b924c07a825e0f7cd3fb534a6ffd16604790d13be1aff59150c045474754e3 home: https://github.com/kaasops/vector-operator @@ -237,7 +250,7 @@ entries: version: 0.0.31 - apiVersion: v2 appVersion: v0.0.30 - created: "2025-02-19T09:56:20.728294+02:00" + created: "2025-10-03T10:36:38.530596306+03:00" description: A Helm chart to install Vector Operator digest: 03beda549d15f50325028ea29af5f2065ac0b8adf3078bf7dc1312981aa5e7db home: https://github.com/kaasops/vector-operator @@ -250,7 +263,7 @@ entries: version: 0.0.30 - apiVersion: v2 appVersion: v0.0.29 - created: "2025-02-19T09:56:20.727336+02:00" + created: "2025-10-03T10:36:38.52970036+03:00" description: A Helm chart to install Vector Operator digest: 0f025fc3a924b37b8c4131c4d8cfa437d2d4e557ab9476ed3e69a00232c7dca6 home: https://github.com/kaasops/vector-operator @@ -263,7 +276,7 @@ entries: version: 0.0.29 - apiVersion: v2 appVersion: v0.0.28 - created: "2025-02-19T09:56:20.726352+02:00" + created: "2025-10-03T10:36:38.528384412+03:00" description: A Helm chart to install Vector Operator digest: af856d41314313e04f15e7143409a9c564c6ca610b0d2eaec3112add8573e668 home: https://github.com/kaasops/vector-operator @@ -276,7 +289,7 @@ entries: version: 0.0.28 - apiVersion: v2 appVersion: v0.0.27 - created: "2025-02-19T09:56:20.725119+02:00" + created: "2025-10-03T10:36:38.527504023+03:00" description: A Helm chart to install Vector Operator digest: 631e2ff02bbd7f247cb486494fd2af60c57cc551066a6a3858226551bc1745a4 home: https://github.com/kaasops/vector-operator @@ -289,7 +302,7 @@ entries: version: 0.0.27 - apiVersion: v2 appVersion: v0.0.26 - created: "2025-02-19T09:56:20.724135+02:00" + created: "2025-10-03T10:36:38.526580443+03:00" description: A Helm chart to install Vector Operator digest: 760a2833f4c1a33466982419b079ff18d996331ebacc40cf93b0f55229cdb7db home: https://github.com/kaasops/vector-operator @@ -302,7 +315,7 @@ entries: version: 0.0.26 - apiVersion: v2 appVersion: v0.0.25 - created: "2025-02-19T09:56:20.723163+02:00" + created: "2025-10-03T10:36:38.52569706+03:00" description: A Helm chart to install Vector Operator digest: fd22b996b071b6d85740ccf76e85cb640fa717c2620748d206d3f4fdd44cbcc2 home: https://github.com/kaasops/vector-operator @@ -315,7 +328,7 @@ entries: version: 0.0.25 - apiVersion: v2 appVersion: v0.0.24 - created: "2025-02-19T09:56:20.722095+02:00" + created: "2025-10-03T10:36:38.522857265+03:00" description: A Helm chart to install Vector Operator digest: ea257e60ecde063a0d1ed52ce5e3283245b8f0e2daba58ea3a5adb0ba82d7799 home: https://github.com/kaasops/vector-operator @@ -328,7 +341,7 @@ entries: version: 0.0.24 - apiVersion: v2 appVersion: v0.0.23 - created: "2025-02-19T09:56:20.720591+02:00" + created: "2025-10-03T10:36:38.52175018+03:00" description: A Helm chart to install Vector Operator digest: 546d202b3b9263f789b88335263191098dfcabd5d8698105f37cad24d56a8ed0 home: https://github.com/kaasops/vector-operator @@ -341,7 +354,7 @@ entries: version: 0.0.23 - apiVersion: v2 appVersion: v0.0.22 - created: "2025-02-19T09:56:20.719567+02:00" + created: "2025-10-03T10:36:38.520665524+03:00" description: A Helm chart to install Vector Operator digest: bf96ddc8ac61e9d6beb8bc763fbf3fa6025d950b29d70d80de6e8a0ea45e0411 home: https://github.com/kaasops/vector-operator @@ -354,7 +367,7 @@ entries: version: 0.0.22 - apiVersion: v2 appVersion: v0.0.21 - created: "2025-02-19T09:56:20.718502+02:00" + created: "2025-10-03T10:36:38.519622275+03:00" description: A Helm chart to install Vector Operator digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d home: https://github.com/kaasops/vector-operator @@ -367,7 +380,7 @@ entries: version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2025-02-19T09:56:20.717257+02:00" + created: "2025-10-03T10:36:38.51822983+03:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -380,7 +393,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2025-02-19T09:56:20.716622+02:00" + created: "2025-10-03T10:36:38.517672504+03:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -393,7 +406,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2025-02-19T09:56:20.716172+02:00" + created: "2025-10-03T10:36:38.517080237+03:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -406,7 +419,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2025-02-19T09:56:20.715718+02:00" + created: "2025-10-03T10:36:38.516526424+03:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -419,7 +432,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2025-02-19T09:56:20.715263+02:00" + created: "2025-10-03T10:36:38.515957976+03:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -432,7 +445,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2025-02-19T09:56:20.714213+02:00" + created: "2025-10-03T10:36:38.515383085+03:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -445,7 +458,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2025-02-19T09:56:20.713442+02:00" + created: "2025-10-03T10:36:38.514339579+03:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -458,7 +471,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2025-02-19T09:56:20.712934+02:00" + created: "2025-10-03T10:36:38.513678659+03:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -471,7 +484,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2025-02-19T09:56:20.712257+02:00" + created: "2025-10-03T10:36:38.513070491+03:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -484,7 +497,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2025-02-19T09:56:20.711541+02:00" + created: "2025-10-03T10:36:38.512514447+03:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -497,7 +510,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2025-02-19T09:56:20.711063+02:00" + created: "2025-10-03T10:36:38.511608892+03:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -510,7 +523,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2025-02-19T09:56:20.741113+02:00" + created: "2025-10-03T10:36:38.54371567+03:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -523,7 +536,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2025-02-19T09:56:20.740763+02:00" + created: "2025-10-03T10:36:38.54313395+03:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -536,7 +549,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2025-02-19T09:56:20.740302+02:00" + created: "2025-10-03T10:36:38.542119473+03:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -549,7 +562,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2025-02-19T09:56:20.739807+02:00" + created: "2025-10-03T10:36:38.541544091+03:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -562,7 +575,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2025-02-19T09:56:20.710372+02:00" + created: "2025-10-03T10:36:38.510934607+03:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -573,4 +586,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2025-02-19T09:56:20.708875+02:00" +generated: "2025-10-03T10:36:38.504074189+03:00" diff --git a/helm/packages/vector-operator-0.7.tgz b/helm/packages/vector-operator-0.7.tgz new file mode 100644 index 0000000000000000000000000000000000000000..415db9c4407555836892422d5cccc489073c0569 GIT binary patch literal 102636 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

sLPtL4Jg0nHVr#HZ+6F7 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 Date: Fri, 3 Oct 2025 11:34:00 +0300 Subject: [PATCH 301/316] correct semver version in Chart.yaml --- helm/charts/vector-operator/Chart.yaml | 2 +- helm/index.yaml | 105 +++++++++++++----------- helm/packages/vector-operator-0.7.1.tgz | Bin 0 -> 102638 bytes 3 files changed, 60 insertions(+), 47 deletions(-) create mode 100644 helm/packages/vector-operator-0.7.1.tgz diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index e5e6b83d..a560dc3d 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: "0.7" +version: "0.7.1" # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/helm/index.yaml b/helm/index.yaml index a7cbb4ae..35cb2a4c 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -3,7 +3,20 @@ entries: vector-operator: - apiVersion: v2 appVersion: v0.3.2 - created: "2025-10-03T10:36:38.56653949+03:00" + created: "2025-10-03T11:33:29.251489168+03:00" + description: A Helm chart to install Vector Operator + digest: 94e6f3d7ad7f41a8edf03e72ffe2f2586f9d43d0762899025a274b1c2329088c + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.7.1.tgz + version: 0.7.1 + - apiVersion: v2 + appVersion: v0.3.2 + created: "2025-10-03T11:33:29.253969117+03:00" description: A Helm chart to install Vector Operator digest: 67fbdd5181070c542bc7b52457ff15962d6b1dcefe495939f076703f71cd0bde home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: "0.7" - apiVersion: v2 appVersion: v0.3.0 - created: "2025-10-03T10:36:38.563952954+03:00" + created: "2025-10-03T11:33:29.249246225+03:00" description: A Helm chart to install Vector Operator digest: 69262a286e22bfbf7571297f05d17dfc8f19e6215faa42f4dbdabea8a5610586 home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: "0.6" - apiVersion: v2 appVersion: v0.2.0 - created: "2025-10-03T10:36:38.561220759+03:00" + created: "2025-10-03T11:33:29.24645919+03:00" description: A Helm chart to install Vector Operator digest: f0e89cc2f3b641588e603107ba4aedc5f1ec585452c88bac46784226e56751e2 home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: "0.5" - apiVersion: v2 appVersion: v0.1.2 - created: "2025-10-03T10:36:38.558942896+03:00" + created: "2025-10-03T11:33:29.244146612+03:00" description: A Helm chart to install Vector Operator digest: e1fe0e96c146c7c275c181e727c8a60f21898cabe90629851a2920d2915f84b7 home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: "0.4" - apiVersion: v2 appVersion: v0.1.1 - created: "2025-10-03T10:36:38.556103174+03:00" + created: "2025-10-03T11:33:29.241579734+03:00" description: A Helm chart to install Vector Operator digest: a916c9e9f81bdbf9f734073fb453a6b67d7a724ed7ff4326d7884b136c103ce5 home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: "0.3" - apiVersion: v2 appVersion: v0.1.1 - created: "2025-10-03T10:36:38.553791079+03:00" + created: "2025-10-03T11:33:29.23920403+03:00" description: A Helm chart to install Vector Operator digest: 582d95c6f63134f6cd815bcb85adce5770e179de21a979ec76f91ab7d8531b45 home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: "0.2" - apiVersion: v2 appVersion: v0.1.1 - created: "2025-10-03T10:36:38.551206192+03:00" + created: "2025-10-03T11:33:29.237032712+03:00" description: A Helm chart to install Vector Operator digest: 3ac5a422f3f1861528f737a0fea077cad5f7b7516db3fe5d392c887d5d3459d5 home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: 0.1.1 - apiVersion: v2 appVersion: v0.1.0 - created: "2025-10-03T10:36:38.548594217+03:00" + created: "2025-10-03T11:33:29.233939161+03:00" description: A Helm chart to install Vector Operator digest: 191ec4f83f11541df19680ce220992c84fb10210f0d54a5acc29361ccfd787bb home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.1.0 - apiVersion: v2 appVersion: pre-v0.1.0-r1 - created: "2025-10-03T10:36:38.545927541+03:00" + created: "2025-10-03T11:33:29.231243725+03:00" description: A Helm chart to install Vector Operator digest: 01bd2e347c5782127511a0a0fbbab72508cbe667664f2c9b615cabb80a4c40c7 home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.1.0-rc1 - apiVersion: v2 appVersion: v0.0.40 - created: "2025-10-03T10:36:38.540964374+03:00" + created: "2025-10-03T11:33:29.226307689+03:00" description: A Helm chart to install Vector Operator digest: c50e673e811b8d4c03ad45c92ce23b9bc54eef4665091e70fab9a4b7e4c6c3f1 home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.0.40 - apiVersion: v2 appVersion: v0.0.39 - created: "2025-10-03T10:36:38.540022481+03:00" + created: "2025-10-03T11:33:29.225419535+03:00" description: A Helm chart to install Vector Operator digest: e1de38c869bf896bfb9f5f615c329d3ccce3bd91cb64bb6fa1c783a40ea290a2 home: https://github.com/kaasops/vector-operator @@ -146,7 +159,7 @@ entries: version: 0.0.39 - apiVersion: v2 appVersion: v0.0.38 - created: "2025-10-03T10:36:38.538992202+03:00" + created: "2025-10-03T11:33:29.224540967+03:00" description: A Helm chart to install Vector Operator digest: 565b148184400900f5572b06ceebaac6340af5e5fd1122b308bdbfcbc2d2040a home: https://github.com/kaasops/vector-operator @@ -159,7 +172,7 @@ entries: version: 0.0.38 - apiVersion: v2 appVersion: v0.0.37 - created: "2025-10-03T10:36:38.537860579+03:00" + created: "2025-10-03T11:33:29.223624926+03:00" description: A Helm chart to install Vector Operator digest: 65e10ee46e6855ba95f51e21ca14abc4411191b260c6b96ec72c775e73c1e331 home: https://github.com/kaasops/vector-operator @@ -172,7 +185,7 @@ entries: version: 0.0.37 - apiVersion: v2 appVersion: v0.0.36 - created: "2025-10-03T10:36:38.536510534+03:00" + created: "2025-10-03T11:33:29.222291666+03:00" description: A Helm chart to install Vector Operator digest: 972b6b4048b6d17b0616786e49915ef52cb7c9573cfb6eb359b6c19b66eabe31 home: https://github.com/kaasops/vector-operator @@ -185,7 +198,7 @@ entries: version: 0.0.36 - apiVersion: v2 appVersion: v0.0.35 - created: "2025-10-03T10:36:38.535507349+03:00" + created: "2025-10-03T11:33:29.221443038+03:00" description: A Helm chart to install Vector Operator digest: d03ba759c42f2bd8d8f1df71702ee4f26a73b8bc28760ca7254af20d811cee8a home: https://github.com/kaasops/vector-operator @@ -198,7 +211,7 @@ entries: version: 0.0.35 - apiVersion: v2 appVersion: v0.0.34 - created: "2025-10-03T10:36:38.534580922+03:00" + created: "2025-10-03T11:33:29.220522814+03:00" description: A Helm chart to install Vector Operator digest: 01e9d488ee78c8603d821f96344edee568ebcb42049d586de37b8df39b372bd4 home: https://github.com/kaasops/vector-operator @@ -211,7 +224,7 @@ entries: version: 0.0.34 - apiVersion: v2 appVersion: v0.0.33 - created: "2025-10-03T10:36:38.533635005+03:00" + created: "2025-10-03T11:33:29.219572877+03:00" description: A Helm chart to install Vector Operator digest: fcde3c94a0fa6caa5f3d333226c95b7c85ede8489d46277e1222a868ed4ec8c3 home: https://github.com/kaasops/vector-operator @@ -224,7 +237,7 @@ entries: version: 0.0.33 - apiVersion: v2 appVersion: v0.0.32 - created: "2025-10-03T10:36:38.532397053+03:00" + created: "2025-10-03T11:33:29.218359973+03:00" description: A Helm chart to install Vector Operator digest: 26323037ec47f1703ea930a99ab4ec8fb93b44975ce969514ea68d4130017015 home: https://github.com/kaasops/vector-operator @@ -237,7 +250,7 @@ entries: version: 0.0.32 - apiVersion: v2 appVersion: v0.0.31 - created: "2025-10-03T10:36:38.531477197+03:00" + created: "2025-10-03T11:33:29.217283384+03:00" description: A Helm chart to install Vector Operator digest: 45b924c07a825e0f7cd3fb534a6ffd16604790d13be1aff59150c045474754e3 home: https://github.com/kaasops/vector-operator @@ -250,7 +263,7 @@ entries: version: 0.0.31 - apiVersion: v2 appVersion: v0.0.30 - created: "2025-10-03T10:36:38.530596306+03:00" + created: "2025-10-03T11:33:29.216352085+03:00" description: A Helm chart to install Vector Operator digest: 03beda549d15f50325028ea29af5f2065ac0b8adf3078bf7dc1312981aa5e7db home: https://github.com/kaasops/vector-operator @@ -263,7 +276,7 @@ entries: version: 0.0.30 - apiVersion: v2 appVersion: v0.0.29 - created: "2025-10-03T10:36:38.52970036+03:00" + created: "2025-10-03T11:33:29.21547395+03:00" description: A Helm chart to install Vector Operator digest: 0f025fc3a924b37b8c4131c4d8cfa437d2d4e557ab9476ed3e69a00232c7dca6 home: https://github.com/kaasops/vector-operator @@ -276,7 +289,7 @@ entries: version: 0.0.29 - apiVersion: v2 appVersion: v0.0.28 - created: "2025-10-03T10:36:38.528384412+03:00" + created: "2025-10-03T11:33:29.214044879+03:00" description: A Helm chart to install Vector Operator digest: af856d41314313e04f15e7143409a9c564c6ca610b0d2eaec3112add8573e668 home: https://github.com/kaasops/vector-operator @@ -289,7 +302,7 @@ entries: version: 0.0.28 - apiVersion: v2 appVersion: v0.0.27 - created: "2025-10-03T10:36:38.527504023+03:00" + created: "2025-10-03T11:33:29.212974647+03:00" description: A Helm chart to install Vector Operator digest: 631e2ff02bbd7f247cb486494fd2af60c57cc551066a6a3858226551bc1745a4 home: https://github.com/kaasops/vector-operator @@ -302,7 +315,7 @@ entries: version: 0.0.27 - apiVersion: v2 appVersion: v0.0.26 - created: "2025-10-03T10:36:38.526580443+03:00" + created: "2025-10-03T11:33:29.212181212+03:00" description: A Helm chart to install Vector Operator digest: 760a2833f4c1a33466982419b079ff18d996331ebacc40cf93b0f55229cdb7db home: https://github.com/kaasops/vector-operator @@ -315,7 +328,7 @@ entries: version: 0.0.26 - apiVersion: v2 appVersion: v0.0.25 - created: "2025-10-03T10:36:38.52569706+03:00" + created: "2025-10-03T11:33:29.211347663+03:00" description: A Helm chart to install Vector Operator digest: fd22b996b071b6d85740ccf76e85cb640fa717c2620748d206d3f4fdd44cbcc2 home: https://github.com/kaasops/vector-operator @@ -328,7 +341,7 @@ entries: version: 0.0.25 - apiVersion: v2 appVersion: v0.0.24 - created: "2025-10-03T10:36:38.522857265+03:00" + created: "2025-10-03T11:33:29.210075092+03:00" description: A Helm chart to install Vector Operator digest: ea257e60ecde063a0d1ed52ce5e3283245b8f0e2daba58ea3a5adb0ba82d7799 home: https://github.com/kaasops/vector-operator @@ -341,7 +354,7 @@ entries: version: 0.0.24 - apiVersion: v2 appVersion: v0.0.23 - created: "2025-10-03T10:36:38.52175018+03:00" + created: "2025-10-03T11:33:29.20912157+03:00" description: A Helm chart to install Vector Operator digest: 546d202b3b9263f789b88335263191098dfcabd5d8698105f37cad24d56a8ed0 home: https://github.com/kaasops/vector-operator @@ -354,7 +367,7 @@ entries: version: 0.0.23 - apiVersion: v2 appVersion: v0.0.22 - created: "2025-10-03T10:36:38.520665524+03:00" + created: "2025-10-03T11:33:29.208283787+03:00" description: A Helm chart to install Vector Operator digest: bf96ddc8ac61e9d6beb8bc763fbf3fa6025d950b29d70d80de6e8a0ea45e0411 home: https://github.com/kaasops/vector-operator @@ -367,7 +380,7 @@ entries: version: 0.0.22 - apiVersion: v2 appVersion: v0.0.21 - created: "2025-10-03T10:36:38.519622275+03:00" + created: "2025-10-03T11:33:29.207029762+03:00" description: A Helm chart to install Vector Operator digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d home: https://github.com/kaasops/vector-operator @@ -380,7 +393,7 @@ entries: version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2025-10-03T10:36:38.51822983+03:00" + created: "2025-10-03T11:33:29.206084826+03:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -393,7 +406,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2025-10-03T10:36:38.517672504+03:00" + created: "2025-10-03T11:33:29.205569011+03:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -406,7 +419,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2025-10-03T10:36:38.517080237+03:00" + created: "2025-10-03T11:33:29.205016352+03:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -419,7 +432,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2025-10-03T10:36:38.516526424+03:00" + created: "2025-10-03T11:33:29.204483226+03:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -432,7 +445,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2025-10-03T10:36:38.515957976+03:00" + created: "2025-10-03T11:33:29.203627979+03:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -445,7 +458,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2025-10-03T10:36:38.515383085+03:00" + created: "2025-10-03T11:33:29.203008445+03:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -458,7 +471,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2025-10-03T10:36:38.514339579+03:00" + created: "2025-10-03T11:33:29.20241002+03:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -471,7 +484,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2025-10-03T10:36:38.513678659+03:00" + created: "2025-10-03T11:33:29.201794885+03:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -484,7 +497,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2025-10-03T10:36:38.513070491+03:00" + created: "2025-10-03T11:33:29.201258232+03:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -497,7 +510,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2025-10-03T10:36:38.512514447+03:00" + created: "2025-10-03T11:33:29.200722312+03:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -510,7 +523,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2025-10-03T10:36:38.511608892+03:00" + created: "2025-10-03T11:33:29.199843802+03:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -523,7 +536,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2025-10-03T10:36:38.54371567+03:00" + created: "2025-10-03T11:33:29.228477933+03:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -536,7 +549,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2025-10-03T10:36:38.54313395+03:00" + created: "2025-10-03T11:33:29.228015585+03:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -549,7 +562,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2025-10-03T10:36:38.542119473+03:00" + created: "2025-10-03T11:33:29.227394919+03:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -562,7 +575,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2025-10-03T10:36:38.541544091+03:00" + created: "2025-10-03T11:33:29.226752791+03:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -575,7 +588,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2025-10-03T10:36:38.510934607+03:00" + created: "2025-10-03T11:33:29.199278578+03:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -586,4 +599,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2025-10-03T10:36:38.504074189+03:00" +generated: "2025-10-03T11:33:29.198717087+03:00" diff --git a/helm/packages/vector-operator-0.7.1.tgz b/helm/packages/vector-operator-0.7.1.tgz new file mode 100644 index 0000000000000000000000000000000000000000..94adea74ab3305eee04e17c2ce30c9d58fd95a11 GIT binary patch literal 102638 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

iq?heYZYqMDIr_Qs_px(yd53jB9C>v@yTAU$ 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 literal 0 HcmV?d00001 From 6c8bdc4c3ab83da548123e75d37d63d9fa6a68ec Mon Sep 17 00:00:00 2001 From: aa1ex <115736127+aa1ex@users.noreply.github.com> Date: Mon, 17 Nov 2025 12:40:38 +0200 Subject: [PATCH 302/316] fix: propagate volumes and volumeMounts to configcheck pod (#203) - Add Volumes and VolumeMounts fields to ConfigCheck struct - Copy user-defined volumes/volumeMounts from VectorCommon - Implement deduplication logic with user values taking precedence - Apply same deduplication to Vector Agent and Aggregator Signed-off-by: Aleksandr Aleksandrov --- internal/config/configcheck/configcheck.go | 4 ++ .../config/configcheck/configcheck_pod.go | 54 ++++++++++++++---- internal/vector/aggregator/deployment.go | 57 ++++++++++++++----- .../vectoragent/vectoragent_daemonset.go | 57 ++++++++++++++----- 4 files changed, 135 insertions(+), 37 deletions(-) diff --git a/internal/config/configcheck/configcheck.go b/internal/config/configcheck/configcheck.go index dc4cca23..cd331922 100644 --- a/internal/config/configcheck/configcheck.go +++ b/internal/config/configcheck/configcheck.go @@ -64,6 +64,8 @@ type ConfigCheck struct { ConfigCheckTimeout time.Duration Annotations map[string]string Labels map[string]string + Volumes []corev1.Volume + VolumeMounts []corev1.VolumeMount } func New( @@ -113,6 +115,8 @@ func New( ConfigCheckTimeout: timeout, Annotations: vc.ConfigCheck.Annotations, Labels: vc.ConfigCheck.Labels, + Volumes: vc.Volumes, + VolumeMounts: vc.VolumeMounts, Initiator: initiator, } } diff --git a/internal/config/configcheck/configcheck_pod.go b/internal/config/configcheck/configcheck_pod.go index 2456d7db..0a2b6310 100644 --- a/internal/config/configcheck/configcheck_pod.go +++ b/internal/config/configcheck/configcheck_pod.go @@ -69,6 +69,16 @@ func (cc *ConfigCheck) createVectorConfigCheckPod() *corev1.Pod { } func (cc *ConfigCheck) generateVectorConfigCheckVolume() []corev1.Volume { + volume := cc.Volumes + + // Merge user-defined volumes with required volumes. + // User-defined volumes take precedence over required volumes with the same name. + // Build a set of user-defined volume names to check for conflicts. + existingVolumes := make(map[string]bool, len(volume)) + for _, v := range volume { + existingVolumes[v.Name] = true + } + configVolumeSource := corev1.VolumeSource{ Secret: &corev1.SecretVolumeSource{ SecretName: cc.getNameVectorConfigCheck(), @@ -78,9 +88,10 @@ func (cc *ConfigCheck) generateVectorConfigCheckVolume() []corev1.Volume { configVolumeSource = corev1.VolumeSource{ EmptyDir: &corev1.EmptyDirVolumeSource{}, } - } - volume := []corev1.Volume{ + + // Define required volumes for configcheck + requiredVolumes := []corev1.Volume{ { Name: "config", VolumeSource: configVolumeSource, @@ -125,7 +136,13 @@ func (cc *ConfigCheck) generateVectorConfigCheckVolume() []corev1.Volume { }, } - if cc.CompressedConfig { + for _, reqVol := range requiredVolumes { + if !existingVolumes[reqVol.Name] { + volume = append(volume, reqVol) + } + } + + if cc.CompressedConfig && !existingVolumes["app-config-compress"] { volume = append(volume, corev1.Volume{ Name: "app-config-compress", VolumeSource: corev1.VolumeSource{ @@ -140,7 +157,18 @@ func (cc *ConfigCheck) generateVectorConfigCheckVolume() []corev1.Volume { } func (cc *ConfigCheck) generateVectorConfigCheckVolumeMounts() []corev1.VolumeMount { - volumeMount := []corev1.VolumeMount{ + volumeMount := cc.VolumeMounts + + // Merge user-defined volumeMounts with required volumeMounts. + // User-defined volumeMounts take precedence over required volumeMounts with the same name. + // Build a set of user-defined volumeMount names to check for conflicts. + existingVolumeMounts := make(map[string]bool, len(volumeMount)) + for _, vm := range volumeMount { + existingVolumeMounts[vm.Name] = true + } + + // Define required volumeMounts for configcheck + requiredVolumeMounts := []corev1.VolumeMount{ { Name: "config", MountPath: "/etc/vector/", @@ -167,13 +195,17 @@ func (cc *ConfigCheck) generateVectorConfigCheckVolumeMounts() []corev1.VolumeMo }, } - if cc.CompressedConfig { - volumeMount = append(volumeMount, []corev1.VolumeMount{ - { - Name: "app-config-compress", - MountPath: "/tmp/archive", - }, - }...) + for _, reqVm := range requiredVolumeMounts { + if !existingVolumeMounts[reqVm.Name] { + volumeMount = append(volumeMount, reqVm) + } + } + + if cc.CompressedConfig && !existingVolumeMounts["app-config-compress"] { + volumeMount = append(volumeMount, corev1.VolumeMount{ + Name: "app-config-compress", + MountPath: "/tmp/archive", + }) } return volumeMount diff --git a/internal/vector/aggregator/deployment.go b/internal/vector/aggregator/deployment.go index fec3cb8b..01f35f1c 100644 --- a/internal/vector/aggregator/deployment.go +++ b/internal/vector/aggregator/deployment.go @@ -152,6 +152,15 @@ func (ctrl *Controller) ConfigReloaderSidecarContainer() *corev1.Container { func (ctrl *Controller) generateVectorAggregatorVolume() []corev1.Volume { volume := ctrl.Spec.Volumes + + // Merge user-defined volumes with required volumes. + // User-defined volumes take precedence over required volumes with the same name. + // Build a set of user-defined volume names to check for conflicts. + existingVolumes := make(map[string]bool, len(volume)) + for _, v := range volume { + existingVolumes[v.Name] = true + } + configVolumeSource := corev1.VolumeSource{ Secret: &corev1.SecretVolumeSource{ SecretName: ctrl.getNameVectorAggregator(), @@ -161,9 +170,10 @@ func (ctrl *Controller) generateVectorAggregatorVolume() []corev1.Volume { configVolumeSource = corev1.VolumeSource{ EmptyDir: &corev1.EmptyDirVolumeSource{}, } - } - volume = append(volume, []corev1.Volume{ + + // Define required volumes for Vector aggregator + requiredVolumes := []corev1.Volume{ { Name: "config", VolumeSource: configVolumeSource, @@ -192,9 +202,16 @@ func (ctrl *Controller) generateVectorAggregatorVolume() []corev1.Volume { }, }, }, - }...) + } - if ctrl.Spec.CompressConfigFile { + // Only add volumes that don't already exist + for _, reqVol := range requiredVolumes { + if !existingVolumes[reqVol.Name] { + volume = append(volume, reqVol) + } + } + + if ctrl.Spec.CompressConfigFile && !existingVolumes["app-config-compress"] { volume = append(volume, corev1.Volume{ Name: "app-config-compress", VolumeSource: corev1.VolumeSource{ @@ -211,7 +228,16 @@ func (ctrl *Controller) generateVectorAggregatorVolume() []corev1.Volume { func (ctrl *Controller) generateVectorAggregatorVolumeMounts() []corev1.VolumeMount { volumeMount := ctrl.Spec.VolumeMounts - volumeMount = append(volumeMount, []corev1.VolumeMount{ + // Merge user-defined volumeMounts with required volumeMounts. + // User-defined volumeMounts take precedence over required volumeMounts with the same name. + // Build a set of user-defined volumeMount names to check for conflicts. + existingVolumeMounts := make(map[string]bool, len(volumeMount)) + for _, vm := range volumeMount { + existingVolumeMounts[vm.Name] = true + } + + // Define required volumeMounts for Vector aggregator + requiredVolumeMounts := []corev1.VolumeMount{ { Name: "config", MountPath: "/etc/vector", @@ -228,15 +254,20 @@ func (ctrl *Controller) generateVectorAggregatorVolumeMounts() []corev1.VolumeMo Name: "sysfs", MountPath: "/host/sys", }, - }...) + } - if ctrl.Spec.CompressConfigFile { - volumeMount = append(volumeMount, []corev1.VolumeMount{ - { - Name: "app-config-compress", - MountPath: "/tmp/archive", - }, - }...) + // Only add volumeMounts that don't already exist + for _, reqVm := range requiredVolumeMounts { + if !existingVolumeMounts[reqVm.Name] { + volumeMount = append(volumeMount, reqVm) + } + } + + if ctrl.Spec.CompressConfigFile && !existingVolumeMounts["app-config-compress"] { + volumeMount = append(volumeMount, corev1.VolumeMount{ + Name: "app-config-compress", + MountPath: "/tmp/archive", + }) } return volumeMount diff --git a/internal/vector/vectoragent/vectoragent_daemonset.go b/internal/vector/vectoragent/vectoragent_daemonset.go index 9c36f1d3..7a8a0b39 100644 --- a/internal/vector/vectoragent/vectoragent_daemonset.go +++ b/internal/vector/vectoragent/vectoragent_daemonset.go @@ -68,6 +68,15 @@ func (ctrl *Controller) createVectorAgentDaemonSet() *appsv1.DaemonSet { func (ctrl *Controller) generateVectorAgentVolume() []corev1.Volume { volume := ctrl.Vector.Spec.Agent.Volumes + + // Merge user-defined volumes with required volumes. + // User-defined volumes take precedence over required volumes with the same name. + // Build a set of user-defined volume names to check for conflicts. + existingVolumes := make(map[string]bool, len(volume)) + for _, v := range volume { + existingVolumes[v.Name] = true + } + configVolumeSource := corev1.VolumeSource{ Secret: &corev1.SecretVolumeSource{ SecretName: ctrl.getNameVectorAgent(), @@ -77,9 +86,10 @@ func (ctrl *Controller) generateVectorAgentVolume() []corev1.Volume { configVolumeSource = corev1.VolumeSource{ EmptyDir: &corev1.EmptyDirVolumeSource{}, } - } - volume = append(volume, []corev1.Volume{ + + // Define required volumes for Vector agent + requiredVolumes := []corev1.Volume{ { Name: "config", VolumeSource: configVolumeSource, @@ -108,9 +118,16 @@ func (ctrl *Controller) generateVectorAgentVolume() []corev1.Volume { }, }, }, - }...) + } - if ctrl.Vector.Spec.Agent.CompressConfigFile { + // Only add volumes that don't already exist + for _, reqVol := range requiredVolumes { + if !existingVolumes[reqVol.Name] { + volume = append(volume, reqVol) + } + } + + if ctrl.Vector.Spec.Agent.CompressConfigFile && !existingVolumes["app-config-compress"] { volume = append(volume, corev1.Volume{ Name: "app-config-compress", VolumeSource: corev1.VolumeSource{ @@ -127,7 +144,16 @@ func (ctrl *Controller) generateVectorAgentVolume() []corev1.Volume { func (ctrl *Controller) generateVectorAgentVolumeMounts() []corev1.VolumeMount { volumeMount := ctrl.Vector.Spec.Agent.VolumeMounts - volumeMount = append(volumeMount, []corev1.VolumeMount{ + // Merge user-defined volumeMounts with required volumeMounts. + // User-defined volumeMounts take precedence over required volumeMounts with the same name. + // Build a set of user-defined volumeMount names to check for conflicts. + existingVolumeMounts := make(map[string]bool, len(volumeMount)) + for _, vm := range volumeMount { + existingVolumeMounts[vm.Name] = true + } + + // Define required volumeMounts for Vector agent + requiredVolumeMounts := []corev1.VolumeMount{ { Name: "config", MountPath: "/etc/vector", @@ -144,15 +170,20 @@ func (ctrl *Controller) generateVectorAgentVolumeMounts() []corev1.VolumeMount { Name: "sysfs", MountPath: "/host/sys", }, - }...) + } - if ctrl.Vector.Spec.Agent.CompressConfigFile { - volumeMount = append(volumeMount, []corev1.VolumeMount{ - { - Name: "app-config-compress", - MountPath: "/tmp/archive", - }, - }...) + // Only add volumeMounts that don't already exist + for _, reqVm := range requiredVolumeMounts { + if !existingVolumeMounts[reqVm.Name] { + volumeMount = append(volumeMount, reqVm) + } + } + + if ctrl.Vector.Spec.Agent.CompressConfigFile && !existingVolumeMounts["app-config-compress"] { + volumeMount = append(volumeMount, corev1.VolumeMount{ + Name: "app-config-compress", + MountPath: "/tmp/archive", + }) } return volumeMount From f20513516c2028d988deb5105cd51bf2601754eb Mon Sep 17 00:00:00 2001 From: Aleksandr Aleksandrov Date: Mon, 17 Nov 2025 12:45:11 +0200 Subject: [PATCH 303/316] chore: prepare release 0.3.3 Signed-off-by: Aleksandr Aleksandrov --- helm/charts/vector-operator/Chart.yaml | 4 +- helm/index.yaml | 107 +++++++++++++----------- helm/packages/vector-operator-0.7.2.tgz | Bin 0 -> 102637 bytes 3 files changed, 62 insertions(+), 49 deletions(-) create mode 100644 helm/packages/vector-operator-0.7.2.tgz diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index a560dc3d..beafa686 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: "0.7.1" +version: "0.7.2" # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.3.2" +appVersion: "v0.3.3" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/index.yaml b/helm/index.yaml index 35cb2a4c..75d50fd5 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.3.3 + created: "2025-11-17T12:42:54.486235+02:00" + description: A Helm chart to install Vector Operator + digest: d1e04fd4039e06ce24d89feb9707a7f4a65f3fd4b2bec6f4f0d937b4c9775c4f + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.7.2.tgz + version: 0.7.2 - apiVersion: v2 appVersion: v0.3.2 - created: "2025-10-03T11:33:29.251489168+03:00" + created: "2025-11-17T12:42:54.484496+02:00" description: A Helm chart to install Vector Operator digest: 94e6f3d7ad7f41a8edf03e72ffe2f2586f9d43d0762899025a274b1c2329088c home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.7.1 - apiVersion: v2 appVersion: v0.3.2 - created: "2025-10-03T11:33:29.253969117+03:00" + created: "2025-11-17T12:42:54.487982+02:00" description: A Helm chart to install Vector Operator digest: 67fbdd5181070c542bc7b52457ff15962d6b1dcefe495939f076703f71cd0bde home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: "0.7" - apiVersion: v2 appVersion: v0.3.0 - created: "2025-10-03T11:33:29.249246225+03:00" + created: "2025-11-17T12:42:54.482541+02:00" description: A Helm chart to install Vector Operator digest: 69262a286e22bfbf7571297f05d17dfc8f19e6215faa42f4dbdabea8a5610586 home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: "0.6" - apiVersion: v2 appVersion: v0.2.0 - created: "2025-10-03T11:33:29.24645919+03:00" + created: "2025-11-17T12:42:54.480921+02:00" description: A Helm chart to install Vector Operator digest: f0e89cc2f3b641588e603107ba4aedc5f1ec585452c88bac46784226e56751e2 home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: "0.5" - apiVersion: v2 appVersion: v0.1.2 - created: "2025-10-03T11:33:29.244146612+03:00" + created: "2025-11-17T12:42:54.479082+02:00" description: A Helm chart to install Vector Operator digest: e1fe0e96c146c7c275c181e727c8a60f21898cabe90629851a2920d2915f84b7 home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: "0.4" - apiVersion: v2 appVersion: v0.1.1 - created: "2025-10-03T11:33:29.241579734+03:00" + created: "2025-11-17T12:42:54.477278+02:00" description: A Helm chart to install Vector Operator digest: a916c9e9f81bdbf9f734073fb453a6b67d7a724ed7ff4326d7884b136c103ce5 home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: "0.3" - apiVersion: v2 appVersion: v0.1.1 - created: "2025-10-03T11:33:29.23920403+03:00" + created: "2025-11-17T12:42:54.475585+02:00" description: A Helm chart to install Vector Operator digest: 582d95c6f63134f6cd815bcb85adce5770e179de21a979ec76f91ab7d8531b45 home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: "0.2" - apiVersion: v2 appVersion: v0.1.1 - created: "2025-10-03T11:33:29.237032712+03:00" + created: "2025-11-17T12:42:54.473719+02:00" description: A Helm chart to install Vector Operator digest: 3ac5a422f3f1861528f737a0fea077cad5f7b7516db3fe5d392c887d5d3459d5 home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: 0.1.1 - apiVersion: v2 appVersion: v0.1.0 - created: "2025-10-03T11:33:29.233939161+03:00" + created: "2025-11-17T12:42:54.471729+02:00" description: A Helm chart to install Vector Operator digest: 191ec4f83f11541df19680ce220992c84fb10210f0d54a5acc29361ccfd787bb home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.1.0 - apiVersion: v2 appVersion: pre-v0.1.0-r1 - created: "2025-10-03T11:33:29.231243725+03:00" + created: "2025-11-17T12:42:54.469521+02:00" description: A Helm chart to install Vector Operator digest: 01bd2e347c5782127511a0a0fbbab72508cbe667664f2c9b615cabb80a4c40c7 home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.1.0-rc1 - apiVersion: v2 appVersion: v0.0.40 - created: "2025-10-03T11:33:29.226307689+03:00" + created: "2025-11-17T12:42:54.46547+02:00" description: A Helm chart to install Vector Operator digest: c50e673e811b8d4c03ad45c92ce23b9bc54eef4665091e70fab9a4b7e4c6c3f1 home: https://github.com/kaasops/vector-operator @@ -146,7 +159,7 @@ entries: version: 0.0.40 - apiVersion: v2 appVersion: v0.0.39 - created: "2025-10-03T11:33:29.225419535+03:00" + created: "2025-11-17T12:42:54.464527+02:00" description: A Helm chart to install Vector Operator digest: e1de38c869bf896bfb9f5f615c329d3ccce3bd91cb64bb6fa1c783a40ea290a2 home: https://github.com/kaasops/vector-operator @@ -159,7 +172,7 @@ entries: version: 0.0.39 - apiVersion: v2 appVersion: v0.0.38 - created: "2025-10-03T11:33:29.224540967+03:00" + created: "2025-11-17T12:42:54.463849+02:00" description: A Helm chart to install Vector Operator digest: 565b148184400900f5572b06ceebaac6340af5e5fd1122b308bdbfcbc2d2040a home: https://github.com/kaasops/vector-operator @@ -172,7 +185,7 @@ entries: version: 0.0.38 - apiVersion: v2 appVersion: v0.0.37 - created: "2025-10-03T11:33:29.223624926+03:00" + created: "2025-11-17T12:42:54.463138+02:00" description: A Helm chart to install Vector Operator digest: 65e10ee46e6855ba95f51e21ca14abc4411191b260c6b96ec72c775e73c1e331 home: https://github.com/kaasops/vector-operator @@ -185,7 +198,7 @@ entries: version: 0.0.37 - apiVersion: v2 appVersion: v0.0.36 - created: "2025-10-03T11:33:29.222291666+03:00" + created: "2025-11-17T12:42:54.46229+02:00" description: A Helm chart to install Vector Operator digest: 972b6b4048b6d17b0616786e49915ef52cb7c9573cfb6eb359b6c19b66eabe31 home: https://github.com/kaasops/vector-operator @@ -198,7 +211,7 @@ entries: version: 0.0.36 - apiVersion: v2 appVersion: v0.0.35 - created: "2025-10-03T11:33:29.221443038+03:00" + created: "2025-11-17T12:42:54.461582+02:00" description: A Helm chart to install Vector Operator digest: d03ba759c42f2bd8d8f1df71702ee4f26a73b8bc28760ca7254af20d811cee8a home: https://github.com/kaasops/vector-operator @@ -211,7 +224,7 @@ entries: version: 0.0.35 - apiVersion: v2 appVersion: v0.0.34 - created: "2025-10-03T11:33:29.220522814+03:00" + created: "2025-11-17T12:42:54.460887+02:00" description: A Helm chart to install Vector Operator digest: 01e9d488ee78c8603d821f96344edee568ebcb42049d586de37b8df39b372bd4 home: https://github.com/kaasops/vector-operator @@ -224,7 +237,7 @@ entries: version: 0.0.34 - apiVersion: v2 appVersion: v0.0.33 - created: "2025-10-03T11:33:29.219572877+03:00" + created: "2025-11-17T12:42:54.460169+02:00" description: A Helm chart to install Vector Operator digest: fcde3c94a0fa6caa5f3d333226c95b7c85ede8489d46277e1222a868ed4ec8c3 home: https://github.com/kaasops/vector-operator @@ -237,7 +250,7 @@ entries: version: 0.0.33 - apiVersion: v2 appVersion: v0.0.32 - created: "2025-10-03T11:33:29.218359973+03:00" + created: "2025-11-17T12:42:54.458973+02:00" description: A Helm chart to install Vector Operator digest: 26323037ec47f1703ea930a99ab4ec8fb93b44975ce969514ea68d4130017015 home: https://github.com/kaasops/vector-operator @@ -250,7 +263,7 @@ entries: version: 0.0.32 - apiVersion: v2 appVersion: v0.0.31 - created: "2025-10-03T11:33:29.217283384+03:00" + created: "2025-11-17T12:42:54.457945+02:00" description: A Helm chart to install Vector Operator digest: 45b924c07a825e0f7cd3fb534a6ffd16604790d13be1aff59150c045474754e3 home: https://github.com/kaasops/vector-operator @@ -263,7 +276,7 @@ entries: version: 0.0.31 - apiVersion: v2 appVersion: v0.0.30 - created: "2025-10-03T11:33:29.216352085+03:00" + created: "2025-11-17T12:42:54.457232+02:00" description: A Helm chart to install Vector Operator digest: 03beda549d15f50325028ea29af5f2065ac0b8adf3078bf7dc1312981aa5e7db home: https://github.com/kaasops/vector-operator @@ -276,7 +289,7 @@ entries: version: 0.0.30 - apiVersion: v2 appVersion: v0.0.29 - created: "2025-10-03T11:33:29.21547395+03:00" + created: "2025-11-17T12:42:54.456151+02:00" description: A Helm chart to install Vector Operator digest: 0f025fc3a924b37b8c4131c4d8cfa437d2d4e557ab9476ed3e69a00232c7dca6 home: https://github.com/kaasops/vector-operator @@ -289,7 +302,7 @@ entries: version: 0.0.29 - apiVersion: v2 appVersion: v0.0.28 - created: "2025-10-03T11:33:29.214044879+03:00" + created: "2025-11-17T12:42:54.455461+02:00" description: A Helm chart to install Vector Operator digest: af856d41314313e04f15e7143409a9c564c6ca610b0d2eaec3112add8573e668 home: https://github.com/kaasops/vector-operator @@ -302,7 +315,7 @@ entries: version: 0.0.28 - apiVersion: v2 appVersion: v0.0.27 - created: "2025-10-03T11:33:29.212974647+03:00" + created: "2025-11-17T12:42:54.454738+02:00" description: A Helm chart to install Vector Operator digest: 631e2ff02bbd7f247cb486494fd2af60c57cc551066a6a3858226551bc1745a4 home: https://github.com/kaasops/vector-operator @@ -315,7 +328,7 @@ entries: version: 0.0.27 - apiVersion: v2 appVersion: v0.0.26 - created: "2025-10-03T11:33:29.212181212+03:00" + created: "2025-11-17T12:42:54.453874+02:00" description: A Helm chart to install Vector Operator digest: 760a2833f4c1a33466982419b079ff18d996331ebacc40cf93b0f55229cdb7db home: https://github.com/kaasops/vector-operator @@ -328,7 +341,7 @@ entries: version: 0.0.26 - apiVersion: v2 appVersion: v0.0.25 - created: "2025-10-03T11:33:29.211347663+03:00" + created: "2025-11-17T12:42:54.453002+02:00" description: A Helm chart to install Vector Operator digest: fd22b996b071b6d85740ccf76e85cb640fa717c2620748d206d3f4fdd44cbcc2 home: https://github.com/kaasops/vector-operator @@ -341,7 +354,7 @@ entries: version: 0.0.25 - apiVersion: v2 appVersion: v0.0.24 - created: "2025-10-03T11:33:29.210075092+03:00" + created: "2025-11-17T12:42:54.452119+02:00" description: A Helm chart to install Vector Operator digest: ea257e60ecde063a0d1ed52ce5e3283245b8f0e2daba58ea3a5adb0ba82d7799 home: https://github.com/kaasops/vector-operator @@ -354,7 +367,7 @@ entries: version: 0.0.24 - apiVersion: v2 appVersion: v0.0.23 - created: "2025-10-03T11:33:29.20912157+03:00" + created: "2025-11-17T12:42:54.451084+02:00" description: A Helm chart to install Vector Operator digest: 546d202b3b9263f789b88335263191098dfcabd5d8698105f37cad24d56a8ed0 home: https://github.com/kaasops/vector-operator @@ -367,7 +380,7 @@ entries: version: 0.0.23 - apiVersion: v2 appVersion: v0.0.22 - created: "2025-10-03T11:33:29.208283787+03:00" + created: "2025-11-17T12:42:54.450342+02:00" description: A Helm chart to install Vector Operator digest: bf96ddc8ac61e9d6beb8bc763fbf3fa6025d950b29d70d80de6e8a0ea45e0411 home: https://github.com/kaasops/vector-operator @@ -380,7 +393,7 @@ entries: version: 0.0.22 - apiVersion: v2 appVersion: v0.0.21 - created: "2025-10-03T11:33:29.207029762+03:00" + created: "2025-11-17T12:42:54.44935+02:00" description: A Helm chart to install Vector Operator digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d home: https://github.com/kaasops/vector-operator @@ -393,7 +406,7 @@ entries: version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2025-10-03T11:33:29.206084826+03:00" + created: "2025-11-17T12:42:54.448068+02:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -406,7 +419,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2025-10-03T11:33:29.205569011+03:00" + created: "2025-11-17T12:42:54.447643+02:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -419,7 +432,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2025-10-03T11:33:29.205016352+03:00" + created: "2025-11-17T12:42:54.447209+02:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -432,7 +445,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2025-10-03T11:33:29.204483226+03:00" + created: "2025-11-17T12:42:54.44676+02:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -445,7 +458,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2025-10-03T11:33:29.203627979+03:00" + created: "2025-11-17T12:42:54.445975+02:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -458,7 +471,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2025-10-03T11:33:29.203008445+03:00" + created: "2025-11-17T12:42:54.44548+02:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -471,7 +484,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2025-10-03T11:33:29.20241002+03:00" + created: "2025-11-17T12:42:54.44504+02:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -484,7 +497,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2025-10-03T11:33:29.201794885+03:00" + created: "2025-11-17T12:42:54.444616+02:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -497,7 +510,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2025-10-03T11:33:29.201258232+03:00" + created: "2025-11-17T12:42:54.443996+02:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -510,7 +523,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2025-10-03T11:33:29.200722312+03:00" + created: "2025-11-17T12:42:54.442767+02:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -523,7 +536,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2025-10-03T11:33:29.199843802+03:00" + created: "2025-11-17T12:42:54.442243+02:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -536,7 +549,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2025-10-03T11:33:29.228477933+03:00" + created: "2025-11-17T12:42:54.467253+02:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -549,7 +562,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2025-10-03T11:33:29.228015585+03:00" + created: "2025-11-17T12:42:54.46693+02:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -562,7 +575,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2025-10-03T11:33:29.227394919+03:00" + created: "2025-11-17T12:42:54.466432+02:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -575,7 +588,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2025-10-03T11:33:29.226752791+03:00" + created: "2025-11-17T12:42:54.465943+02:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -588,7 +601,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2025-10-03T11:33:29.199278578+03:00" + created: "2025-11-17T12:42:54.441691+02:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -599,4 +612,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2025-10-03T11:33:29.198717087+03:00" +generated: "2025-11-17T12:42:54.440609+02:00" diff --git a/helm/packages/vector-operator-0.7.2.tgz b/helm/packages/vector-operator-0.7.2.tgz new file mode 100644 index 0000000000000000000000000000000000000000..e3868d96f085de7d7af6d51c798a8853d0d9d197 GIT binary patch literal 102637 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 AqEi_kS%SoYfYA^q;_)Iz{ED5 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 + `; + + // Summary Tab + html += ` +

=(_F3}K+lZ>3$*-($7Di#@%Lo1)`BYEGLzUk>WIc;X%oCGNw)A0(azN&5P zkuM&*p{l9Eqw?KECZ!y_w|Fff6W-QS*3fuCoz zc6@}`q}rzm`U!;k6&H%^@sXwc!mhy zZzdLTJ1WPP9n~)BIw7K!d>bleDL##YOsmHFu%rYx`f~Kyhe9`x@-yjOxd?Fqf#VAt z_yat^giIiXn$cm;xO)=RrVKEdYDrwctZybqsYD)$adE;E~t}YC(s|^SZ%z*#!P&g8;Ldbkxmh@3R%g&=5&d4{Q4l=!z@KO!BV}~5YC6e z+>KOXrERx;Lq~j$p!!_CWW#aeUAZ+chGx&B5pG^B%JTX8~(#w zrm9=(3XuOLV9G!V80d||FK89JGE8V=&qqOaXo8x|Z=&Y@ISNC0=`d~>wUM4AVCJ`g<1}ioA)+f*W$4!wd!p8sQ)!g5B7KQ0NdIC{#ji9Y70%-& zDdW=nqymG2S?|ATv7Cy9zmYlZOqPtR%o^~;4E94`8};Uu$cWZKlM{a-i}p{L_Mdzy zDmr}$|E2f>i{PdlxgJRL3&#E2bj`<=#jwx)MmF|acG<1oIyDfw>0##`>4yly6{{?! zpo42DJNOacd)7R82z!I~pzHTT4Yv-R_MX|as9)N`%#dmjfszW_Ez5L(#@MpaD|dnh zjDpgOq;HQMo`(L3D${H>y?0`f3!eJ%6^bQS!vk5xOVx0xo(MsI>LOgu`*b^uBR!i% z!(^wGJYmcxPocV``Msdh*~%lc;&D}@2?pe6cWhic8`7cx5JfcsSG$JfZmoRZM=DV` z*rwD<(&0I8rjfd?XSfi?Q9RF;G5j!CT(8dZ z$c!UP>0!3{B0zH5(A!~sw$i2;_jT4IPse#SF8s@H^cV&AgS1f4)DZLeeL>>C`Im!dQRxR`{r0jN#EffQvH85nU#5<^A6TC&yO^r_eH%Km4@;Yz z7k>UJ>*a|*t6Zv{s+N{!29FDVcg5KaEXK)7zAi&Q<11wIsvSZO?yqWb%HOk=h^Pww z#>`FHws$BoaBWg8?FU7QB#MV-_VHV3UJ^Vxm`y!Qb2f}nMVM6eipo+Z#jL>hgAV-> z@np*mh?oGw2{0*_MpNtfFULnOEg!uP@vCE*)ATYcpNpxF>T-QtkCP zqy|NC{D|$*MNI1>1dDU7pnEX!RU{Bv5m7{=N|y>d-ayqo<*r)xNgo7t5MuM~f= zp(5Y;b1Gm%>CIJ&|KyYd##NYq?I-7(J%3EG!6|?N3Y{u6xFdnfjU1^cQ&wY=ye*b! z3~-;uRQL_U=Z}@>0C;cM{4%MMDKgg0$0{uw49cB=clc9xe+NniTEmlbS}EEjA;BsZk8$2eIJ-zpAJcD9x>^O(Qhq#{z1y>t_FTvF5~07 z^Qx{H<&LWy$=M};qq#hR68JuT_<;KlK;v8-*Xx`F@iK_h74sh{xPI*SjqCNABR1dJ z<@i2EwAl8X4e(DOVSq5zdcT+2rI z)^$jD?v+DWubFjN|Hob48rHHg#lKOK=uB#UoESvJxls~uYV=2CK7eECMrAJE$^pLu zP5Yt}*bmC!{_nmyiR~o`{11ylV!(Z2jPGo3VT`~%H;DS*Ha>E#A5U=oZxK@$Ww=-% z!5}f~pkZ^j*x4JlS#@!DxrIcf@?_&|+=rhzjD1}$UOE{&p3V^qf{`rS>O`0>9^Vag z9QPtr5gsIBdp*})2c{u0=7e&_cS^CqX|&DEx`*7F zqA(i{?li=1c}O^GIgDm3hjVK_YJuR!m77!8&`*bQ;lXoM`eRvhVjzb(>Joy*ORMdT zax$RPoa2x-@%AUSn|rg}b3(G8repa`FyDv+oQ&~kx`i>3x^kC$RUBSo_azwjt2eZW z_2nne#M*C!45~CuHb}_wHiKxvaJutWkDYGa3AFZ%g91tJ^#^3nm_hL1D};Etmvif! zTB2M2c9x!5@cX2h(IjeUg#CXNx6{u$Asg^>EN5ax4eEctW=;}NeS$(%_TBV9FAuQw z|5(Uz&Nb)|H8K44X!}tWWd>fsa3E4dhaJ)8U@wP#5VzZh=gw~N126fGLRVIXw)cR9 zP)d-W7A-?uDW@DONULW^rDuCnL(`g_;ii7)#%^Rh!?pa)O#Gt8C9TcP{8-KU>^zy| zDcsSN+rp1TeIHCn-K56=H2SMkAl_TCchWoJOwyzIdo3Y5t8x$Blj_Fn7V5?;9JtiV zthKYr+}=A0ehiZ5QMpsgqM_-Vz{R!Jk4-Uq{^AYI5*c-#e*zV0CbB!mX83+VD(wz) z6P`#(DLyuds#_lp_V&pwJ5iL*r)mT(S|zTn0847MM6qRDRl#mL8*NpadyjbfhTnNr zs^W^^c02+@5VY0XLU=v?jD(lVH{0$43ia16*79PB%ryyck)LPdRg|+66h$6rPGe+vSz5^?AW}XUn7?ZL zV*MNAg3(e@_{?cACP0~wFbDXRU$G^TKhdOkj2BY9%muHjjVJ)GTR6M4Es<|MaKUSF zuu~hEP&CcVCW-%-zD;;jlC9R20#`vW0kQl`Q_Iq;XI)z&0Wm*H zA!9R5$xT%)0ns-C5HtLtl0xBC$<%gpJ`g37APfJyB)L7I#M2~q#$LXOGVCv*iyrEA zwVLEbSN?8;8YZT+OlU1W{$AwXjoX!|NNkXJz?A=>6>6pRnboy6XRyK(hH0bh)S8h6 zzYqU%Rm9X?9BLZ2`;`q2cl=QHoFRvBS4H;QvF)i<;em0ZH)PA)?QYZD?cAco#E8WU z4kIxLh3*}FS@OSE&I7%&9)3u531}>w<%HMe2Vk@dK)Egac>vVavlyjS;>Sb0(|zM@ zS|fsTzS<>2l7pTQ+lWVimJ5g#Z!3C4vl}hxUx4xylp7NiA7`nOxRap8(#>H8WtUb6 z2`3U`&%;?KY$n<88zzyHOu0(uYbt;_&T0@AZ(@$q9CLW}fv-4}4m;Au=qtT@X9kFp zHWnoQrn5iHSjRL3gpDvv#$^E+r>CC0oV?3-UBVH@OQuMVg0FH5<6cVgz74FLIGn`VGf;@YB3hhKn9-RO!;G^!}NQeO>$uT(V?0^J+BP$+UWW(Xzlk=Gd02b^n*B? z&Nkll<$w?;0I<`)8h}6lkBi|d9{wz4Ku|hB7f94F3t|F?VLnWFjX7}iY5(8-#sy#Xx{8HL3Z*zI3p!%MT81Mq^`q*s69$SbwMr+EQfcMHg!uPFQx z0CF8Rhkd(fxM(nFg%EzX!S}ciY%&=G&R97z8vE zB6G|(X9vQpyJ~zF2D-viSpbjKPb~}eE$``Le2eF5RO|}%X*>T%a=a$f3F3c93j_W) z*WKKuPfM^f*DQN-JL@0UObGux*Q^UJ)ZXQVNFoUoP&Qrw>y-p@QGSfT{Be%*H6f7# z5}_*0H%|pn0)SWs^UVR=X5ltC@O+xt(mVK#hEv16QFpWY>vg=i6u-4!i|Bcg$B}U- zcw-L;iK{vbkA@%5_#Q9NR01}Pvww+va+&vja}4c10;RF(?=C5Geg__=m5pUq-V*X- z<2~f7E-}$ZLu3g!_a2*90^>=crdIp*7eS%R$Bx|Kp@%>esxYH>0q7SbbWeRn`Q_9jxyTEt zCzY9WN@zz5|Dbpe7Bsq()G~D%bIHC0h_kefQ^IN7ggV)Xga|p&dnz>Z+?CrVJKU_(UsAc4^%sV|iD((KRQB^HmgY{TSs0WCVfSlju)S%fMGr zSGMP4l>f}5e2__$@_&{Pmj3oMF&o2_C3?G8^nHnF3D?EXD$t?g83O2q6TSDvAP2;< zh><@esrZ+i`Z|-UCAOtBgsq7CtF*h-yz-b7T=1|o<~B0XL=T@ejwT_(Ko&wf1|OAX zKBBEWn4H?LTxenI!7)7lL{*+1RECN+e$?8^<_0{}1{-|T28qE+tN#rQH8IIZ+Upu? zwadffp$}W2k8glVn^oRhjWzp98=UKZb@hgB`1JV57RIInb@jVBzHDS>O)WFvy{2Mz za8~kgU8Z7_cn;cl!0@yOIF=1mR(j|C(m|(RgO%}ju8pXy)?$-ZHsnTXG9bMu)E@&W zOWBr0&-}4E+5c1s>E#BbPPQOg=zlj@$_wJ6HoStQagv#FBL5^aiwV~RJ-(mu_sv@T)6q&ic2!dz9@=cs-x44X1z|Qg{R&ms1ClEL#}YK9 zdr@{hh01$|J;VR&yb_hydT_eD5#(&Ximrq9H~_SfouF$S2=Yg72j+<9*ChPHE&sRi z?~d=Jpp}JZo35K1IpfwfNc{JF#YD?RqD_s95C|d)dazoufrz7iO7? z2WlCaIqNLTd`pT!5!%IfR-wB^C>8uga*)~h`u{f@V;MFJ&CVFJ4}TwIG2-UG5UTS` zshjj+#FQ0G-LEw)u%;R$*lqE>-(-J>E0mQZ>ds?deqUgb@GRyfK}U-^&N*1k-69xT zz-l+wK)-aR~kGp+7KQIZ02Vt94G z$BvA~ZYj-CW{AanXBO7P(4Qe`Wdugx2mvBNOa}(|(w=s_b%DGS6BXupeX~cy*g9hi zd6eW+7{~kG-FdXxbrG4DgE17jZt*3z7EXJ|a|&&|T_Vl)3BFF&NeWw(6(Ilvnc@GO`)OwY9j zCBW2@ofb{tezoR3sj;~H4rZd2zbAb{?jz+0RLHIOqQa!(#*=G4i}AC=p0-sKYCf0?rX#3x4U(t^!Y##mIbix^U7V?Fgo2 ztv;>#u%{;7oWV(z-XI@qqL4zvPe{pL+ughK*7m#ESt`t30cc-=W{f?3Fik$a`G*P_ z)!mt{S9TVC{!+(vW-69a@0c%wBVRYbTxx2+|FC#N`+n~qEhAqJ&|fz-AlA#;=f4(e z>X8va@3+TH;MqjaU1F}EWYiG<=h@Le;?;v~2wol#!q4s}l`0Q>PI<&Ab!&pU*n#*P zZs_(Wx4r8jjN34CXCCoOfd;5e5>$o}<8>K2xQO#!z}~@fz1nOS+lB$fihP336nm3I zE#C#D#zboe&7+l8EGWE+P>m5ag>Vdeon&uX>^{3TMF}3R*Q-T!Zk%!*USXmd`Kjwz z=XDT`9n8#jAJl7_rV)UTh+=*~AUjF?hQo@LY3XgmU4MiiO+hutA;#0m*i$uAzu{@E z#Gl|BH>JHq8xodqT18v;4Na1VaaKR6z~87cI6L~V^d$0lQ)T%#WXas4Pd0mjBTerO z_0b<*(*@e7#at;;KsKDZbN`yyrwQoLOh6J^0oaaYU%PZ1q$gY#YR)j9#D>Fz~_diC=!Bcr^A<0rMsjz|TzFW3q{w&55m7c@C3 z!&9Hc#F5s9>?Xd`ZXn~6h4|4$%sS<|*p{Y{cRbApC#LN%kZB{kH?-hBnTP#IMN<7cB!>Qn_sxev# zb(VWgP{)G#K0Wr&ZNh?~WEXv@61f<`DZL*~o|F}7kvbIz`#-R*@o6wz93?)vC&a_J z!L`TDG;&6D^imlU*smFsr1bt6SMm>GBVaj|Yt;V~h9F^Is%SqRq^Eg0!0x(Vq_!B~ za1$3Jw%L+wOFt$qSB!FUeH>(HlsL{#=^S+8(9SutI1%LJQA%jQ-4N;2*52w9qOD$2 zo4YZ_VA*aEn@u}VpPDX;eNpxM>fVf*wfNz4_3K6jGqU|v0ZCr}tXtdYw0=5Fah<5@ zHdAZg%_L@f9<9fk-n;d@2j$Ewd}q_#g}A@{5dhe3DE3K9=9gTAt*u5`+xgI1da_d7 z?$02qUGP!6m&1Qht3h4swc29MG+e%Al6m>wU|c%JI=O*kV&&~y&uJ>z?fQ?$|FC^i13}0Q>yYb` zM$B&;HK^2N1!FprOKnTLz|FTZLa_ihng7V2(oW z9LOB54LnMI+mJ_YOR?o)uHWhro8qDH=F-N{G!pEraBv(uUhY6RvN;deD-QA z(%H@D@%2`LZh;|SAl)t%(xwe2EiKrN3b1G%3UH-=pt-OsXcXfpbmQB-upx<7 zx%aty0~e-=%^hhQ{lmRhRQhS4yNzDjNvBleFJhXeDW|M{Te6Fji}S14;g`KJrz-R< z(tA_$r>%y?UC4!_s^|XbgB!Q{m-+=5kOp2m&CQ{ZWx}6;0Kxag%keZ8CEXXja-nt5{Zi1iCBDXg`Zw?9}2O#CJ+p6x)aTDs)5)uE8ue%6lTv zVYhZYdhc$Q^`6YZuNl;cA1^?K{_SXRT~$+|qe7MqNG|-#>BX2;Czm{te~BGD9`QL7 zc8-dTd|12nQ{3t`0r?oCzk9vHT8$-MQPi$_R^0L>KD55k%*Wow+nq$tiouui70Ggu z8ZFK-X)EDZ=$=)fq3tT2YgJ0tFGL3em@#IFJLkW6An9kl@TC8!RqcDaT-$W9sZ|SGm?K>QTf@J+vTVG@dm}gvbGP>f*gkGP^si zPAAtStn?_c2TZv0)N4$OjCMeC>511^wOgY|s;M1G(t}HQtCr<0Wafz=mw`O-97#SZ zZrd~;^&?xw*$J-eTV3yAct&QSh}5P|UItw)69hPR9!}0+<9Cq-4Tj~CJn?ztXqg#0 z-h?$Q3H@dhsXW^c2|x8X%Q$#noAG&r`%HweDIWp0m;f4{Qc zz@jdySg}%_V8kzP4)q$lwXL02OR8lLNf=72<|M(u6g3HfB^_^ZP`zT_vwx++mBj98qrYb2-W3X6AwCp zn%6CxqubMG*M<>mbq?%9sEW5tzA z;5JpVw~fWr*x8*-nQEsbF87<{2!f_%+;atkCCsBXq$lFl)FnS;L6D(~z!>kl$HUHb zdOlYwgWSHO;9&%0O7}5{j!XoH?YZ4Lq=8HaPUg#Lvbt-sTdIVaeJ9a&^sXZG{sqX| zyNd%es`T29VD#S%j_hGwp$afZ=DPZL`@HvH!H~7r4~gz8=}C$_F)5laxZuN(1wCtZ z-yWI@vznLsqZv?H+Y+^m{1`jPS(O>|dwK54h<%T5w9LKu=6f1*nhI<0WA7;X5Erds zwQe`;NUA1J(BSZPGFY&cWpF+T0z{qTUDK5;oraTpb7(cM<2=241h=8mh7A+}pRMQ+ zCv+|jYo(r7?@Tt0+4CC`e%GIbpj=K^?dNC|s7ZBIF zMLJlK5<-8(5T?UQwWiw}-y!it!;Z{W0!<{G& z_{;7hTwD;DT`-p;O*StzdBERBU!t5cp^PE;U#L4EoC3>BKRhct{w!E=j07SE#}2aL z!>nzV9z6A*=rrc)*A%VHd9#bs@1(H$g>6$(q8Jfqx<^7vX+g>gGA4WaQ_g==f!0* zpD%7PRbf!A1SYN7gb}d7l91d0mYvWc65k5eaYz_*No+wRDqIvzXB)`jAs8H6>Yeng zrCz3jf$%-;H=y>r5t1hYZ-g`AxDRX|nJktTYLrGbu5UdVN*nAqoH4>CfHIaLRMPAFm57sb8N*g*{&b zUz4Im*WI%`!Yp6KWM)?eHqWJ~5z~c|7-ek4H0AIfT-Zvwx*dLIWYu5R)TSMl!-6D` z{UoQ{J#`%ng*@5{&oea-m-@<_D7(5^$x2|P$o{`Gw6oRNVsjbw9}Cc-w(rWa*Yk?| zwI+YYH%hnO|16Tb3IxGB8TJQ-=@mMP1(#h~G?(}lK5a!3w%&3{f-qd9l`LX&`A1e49K3<-zZ|`|9 zATTwwhe$5wX-BXMK`OHG=rr?lA}c}hg)n)0M((9cc%7M3f7V9Sc7_>LlRz`J#Pzn^ z@AQqRt%O4-g(*|EEQntWXY605AfA~e>|i)`CU5e6mYogZvUI?q(ziUWhpnlylm7cI zrfCk~&1)4u{Q;9oZX)CF!4Fjd(dVxnt-PS;Peb=l;pY#k&QI27Z-b^c-%o+wT3p*I z#NCx|-mX95rX0Brh=VTAJhbQv3HC}$beJ`d<=9fd^I7poH5{V6GH|d>PJGsya&d;n zNq--gNaYdo%#H>Ks3M!dzK%8I%8J)f$}ELQelPd&DQ&;9fVp&>f$L+giK1x3f{W8~ zJ}&3k@L8*nk(LVe%doofdnG{lfJ?xXny;_(ou!^MN<3jc9;x{odC2{q%Xs#bpyKz{ z5lgzx;7xcmCSb;AZkImpL1Cw{2*WWudL503RL|)OtNab)8p<_h{wa$|^Y!a<`zwyh zuS63*$;L;|gth;n!-60a6=IXYQY022F?gllIV&F_`R@>C0YJWJ|0$SZ`9-7IIL+Jc z>cv#_8c&rqt~VPx-1T1Z=aj(D7JZUJa~2WY=`zi87Jr1jw(G3Uzk@TqLUFS$VD$BJ z4)?sC%5n}TLS>jSA8E|TSp#V9$U>cRdruxxZa(Q43rhM_=~T<^>L|dzxX>CS zq07n=Ur-$|IpiI@ocDx-DT-22V9j;;43DeO$~K2~<<4lVQy78~bz-+(82IMBCmjGs zU8=TQO)X=Nw%$6`X=`2qf9omtxQMp+Nye@5>uxHvh%p&u^*P1b9fV`@_IJ3e1oh64 zbuXc6jMA_aIe@|GnMsXVQ>}V>h3E9RL8&00o{3_Dx*>4noagb`nr|~1t8SOsQ;2;# zLXz6K-Xqv$;tb3(ZM(0^=c?Z*rjZg(Mw~S>2xQ}t|3zSu?8nfI2uzE_0?hcsNCn#T z^@F;By|^1Lr*}ik{=e<-&zb{UuWB=smX+JZ9~lq6R^QB(twQ+0b1G z=i_G0897q@%9UTCG-;pKdwqY7{qV}K=X;~IW?BCdipRk(oyErG5NB7KRuf3|SW#ew zGL_Q5kjbdA3=}?N1aR;UH$I#Y5Ts`{PzZ}HWwqnO_$}!xd7v6{z2D5u{IYkE8x4|- z!&sgqX(6d_jy?{Zy>c7Bp7-o!K$S%!gP(jI%(!Pb8I0_DA5DhiTvFeKGE4b+FXuy= zGlMI;=zZX*`8DSFP13gPrO3yBD`7~!wX?Q+M10!zh$m6&X{%(uUpr_YJwnCm_DII; zjX7D*gqQ~JnE5tMxR>xCLLjCrP~@+S52p#Lc=XVPVxTYD^wrG9GgIz?sa<|cjEzO` zFDPHDrz32 zuKH0h2!2KOmo|?|ojy%t={@$OBdoQPOZV0m3Sr$i9!It>lZCmc$R%Im3Wh} zF3-fZE_WB;#;OMOdv*C9Rpfiwzl!BKa#q?@S|;e6{_3G%iKx&a738R-MWsSbEIfpJ zl7RdKwS!0tIwhjH;IG7?qERs`qdsBp=)WfDquR2`0p3h^84qi;q(TBt%{W;z(;|vq z0rOJIG$N+TuaV`5Y4RFb`Y`qkzWH_%!ukGQqDW&u8Vcza{#T5M{Hhqh=gnI&^bYJ4QiJ3^U zdp^SOsf2}$w&Wu3HiYE64>md*6&{4{!BhTZeCEU5!9_~)9YikdbIKf_Li-#5|GHlh z?Bb4e%&nFuvmdIUS#!1b4E{Yt0s9xqT$ zs@3mUll_^qdBHh=h-k;VdeRdKLsk-5yq{LeG})J^Bm-zi!4ZSIofnrTP?Cj)(i`u> z#s2;Eu?%%LWEro*h-l#g%gWrz5JaNiZxNekiAP?Cy+-<^CcYLEei_u53u*-QZd=2; z_qgXtuRREcd3?-^|7)e@+Ifr}Q}r)@`o&!`9HPv0g+f{nM!JDM3N@B9a-zBm+aaZ9DhaPAG)vmF zN=EFk&^mx)F0zb2CnL)06EwGEj%H+`k;IgVgf+`^f00D(GC{A}s-#I6l!0@FB|I-o zv|Q4=%MytaYurmgaww!yW&D~_NoQx(e{>MoicZPq>#eyEkrv^SScA2S{mmAYb+Do}hs%Z?G{iYW{{Ayk5{6 z^SI69A5Nb}k`EbR=4HVJlj_3vN1}V*d33ov#TFS&mEJtUzou4Ez&`%SNicc*Rp(bL zgavH2_P;>)^_R}#s!(o~7HlmoUm?zwCc1XgZn0ti=RC&S;Jw;FVvgake|(0m?9E66 z-t^{Z3zvklGYae_P^e;(`b8{+%v*N2NoC+F{^BIKQP-k0X_RK5)bS@`#C+97%bj^C zG$|MEv$!+vAu9<~_xH_Kz~C64#CmKtL#poKh9z!5TYG?tm6*M}y>&Qi(+N9YS5y1U z#i)@3_{e1|S^La-h$v5@`0z_c%}9+l+yR*d*uV2k^VbfA^$g8;aL&os+xS>uq`qkf z2#I6H^UbP}{cClRz{uOM(yNEb>pJ_I*~7v2*n}9!LH(Ziz?hMnukPtQu=3k zYfJIm#*8htWe zGHp76qP~L%L{SSfXFSbjVyGc7D~D0;h3)!Fs;qJ zQ1uQke(Q}VSv1@2;5i^NTd>ma)xi_ilJIm9{TxWb`&aR4b@YHs+AwJ{c;;|=X-iT}8rF+?$ zyXB|;6?Dr0B5$n3t)-F&K27Hc-<&k4`G?nGpZ8ewE9X3kEAVCPP2%sspbAVY%S?G5 zYS@?Q2>9Yk3|qtH8BK1P#DrT16_-K#JsLG%m2xwkio>x2U%&tXf#x}s=S{E>{HoiKG&M4k za=@({nrQ+RYd+BT7h#=Mf4Y$3&Yao$<@ssE$FJ!Xz-(9G>d}|X1IHCWh4U3q{CAkl zHY1S<{LDRZSwjl8Hw-~uR65f#n7R6KmeyPp>d>Zmqt?^m7q7ss=jl>>SeLb`+u89F zkW(%GzgU3ZUHH(-$~9IkwEm*@TYwt=yA+g6y*oRIH3EuTm*i=HSPIEB3cyRlC`zZn z-mllIjsmqyYW07T|8nEqJpFrqqM%TUDJ6?x7 z<#UP&ZI!&PhUkBYq8UW_wDIr&C@M#V?k|nJAcvyR?v^269F$zCaTM>ktgQ~jSOQi; z@o2_PGqvB=IAv&h)+`taMQN}XtyxZZoZ@!+*jv?s)F^5$lB)@a)T|IFf9PfxfpJgb zF<72l9#9uzo0M19I5%-J6BoT^iS)F-mtDBKREh`j90}1`0Gkae6J6(NG5|G$KY$2< zy})H2af)u1(}l|cmUn_Kz5>ZQ9&eGanOonFr{f=dPE4$?8{;d!@Au`cZ)TtG$N89^ ztWWdz%jMYE50q!8`}64O?e6;H;28hsWFPze_WAD=YhT2S|akDRd0oT4lFq z%WhFd`3~>_4UeZ2_V@4mY0d{59u8OuHM){W+LDeuY{NNuiXnR?keFvjvJ$aaN?{;r zhO8T)+jlwrCp^MLk<^0pR#zpTZL(;$IF2sWN8bo5ae=AS)1tRNH6^@4U=xM9Hh{}T zb$W(`qb?T7s4l&q+`gk-nL!#K4sOYIIm6Q1P2rS{n`1twFFnO`R<)1JzU9ItjgX9C z@a^cYwlUU}hBk>!8_rUa%6G>$aMxk$RmRRib$Bf(Q7XF-Pw$S$TBA}nMGCjvG{nb8 zh8b)?c@xp!cvMB-lCQNb=n7g^#Yc|>E*a#6^qmwkE^XO`=xbGCfyEz4irqK#1#N*) zc+fxEOJ>H+l${{dG|SvvMvs&ojh$-MEczge9qb0O(cwd70cO<*OGB^*4j*8jO0>CD zNkXyU1@k&6e=x3NjsDS`tF<%K1u=%AiRy4YC349JQi-m7*R;J7fGqf`KXUvEjaHUf;hus{C;o4u=; z_}#)YKR50x_{2Q625XDDoeX!aBmPOmtTw8Jm?cD*cT81xmCQqyxQ3oeP5V3K#|sZB{fut+3Dz@jWQ6t| zsppLO2QHACgx1%tr3Y={fh$xHpA}TEDomx^Jd_55R{T5IFU=A5noPT*IexonrIVs` zx>!Y8fZs%WM``8BLmtXMa)Mk;c1NuU???>zHvoq@t3Z$+KktDyV{9-!$he@*J@=XM zOFr&Lqvw#0Gu<5*l1;~xb2-gExlQtzqu%5et@~1Dv4f`qmtFT*Ik7VWP4rD#!Xcn&|B@onn?iSMXWlLqP3O&xENpJbXcjyL5FRlS@zsUl#sLfMs+FR`%Q(4gHYD63&n88V)L;Z)EiS^{{zfj2Ns?rf&UVCDYog zN~H^Md7f4lamL2e&zxN(MwrU%(lmIcK>}iFwJ4{PG2n))wR9P%cMtKPy(?i2vWr=2 zH6tSjDv?_#(qrVR$}#D16vsa_5iHu9QwtBnvrR4y&4uG#1iV#N{*j#aZC%QI$%A#R z?^f9{lE>(f=79yFNaZh?o8-#Ol%egB<^Tv;BkAvJ z&YOZ7A7;_quGR4=yr(g^V|DN2E2r1ZOu)b=SNzs1Cj%1VbhUNQmQ~Lt{hq+?a_`tcXwGt_3%6vPveHS<=1KP^W(IlRtcv0Z^T8 z^=PONY5X+MVES_E8(sRHdNblURy4n7s@(?5xg9rh?DU2ZpDKRp` zlPmhGiEujkt6(%8oiv~j{rRDsC*POct~8$_C5@s1{5ZdBpmEe;W?{8+p3Xi9@)y!5 z^TIO`d@5*6XDSVUD9IMgN7kgXU9Q0j_Zg)6thsbI7z*siF#@MrC7~4i&C@*Uw)VKo zVc-SKlM$a=S-m_j`ZTPapirMnPMD{mxi^O1?beh^N$ktj(YR0GmmH%8fKR&a_O}bm zC*tWms^J3B!gSGW3w6Fh8ihgtpeiDzBcA%lVNVg*eC*Gz!llD|<0p5-WYQ&{vw^k5 zlREjI*+|krP}$I8k+N%IDRa$#pdq5$3@ntxHQ@)REKmfhxfjt{U7_X+s|ZcHwG2fRGXlX|!85F~?gXP*zr-8fLaX0KlJ9TfJ zW&v4H*s6`^HTH{R!kyq8iGenlOvU$CI*isYB<_)JGakb=Yh-k{5`1q@E!I)pT5Hkm zZLahlo;Htb0J~8B>yvmwkX+hc6F!n-@iEb%Bunz{g-fxGugA6Bg>3xuc1@Bnkp$*4 zDl&>T^t+~-Od=M@)XP7FTr2FVJJ^=vi=(@bsrQLnp07t~o|q&|w1j9krLKi#%bY%E zY#~eHr^=XBReT2(a}Y6%)WXWbhb-f2<6>vU13{J`rLz33!QRA_$Ijsxn5j zKB`Q#ghQi?uk=dWjYDXsE(5s7{x%_ z8EgU0`u7ag)^U$%+C_>AG^GZ6y1ggzCHY_Xa{zsaP;p)|->&6mzP73%n zI?XY~CUvtO$eujRX_Umgr%2Eacd(KznV;BSH6{nL>|Rpg2g%evvJJJrb?@tS9Kd=9 z_vUk1Gv4`IwAo7P;$EV3GsoRaZadMDaWX-Mhyy1k(8t|oTAzn(!YiHB(R?jIrkmF;wu(-K5p@=x#d z1l}Ab30nP)M1nenX2Qw$Mx&y`>DM>iOJ?Oe}y;qbLrJqXtce zEP#NsYGdd+%M*_@28{?d6XZ-=tGLMYK*gfgdrLnwwtlKF_a=cc61hZr^P85C{6mP6 zK3Hj^s8>oAvZO-KSaroIEi79X99?z!ow^FwSaH}ZU`FluMrpv35f9Oflb*4W)Y0k| zcG|DiL&Yczr{M!SYe-vBm#(To_Z1_&i#%MFZ5~0$c*lV|c`Ip37cNLp3ox;w$3U};sF^l7vnGd9OG zv##S1xp8rDvoMxUHL0rqr4>)+H*S0PbO%wD@%xK2a7LPvwec7=?mG_7&B#%g0S_os6GEVP%{C8nSa@LR%qjPj3MWgI zjAv}YJnX@wWI;C)$qQT;+sJnI^#$;v^_RECJlDDka#b zJ=DM3Jt4*s!#+`{7Jc>dVGpl_sU!vSQm4g=5s-1>U0W77Add=&LONhIlBVU?iKz1W zjf|aS=50zy4d;7&{~TR9toY}PyBW)!Xe&#na~AhXBuUvvjVQ()t6-s4p*9+PSwJ`A zp_m`d=xopXpX$6Rj_sgH^|98|yi-k+cZf5r=}>i=&zfwn!BLO@RJE)y0OPKq{nK4@ zQ-Wsfb#1ASOPB;B>n-ua3PdoNv%C*b69I)D`bCS7-P(up4D&R2NgL58H2Dbarecq< zIrZDovjvpQj*?0fW3Nq-vxi>!KKxoSeLF|!T-Ej2Jp~0EDPF<}DQfaf3=f5xak+fO zZ3EYzHzo@RtRCgdjYNVbzu&;3HzmbE*nHMV_nMYGBY{;lwW8>A?M$&;b ztJP~e_21=;@VD%>NS<|!MxfN+E#Eh<&q4rfvt*oLy(quAMbzcy`>qS_W;I~L?9`=8 zu&2D65k*P3s$bcfxBF=5^z6<ih32R(|-V$cw-hLC+P22ReD_++K{IE)#Fy8)7jM#UzqBYu;ZMvC1k0g?r-y-`(x`*;{x!a=NUp*E!Nxx+Z^F9RUuj znBTHB(l^CFmS?OpCkaz6bfaH6v14Ny&PtStpjoj#{cBl}9Vp;VIgb?m^_ADhyn5a? zr!hX}eq|Y6AWhbM`dsx^+1$3Zd=6$1468x&R4-;}psLM0v5vEnmXq&+amzcCX=S>v z#@seJ{5XkN z($RjKilvqEA-CA`@Y+6Fb*o5U5j=0@Q-5PG*ZzR1Y0>ZQS~kEe9(N|W%4@i;OU`-{ zJksseWX(Fo`v?)I^7*$GE@SzP9K-E!<*hy`O=&1tXNPAVNg@A^T$$I@V^{r=HHsY5 zbSKX-jpW0L(C-bq8-PV-0X>?r@CeefXf% zOu}AaX}A*4H1NNz7e!j~pon?cvNFy4?s&z~JgYW0>YGSfA)&t^Kx%JAF_brwaeR;< z^m?-HtS-ekf`osKmj=ej2@9i$>7wO9L7vZN?;{@PlF{=LNv7)y%8-P_-asM>3zUIj zL*|!p`!Rn(+CK*`GJ_UsTz@(S#G-!)yJNnhiFBv4&tSGWSEy(P&tBTFC}{Yd8Jg-3 zIxza5fFno@2x|Q0ilUS&FPSrhY#pLeM2UM|JU-TtROLM^d-^>G4rc33x*k*RnjcFo zA5BrnHb0dmw+~#6yEl#HHA#w1ORUrqp++EJ&k?CRgNAkbT^B-lzSLoc0(;cwDnM%| zpFca8%m9WZH|bxeZ#<_>gKe7O^BC`nZ(4996gGfY!am}#S12aUsvSFaiJA<;Y0RRFs|x1@?rLF&9=bEa05HaPkZB($UmZoj>bjPbgdN>sd*)YVI>)*|C<`5KPMLFg(}XiY}Odek!2Ip@hgM0Q!0Xm+_KD18}} zgQIH{&cSAlNz9bt{HTZ0%j=If$+u-loFzh@Xsf5}^$<-DH@ft*`*9sZzn zI%D&n?ma<6>@TEa^D@<*=VQt{QgDlOqd!cLZpuYdxDGc*;lGHd%6}|^rm6n?o1i=| zJc!Qdv)Z~AVGUTk4%&i+AS}?OZ!-9HGi!bTJEyk-l$-l*~`q$W^WL5z?r zvHF=X*{1y9A3au;;{0I`wKNHiw!NN^hvY6Dgfd1KEryl$+F=p{?wfFo(t{HR;&3338Nw+Mt!qfK~1BmY#$DLBn z1&Ny9>^}&RC%0GIn5yT+UrZU8IO_8BY$M`es!NDi&sI#@zP@u@;3w#{A@aO37Md)! z2CH(D)U~%)9&X&&Op@q{t&sJasZq^SJ80Gs%Z7W^7@yTNrbsG6?d;<U1 zHdDfc-*g8J_@=L9_r?)TBH&CdC2AM{fgmCfQi}-2olcwc?%6awviweFx*Ihp=}Qu2 zD4MJA>jN<)wK+e@hjl7iU1na|V4IS&3&eHwF3rRFVL6eJ4U0&pKj;m!vcqc4`BsTr ziMqHp!%JUrH4n$ut+{9gRK2}O#Qyp-x>*T;cyuuV7O6rmKnummL}Wuy!v|=SKus95 zMco<|0VDRt_?+z~LV(HJ$xaGn7i71t7yvbBGJxtI5>A1%E zoCjApZW&kYPf!odyuDF%C3O=4rfyBHx>pM^URqaumBhCi%h1d|RSOX-5eUZK)@Ode zw`OgBNrZznTBC~sD7Wo?9imQIny@m!ia^W5M?K2FSh>fE5H^ohIFcck7FA0UsOB zqDo@K$0v=D#b8Cl^@?<+yKSXL{WwUf#@JT<@(g^*F(q~PIa?8^5ygH~`xZqv>E#G) zo@iE#&~w-ejQTL5;Y_sIn^cB>?ftD&cavoirqalixo$PeEE4J$+M=xvz=d@Cve4KQ zD69P`0}U&f1!gJoMyVO77D0pnZCY>r&4^mq5J)J~76v4{fNhYO(%mpn+cwf|E9OX7 z_=t)!^|BhO)GB;&AzjgLN=b=sfkeDl2PmFP;`-MerwiKI3<}phVU95z`2yH|v%w&p zS;25l6&_$K+#EOAhBl!~hdMO3l0X)N<<r{v5gsInCx;JV$# zW>bcbx*nak$~iqhg5=vbfWuh6e`45fe*yLWx~&miAW_~4=a^_BO2*e`{(6JJo>-3o zijki!Ud(PPsfeZoT05ecj#^u*0tKqB(crukq{*74-HqL@(tG%W)!v6^h_~5{-hLq$ zeHrRic2AX~`U*KOfJIwr7&Lja*MBC_`Y8J1Jd*Cbv7IiNQ|CZ%9N{S$SI%yh6hg;w zmbB}s>@1=1fa;wOfbUgSGCs4sT=MRUa`%T~zY@YAX?cBxf}u;S9j8P(-9vg=*vn|- zlWRpVW{#?p*#x1UHsdflA9P#VN$cvnB@7t_eNUw*Vya7%znysnmarwa43=hnv31VCCz=97FIFQyaUB z)+^!N>{Yn1pLT(#>YXQ8`Xr)660_Y9^r2nrCDAx*<^(cJcUH2u^F&md$i9rlmKXP6;6D_-$ZBqxEB|eSckYWGU`{TY&(nT)IyqyH59l*Mh zL|p9-1=8Xsu&nW5=o@(2RgLHqAe6bLm$&>TZJ2yrsj zb1R|cO*{vQYDFT)24i0F4cJH!O=|a3nJt$5HH>(*W)rhOfPbHAi0`ise2yy{M83Tb zg`r~qcn}jngBNtuKt91{WxccU#}V~=yxt1ILA?!DjI|a93_-ICV)fZVN`8ReX(jHH z#vm>YzBuwD` z0K8D3lFY(k02%DUn)%ty`Jl1PG!LtN{o9f2KY4n*)w>~!&~d}U{tsR4BKmQvL^SXt zP==e_9>1QhRhkSJ6wc?e8SCHJ=CGw0P$CG!-IF&J`_=*J?T^`%ydq>vw-VN)@-~DN zSw#zkx!PNV325$Qg}*p7Ne^}>krJDY?oSwm78RQL5URt@ zUnMzd|E{}{1}N6Of&?5H27bt6IieZbW2a?%1~Fn~Ye`lhkH>B*&x@x^UB4DHl`AZ+M7#Bxd(D*5MLf-(jWNR+urAvgAb8^jGHk-Gj29@^9%-QxP zEd?M)8V)wF6dEt0xKvljBlZ{;e)%8OZiu$y$V`0T2ht~7=0SpCiahkCBWUND=`Nra%^xw!a`WOzG}pK9-PBS z>l+4?DtrT2Fd6c-Pw=Z%_k2Ir@$;$%MFJB8nyH`TFBPZT~D7_YKiVS zaqT2ZXUZa@v4=J!vLkbuS*FO6L5N=>d|woF%I!o}NaTb)<#u)kQC!`~XlrUy0@nm0 zbW!8^i;Z>goH$VRM)vo+{m`G4ZF+@q*hIfzAy&9*GPmZoXOBV^M^;xl_G}M!I%fJ4 zz-A(MGdQPAgs4EOCEQV=_+xzvCZ-*ISpI7gZCB71Xvo71w{W08vFe+x__f$O~`Um+1{+Gw`e8xu!oG-pnNKnE#(Mn z$AW>`dZ4ZZ`vxu@^~x(Z(e8ZMZFyBaQ_5IIv z;sZv0L5$aB6Iq_ex|QBL?b9)wiXR`* z6MwCV)2%+?d}s9)9Or{cs7M*6^0oR-9Ht0j@81NR{Ok+sRE1%8KsRFXn=&*Iq|aWs?zOjWh0><1W+RTgMS?e=87=L`wT{>G#Jf1c=d0R z4Mh=BB^&TLy3XEnMSP1K+BAN`M!v&ldV(o^jiIO(!{DqEcpE@*n;TiQXkq*os z$qQN@2j>B%x~9>ft;)2ywt0TYv0ssiRD$vRA|irKOo799Z=Y^X?%q!B4nD50N35^w zQN7-;?$TFYVOaDA^X3z_@FgL>?CNzmtN6;R;PFa9o9MFk1wU!k)~C=li#F zm~=Y@xAABl$QZpAS51z+@|ihQkI_)C%f+pNV>8Jd3K+ne0kno9z^EnDe;g|(@}SHN@X zzWY{puswasaQd!=Q+f#9=7h63<@DDsp-4Km%r+O*58zw60|d%dy-3dp@+mAPL!k_~ ziH3C}E1<~KrD>ehVLaG!EZ9@D!ciW{*``cBIF#6euwD|iH0sbPrcCTTMd!KsZ6l)U z%6b{5`7!LdN?Wa==RCv+sr|M&hic|$BF+($7$sYPPs07wlcu%V$m+47SjdZ8qgwM@ zvJO$^K|4dUR%X{6zGE7*@dTAo&`~LtY2r12+!~r9gC}deJ}K2fe8~l_jJvYaFcah- zZ)>V#nk5GTes>qhU(FlC%c+Pm@_7LoLbaJ`jnbxKJE90xYEo*D}R=>0N!&wYb>J&;r{I&9+1 z>EClVu9L!kDy6&2p=1z`yGJK4)q|j;2s!gYS#ec?TVD!qTRK@MOUurbCX}{{UbkcE z2;)3YRnkSbcq~`)qa>`xuyZ1bYLWheqKcB}r(kt@uS+*#$84VYBCpGt6#18E`Jz=G zA;E>5R!!MP7Dj@LTrk}%`D_kb`80g&){`(VeV7!rp$=Dei#n_Oyx6s^U{*;7=2HLOKDG z$vQ|h`7|ssC9IFBe8}@(j;42Xa&UF8?kN{YvsxE;(W@oCJ07)j(0e81FKc4wI1 z;~%BG;PrrI=?rH4wl%~8TUUzZLwp(RPlSaLzM^9U9?{#;Whr%#u}6gOETC`V%7~y; zXrfb$4A0^v5%84`H^ByfHi|GvRGN(1Eo1ndOtSVnQ-M+uCsRVAgjQM~%?U_t9Z_d z%X)?orrv6|MNof^2kM;J-j*xXNxSXoB&r1mI$2l`TP{O<4P^=rgxzM+k+Rty*4Ap< zNKe$V-1B5g%@?%8OW||qu{~YaRfeOj$&|0qZj#7|%!(pY<%WkrL{qT@l^a&3gMo_=~fnNEc@=n&Bra$0kYGr4th+ zi~{$(ZbRmF{ok)PWP7*ZE?^|9n`J4kI;FK0D+HtfAwhRIyPdxxTl5k{pd>IDSP~+W zEVq6Q0J6KJ&1?V!vz<6i97w*{a6TNug>v#EbkJVbA`YoC6fim&WS1};L+~U@v;Hnq z#tmg(Mxm3^-_haNFdRwOBO4933bkV0FR^%jl7L}dZ%7H`uinw$DWrxLRA5DnZ7Rxj z67ieOG#hk+SFKSbJDOUHS(WCIYArvK+YD!W^>j8gegg`8d~Il*Gr(r)Lnfd(~{wpuN! zTAAT%kf=53{tId)1_xcs3`@+57>Wq+YS37Y?)Mx_*@qI;b@Vc_-k!L|qV+xGR&vgO zJN!4N4wO%y+~*FMjD7fe3Wv31GO2BL)>sPDcO7+p`BmMkaZ6qr^DkK7$IQjbz@NyN zN7;~xS25Egu>*Ax4Wr)E>2pI8k+TV$7dY3}x@&EnsA9@X4jQ!S-f2NRX;<6#kC~2; zo_JSnDrwH)P{I+ob0l-93(biNp7;1+hc9e9C9B0rKm&49S|v_vY_d=65)(5;l`F=| zY$S>A%N=Q$@%PsTy&l`uH@QOn?tOIVmfDvcX(GlMizVj;I*LB)0DC7GWXr7+3tHPb zwRXbqd9~1or5P^oC3IB5;vQzXjurBxNv$5e8_PDIFSQ9-ud9z^E;{D%rhJ9JKjPz-BYKKne zWXEE=Wfg6rXHo4#nJ`}3`Mbo9w6x>|5Q;z`5JX2KOFO)p)W{s2l|<=ZBpwls zyY{Y_%|u!@_q&eWP~MY|j=ATJ^yq?&Z5k7@BymsW$JGbJWF_qZ6pU#AcCSB!%tjl& z+MZJMK;4<$<{m792~tjFwBNr5+%2ng_f3z2bAi{@X&k^wR(EY zf~f6|1abw23HK749k1c(V)mM`p`Bk0WKmPAC399mnClYxt|Enek}c7E6{!V;nO3sQ zD0%3SBM(0Y7|xz*7-}f08NZ;_kevB40iiWgP{!ZNxT05W322Q!;oP~e#rM@hK+aIu zC=}3z!om?`!!f=yi^N8h?yq;G*m`*?|GUcXp(wPBrLsGa5u(_o3 z;N!!0r?4q;B?6{UvC@RFEcP3FLuv8h4!cj&<`+7?+ms>T8KZZ|J`84xk>B80un<&b zg^C0vqqk+Ejb&M z(q=7f@%XLSekBhkC(O+W7b@j=Now>;VVei{w||HezN-6@Wpo!x=*02e2sp9-+ucGU z%h@>$R@OuF2;T6IZ4Z>tsf-Tzk~DIMkWxTp3{Sd85tXmFJI%2N0~SS2lnGo*BF+X@ zbjllqWPq@lcN2=xSes6?7M1&TF%j;1Zsh)VZy*pXdX~XyiazT7YC8b1&!Wg>h8P*E z1dVA5_D}H6V%(91{7?BJbeQ+-Vo#k0Jk5%FkC7HCiu^IpNU$nmlW#RqJK(2JcbyTLKYA^s=4J{)S!pvubj%HI0{_V`*7nEGvD_y2FK4shXwY{P7@F$e$pJ z^QpaCy@0oE!odxn#c#3vIe$$Mhd1WQJx7^^wU@eQEmLrzKsH|!&TPPkuRN-FNb*4z zeP1KzTKa?TK$7u8g7x57BiuB=cY%mfA0tu*=Dui>JqVN*35nePd92BGBcaowcdXgH z(V7BR9GAJ+drLD771mwL#f7&E0Y3-&0<%mphKg3Vo?w>m;!>`&09yRA{7*9sJgT&a zW!B@|>yQ4qOiZ4^IzV$~_VN)On5|=7G{g6=$M(E>Xe#i42z%F}%^%nLBm(-b1vO;| z=reduRhuq~8h-BkZX}U3&M6k^cY4~gUW;$L1cmGgM$Q%$j*lKJ-ih<#gr8MmYmQr#@moiDlLgS(1wd;X;CFyaprqb6|~V}pR*04J;N*|?T zBABKoca4c<^h@n#=&~)p&0Xi2N6*|>4~_V-nmM`sT6t^GO8D**$Oovj7HTy z!_N$EIe%iCmxy|8|6*^HMunfrQy^217Y=m~DY%33X~}*AnPV1T{W_G$;R8+I+9p!an3Rq4add}=InD>=nBje5r4cvW z3S#p(*^t2TbPqY^@FRHI96tvoAp58ctQF-}CM;A#-DW<@z3!hn4o)LdhrULi1o4S1Zq#`SC>LyIOInc9>?(V*Y_`jcafRYS+B z8$LqmsvFya097sX(gjNx7;jCRf8*fIHt>0|=kaQdXvDg`hY0N&M~sCrW;5EPZXYLI zs1kXC`a)-1ch@9om8>?wdcqYt9xYhn@NQFoFKZwcE-h8A3>Dy$Uh5tkf0`9>EzNkY zb%5ZM?b?}VPQlM{dwp(gMtExo>M?`RbG0;(4W;DcUKb9f!U{agf4Vk({Sozut>#4J zZ)6$^LAygZ@$83YU_jYP zwCX_THA7W0msKhB<9?C59m_lNq|)nxly#U;d68F2;Eo|8&k5NXgbzE4s8&rX%QvMo z1kowVEb~D#*Z})9)S5XMu?1xUTYnxAZf_x~$>I=QX!lChc>Ln>`mj60KN;wG4tW0#sVSK4XrNT7132Tq+xwD!&mK{?yY^%kBcUZNH*o0 z$7ODGBSU_ZilVOS6HHB5U}yeQffsPNJC zP)Q2BzkE2+BVsy15ShoT={_+3XGCHY7#!%lk4aq2w=27U2XAN_VunysERmfH2i{pZ z1o7NbVcUAWY&J9(bJ2|`hwtm7DHv#Ba5PVk%evuYSD4MXW?ZAZ^^Y^U(8n{Kim(vM-i%mwFL?I_=78S{EP$rd<^W*jVI7=bnK`pRea?%TU@_U_>pvo$u@g7La zmnbPZ6#O3luNldOJ5wtxmr>7tjrWgV@q``s9Cu$vW>>58k9MwnMhu} zU^-0cy=q3F8Kc~$Kew}{$T3|L`W@Bes%CQ()zIAo>+27c_towjC+p+&<)wyR?0ett z+vOw2=q1*d_w{~^|C0af^5Ed7wA1QpD--z&@^zjw;%k!X3v+RBa4^$z z$}t!5Fsa0f{QCbWd&lTXz9(EbnM^z}Cz^?E+nLz5ZQHhO+jcUstrOce@0s8K-t~Td zKXms#)xB!(?$fpU*|n?cfoi#D9)NN_ouYYiK5FlrmJrIEX>F^|8*<7G0LX~d=RBDm zi*9VN=fnZYv)*f$#Q#LlbSoN6F#AN7mDAoIOFgu$+T}P+q?Yyaeviau)BUmR6^agP zCfK-wNX;S?0jJ#8&Y}vQ2RExP*r>YEkP}D20F=(^=U6miBKHTU)>*G<{-k?kAB+&| z>OkU*A&@IPmFBAT#f(Lx{Hu=OYu04$1Xai2tEJ!7@ev$9OK@oA`@p<~H!*pR$@AM3 ztD*HhK+j3s=Q=erAyb%ZuNVY$a0?6)?3M{UA|>5ajls@y7>!|yyJ$`Gi82AY3vj<6 z%l2LFI!;1lUlLVJ(*Dtf(l4F1<8Jt-z`h^yo~N%8=#hu&jm)rPB^dw~G=dcGHi{G7 z>sqT=f}kW8wQbd-$5x2}#^~UkjgLWS?c6{UOK=%QpBlC)1RAT+m@*m6WuTNZ7pr@E zyjb)YiP0aBpYqN$bj1hN*M%D2R?N5G1uxV)*q$G)t6?k&8((mDAb6d=Pwpgk?YxrWxi*T%dfWc6^#^{jn)Wq^C%6drF*k z*C?CU_L2SiaDag;Ez`QY*nq`%9d@%w^=wmO@spAG+lY}rA>#uxtwVw1fwS(nkILJ5 zuwUzh?>1*YylDnL+?BHZ;9~Im|;DSf+<~32Hj6m9Pz8_DhBUS43 zDH>Wo+NyIA(iI!?!w&R8lcuH%8zvfDW2(+Tq z+j-p_GLiQzL=>+k3!zDvtsRSBd_REgLb^#M_>E1d(5-tyVTA?yMv28;+o-1ZF1^+i zkwTt@ZMaE=0b4zqGDzt)u3#YCD?ilS$sVo&pi#zmf&5wawiYZS=BU2!2`o{Mf6CMH zF(gd*eSS@44fkORLK9_5BJzL@Wzr+v$_1UvFi2Ta@3||02JlzujjzCU%U$c}NzauL zPBCF?SP9QX&;Wl_MOOVB4ooW#o5PlrOZ+izsv&Un=I^gwI^!vTn)k1q4bAKxHy`%w z<*b9FU^x}gk|ARCaDOiON*7FD9hc#T<$Z=EVe8E;rpQBn&c50HBp)vj$&M;Jle;&P zV7vkU=S*oA5vZB82CUw&zMM4){|D~xJ=tNyamWP%^+-0$ztolY{HXDjYwfrr5q2ZX zj;Zul&nG&Y3WS-ul%uDLsDcX>`E2BHMr3 zaJxl8{Ni6+&GJRD0Tu0ud9Z>kim@+Nzcp{)0ebWM%6DF3&GL+?l(+=u+n{|OR@vIp zbJNjUTJHz^57=YUi1?m}J-I~!R!4e|%|w1JV{%4s&m~hwC$6Th;==F6Ay0PIo$xS`72>s0KtM&i@z=q) z3jQyLi&y;Og0PV;f+gW8efh99S zkhI+xV#c{etyCz!vf8}FR|AO}Y@AZMt(T2z+jxaL{8*n8MSA23SjT#w!)K7H(}i)Hg_Jm3 zHGg6jRx~4%X_0lD+dX99qG9v4+!+BRH5;~C@u=ewgfEum0ygYuHq`v$Av~T!vD3O9 zucX(?eDY5`0L*|Ps;`+WMtc=zHYgv)YCB!7w2|=dVEP*ZF7`1lH*veq8m7bAFbCD8 zfa$UGYzhl#JV))St@O)pP6vMt$1W)tn^%KnN7JiH|Iqfl??xn7L>&2A4OPUxY}*vE zgW><0SFJ(={hO&+vFg=xQtu%V11A*%7twG1pzjPr_w)_xjV7aROaG?_ zO`Kh5Kc)v|2-0@TmFg=&ZK1I1oSN)QxE5N~ajXxba&Epy{1AS)9G4i7ewdQ0uEicD zgp8r?XbyP;M%;1K7-rn?)SK^7l}vy97Lk(@rY=Grv9FSfDyqC|MUXFkasEguh-;|~3{ zeX4{uDm8};1isNt=-Tyl!nDl_kNME3nqAoXPs3};&um?$KpixoZDNKs1+v~*S+`MD zSm=;o=IU!JFb3=IK7^;L&Njh?`2Gc0$J`qcbalOAORmOyh#YcF3tQtDe46OKCoV0i zMRQPz^f2YDY83xS0!bq0Hw|YmdF`=sFxNL3l;Fu-G9wzNEK9Tudv+Thz2n|9c|#cGVI(h&B+TJHb2 zi~hNwGQoo8KxdURVaBv~!XixhNNkRv@2m&uR3~ue64sQO;_#3b9yiinBLS9W9tpMKv`8u&Yf54cs#^Q)`M(&eH5_>fL@mk z*>-PwMhaF~fiau2kMn&?q&mF$>-=jDPhW1T{a1_?mfm-dx*dclucfp@bM5%>a{VBw z>ReqB@%^KDv#Damuy=HAx@3B=KMn|sr~1b~gpw9hC^nWIj$9PW@2Di$B9OkGX%($= zbB!Zh6TsK8x6g#u=M8!P>hdiNMhYXaZiA>^b&7#b|4hoLm9QXp-rKDH2+i2%U&5^3 z`I2AfWc$t8orid<^Yu~T^K(rb@GW{0iq<7AN7#*O`r1)Wv33|CQ9RC!`Tmb5$KFqg zdpR816E3q~dk5nw|7;8P`74E1r~32j%KPo$>hoanO1ta*)}`m`_5h>jBPFHhV>e>! z09eTwTZ1w7V|5+rmfof|ztMU|hj8@!?=R=bY(!yVz6!8r7+6jApT0()n)B@5| zj%5r%@4TXbg{T`Ba{1V>krnG%OeS`kIZhrE`PnvVwU7$$%Uk%kjA6yPhpAY>m}ToM zah0w4z~P?q4o{Yc6xgHBAsXwkUrW^lg2XkZ@sr1tni3!r&C8)9s_x!i4<9{S%HEq~ zRx4dpf2%;<0mdp-=ca=soocP6g5PbFZ|)u8AX;fJQ>B(~;ityasj}M7##ZMRG&!uP3==M%sFK3vqtAeW~q{`3rNa7ax@!9#VA|;h1IEY zRM)q}elS*#m4Kd?Y|_j8?3E`(Gp*mOJDRpXk2G2EQ-w-icxuXY=+8Aid2I$Td9w+H zge8>H!u|)&13aKW#;3^o=UT`Hjp|X~Bivr1pNJvFI?$g@*-FZe%d-R{s?ik3TYX@tu z`ySb@>1L9Tt3{ih=cg^77XaGFRZGv;6D`EY72?I}nNP>ko9BgffwD@$Nhb?QJ-7C6GPf@WY_I($5VnPx<4M_YznV z??fjW1n9l!(Hy$+G*_W8na73c2bme%xQsB#gB$0VAo^;UO|HRdPGME<%ThIFC{^3o zl|b-=g}ZMvM7E~UbMa#K?22G+^A&6sIzZQCNAkF)BHSc$qU>H0)EeTK%nLT zo&oV(+`zosoDL&U^ZpT)cQWh#h#z(FDOCly7Um?HxMFNBgmGDsqwHd^!z}3}u`sON zC!#Xq;q^7i(2jlT6Pm9sPpMewecR9WiLoni9`4Gc8C-4fLqMO>5 zB_1tGw^v`~6d)y|-v=@X1`<7IJW<{g6|@LD0tXrMuNi0-52u2z8YcT@LeR-XDgAs$ zx8_)cn3uwZr2vaj{R$SaL;sXyspf?ZD`o9d4Hf-9^hBJ?mNEG)zhviWWJ#nZI$Y;T z4yQ`yB`Uwt)9BQF?$~x{^SxR%4NspxfzATf@1X$5X0CV=FqK04Ax0{Q-)Ri08Cg&u z*r{aeEqW^-|Ib@|OPHcg;`YDDNe9VpWzqgC4*ruTZMXM&!Rj}+aC{ecEw4$nbmW+c zs#Kl_l`?c(yFm{4{V{h@##|{SDPfNKQKmD(-R5JtkEqqv?FTR)IMMm5g|a96WdjX( z<^ot{{l7o;t`-!b>iTYUr$_CJf9z|RbF*oQ;WFasIyMRsGYNF6BN{)5C=3>J5jZ5v z;OjEOhzrtc40}LnOe@Ud25mK0%|6!!{qK;&6O^g-rXAhxdor4O3j(GBM*Ha6I6=NuVGK719Y z=9>hf5kpGYjhaee`RLi!lU6>E!D0%cWF;qwaViBWv^v3=t=387?Ta+9#Ak6PMOI#@ zE_st%z!>&CCP0S#**n#n%SjsN@QVY*XFgU>#&~)Tqp&G};tj%^$7HJE3TfEP zMzZ2OkO<{5-D{xKG50;Hs0!*g6%zTO`w*AXUL9LlN;#u#Wj-`x;rh8FrRoI4 z-<C$s~D$n1xt~#P&|$GtXH2?Fc8Z{M)gqk^lG=bzMrU+a`h*7Mw6>!-78bZ;PFSl8gDS~xuqK@?43noKkw{o5&P37W#PcChR>9L=;=5qsk9l(@o5cJ& zPZoV-BLbdeQn*zl!Q|CgBNoFnT(WUX5n_1?814r#6FkB>&pVV+_IV+T5g6oGiQQh> zC@D>_h?U;%WGw7b;(B@#L-H;6%AjrF6qjb z35!hizjp@Z`t#;S_y8?WUbJuWkQ*24m>AwPs@>n2eC0Wvi%=y4VN;0mk!`|ke}0+! zpM;ym5tXcr!)&yp{E*0k`FPp}eO)npb(bDJ6{v9ze5@Q$HiEX&*LT({Ahz8&*eJV; zrm?Z)POPV{f`p^24N5}9yJSn!QiTz-vA{nupeKSute86JX8?F+bY}a@;(_Z44TiyNhs`equJO1mWtCdq-Ikt6^-%%|^>0x~Bp(DxT zACcztn=-23A=CW%Wlv)v*!p&ln1~xqQ@#Ptx6iG}6X2rE-t|vk+|PfDGL1OFajob- z)Fqepw-kWM=g5O3A<56Xe$+ad5Rd@CA>$l|%1Hz=b7wzP)^Dh7XWDHWS{9+ON8|tx zzz}CweO6t%BH00U^EpPsj33LEib%s8J7S=6OI9yA(y5L_^pIkJGPZRutiT#KVK@(+ ztu8c~(vX3%?v-wW9!^(VkdS4yP-qB&gX&wcr}nikSJc3IaDf44MX{FPA|cgtNYCF| zTh8=>_#BGzH<>{3TNKeh8E+Vu@zx^2h61wfbFka2v}{eS#o|R6yp`nkdyHKu ztCL|SrT2kVVA{jumhWBB9~cvpm*|V%T%G+@t|ucxf7z^^z%$WRQJ{GHKDN|&eP^Rd z2zrj}Cgew8YZZDhScFT7>?YkPJg|9=-3SIx%zff0$4aLs_aciHFQHT1*@@47F9$$D zncQTPpRQ`cVJ8L_-Q!92E`oU`xj$ku=qaMH6DpKrvIncoxY{W9GzleI;S%@YJ)O-D zc-y(@T6b`4_u6Xww5C|-bqH8<*ln{2?B)h&o!M`9T3jgXbi#{1LwgaL!Vb=CptH?00c& zz_v=cSpbmP6wbS-uZHP4iJ}JB%4K;@g6NUg0BqJMmP@@YHF*s#1x6B1AS-cE%o|-O zJnP65xW0|uI-9rD^w+4da+q&4aWv2O1J?_}UO&>XG7`&P@1$DC7E5*vcq8Yt`<7F`%rmaNVj;DxV=%sC&3=YJHf*1`M9PW^V=@WUKx$tTt`oXn_}?`IR(%!F`+k)s^us zb3q_?hUiLUC12jsRYbNRGK>3zAWLEMbrE~Rq#S@Zzn)!4XW9ij*M`OkGhcQ2XY#&3 zt6R&G2C~xWfoeSpn8a8ibvejWy_D68&YPF60_jMM*B|vfy3pzVt!1a7+PjGsn5>{l zQ3y9!)Le1AI{DF(byfoz^{CV6i^YvuF z-K18LBQ8aj#dh|YmBi&LrIp2>#}BVU3tSc@iz74j5SiIEH}|D&gw+`cA5q{-iXXsh zcpVbh*!QbGp%2mr>gPK?_S!Z=nc07G%bREC&sb|SX|Ll;a-d@}ZLRUFv6xiC>{T+7 z26sN?HTM2a6bNF;C1f(I8@0;kXyy_*G6S#gldcyEkKz-Mp`wx~x^cOZeO#C5>alo! zIM5smT&EfaF|=}nvZ=v&VSTRxC9MFHRyO*AsI8p5Nb@yXyjb*2H3ImiGXFMK`rpCL zs?<_-K6osc64+>61IVt@<-!3uSO4HqV8bWDfoTJeEG>EUdLb-|EI1U{mnTV}^cCG+ z=pZy;?h>*d(#OCxKEY)Bi5G_^*R5pf;f1A%4h#N>F+WzKJ!afBjYg z#XV31mEtUmCD0j*A1Dy#m7ts!Rd6e6%vQZqShWp6K|Nd(L5O;slT|4iG;lj<%2PJ< z!ZsIncC#x*TU`lOk1PJ)6+M~iabV6%4gP*V>2%Ej4t>5qYa6`#j%k5HM#FS4XJrKs zppExVy$(7|1H1XJ0}89_kRPA1?JUoNxp1F|HE^G?ufTSDC)=|i7GN8J{(t9nqInJf zyI%4Q_w zB`@(4|NIn=j9$%Ejzg?98kmIdc&M%9YJHR1g8kUssUXB5ZY+~4(km^aB%=t%AVnocN?95~Tj`k{|-cIzI{!b(4M$1&pM)G`IX=aWD5cT+z?1p(ndB{?{SO#8plvHc6%fv209OUz+CpNO%=gGoM=q<03fp zxHCv4L0;YN#rw~k40vRq@o-{Ec^}_TW^6`{S=ycvdH zi|CC{n@B71Sz8ZcDN8H9Ca2+o^cA$Iugfz2^O;M+bs&k_@S4GNz+mJf=v=^y=MMqr z&jg1?-$oe%G;8=`LT-BUvF^uh`{~TqweeN&=zZna;5R?OU!o*I=fDVlFe~$k;h;`@Xc4@s2MkNs^uxwc#32bb_G-Ak}_(C zg(I@>ka9noj6&Axv9&$M*6M9QJwDiBd_rM(p?s{Y5I=!aqj$J|omGe(8^q85cK(~s zK~O$2@=e^Gw=`--J-kdQi*VZAPJ$Ka+m?pt3tP-PYJFPHm3QP7B}%z56Kc1 zt}}c5#BO3L9B1};%^ctzRI?K9Vld597oX+#@!Tq>7awRFFc0xOgYNU3j(v*@>jkbLOXY2%o*+ zMnB)(=H{}W8Bcv`vj&@KD{?*&exUx4OT~1EYU|iqQYafCXz7^i0RbL#g>dNki!7?4 z5U_Hq5U{hl_*hgz0YPNPQ97YPrnU&xUK9UrKF1>9dU*z7hqe%|f9szBxMS{q9JfPo z{@A4MVahZWIU9QB(^1}rdDQ9X)#Ezv)`m_>3q89dpM9CTjjPTT4hu3F*PUT^&y*hJ z6&j$VG8agonF=Xm-|&rF@Til9~l7Vg(RE*r44mwet9m*Hl%bWsXhD3QgO)p~hYh z-hmBc=W^-LaT?nEpvdH+OYMdi)NTbx5>B!-QX=-Et|+XEb*aG<8)WJ*HoaIi`E^IN zy3|+~m6m)SxIL;j9tdy_wclyBY7_L5YB4l5in7M4Oyn`OkNKtUD zXE%EL9=EBd``U zpl`>>iW6$W@+D~+l5VycPu^tu?bVDB>aBNftr@L^2S>8XDhICN5>&$i&|8xO*RzRM zS!EUC-$LEvw7n)X(B#p#v({{2v%s@$XT?b+S#8A`GTwA?UE9`tfy1-h+*h%@2~o4T zy;#u*Y_9y*gw5#NUc~Nn4^XkWJ_UXe{~h48ySlZztbY$TTM4$vfVx`(@cUw{0L)PJ z8p9>u1e-4~^wZCv(k%fjfA-yc$vVJYA#}%@x#>&mtpI+MfM(cOBY=BmWi^92#`VEz zBgeXx#zMt~?Fe3m1fZEzFv!B~u+kF`&4SZJ49>_xWlgsE(JiU!;Ty{PYZE>t1e*1e zW>0eLFXJCwDpqC1|w5K zHDPJ3lny+dmmTH^JC?Crk2D^~;)X~q{dW{hLd^&>zhG{aYhUFi-2rTJ^0%2fF7~>y zv2sL=g)bI=?UkwpB|Z(LcK@N3->Dz733#6iH0j`Q0mI8A{vI0I;fDB%TeHvelva&C zQ3qZn5tr;r_hPX4S0iya+d`K*+BGR%C(x&-+}I^IN3yCmi(>2Jr!uFw13iVW?vTzG zcw<#>VoS>&1gw_{9kw}hUYw|P*x@TxJ&%cKksTcdEiQwPg~L{=nI{X-sa{IGIQBR4;~MtTHK%4Of8~q+-!rs`~p55;Z4eFAuFc9k2wP zhyw1Ue2<6CAyS_2RG8M&fx~`BJmk|GM*e&F3D}TRfKBIKHxE+!%Nf@Cn*G7Qq%@br zW;bTY=uh#LY|lyRYyWr31H?`LFcaz~M9Eo@J?0d@c;b3^nzWFONJlMwExn$n(@9unZIIWLB(aQhS zI{>Z1&+7V=`TsK(!2fLKKXa*>uK0h(BC}8d934VDR#aWhP-M$w|N2vK06X}`xC%?Yz5x>`ko7HNjlZ!d7WxLWF8 z!ymxZ((nUvsZiv6xmbm79?aQrNe<1rfrn3;2TVHg>lFIG~TM@4!P69&Yh-1Hll^Sif!FlrM`jMv*(&-W^D-L8&0r{OeInqG% zc(EhH2qBIgg?imK%Ue7MS*5xB(es?x7%uR?aK>6ud?8C)P;4%ffi9r14)g}I0mdT+ zx7vKCD?*(lj71DDujiHfzYS=n^pQpzL<^C|6S+`elWEyJxI9o_z+_4Yz34W5wgZ}Sr8f= z%-)R5RYVpO8WX*;>P_{`c2kwx29sU9`gPL_* zL!D(fjB|b}jlWH$Wg@2=HW<6ucL?1Xr_Hg)P>E%c{;n_^9CBd|nH)3Q^yHyUJe)VM zFA^0}P>i1}%3swNlb+O3^I$2|%mHMahR9Ly-dE z|7!~shQGiI4w`UA@Pt*}S_9WcFPV~}86c!CCg!(Rwl~&H7eG9YnWmar>BG#A_L*1< zKAr+I7iz_Y=3Hn(S5nskRuY>3F%nRl37xH&S+L*<@c))UyK*xXXii9|I?u9%Rqh>F zj(280oM~JTvXtBHySik?G^x+2eLNZPM9)6&_;OL`h0u=ris^(O5J^#G)JgJn^vwHp zkyDVs`|H=&!@;r7`^8rDm&1>*n;KFdo|lr3;hwBa9N?*pC!H-VozJM8_q!T1dY@jO zkGtoZ^IIFgtjx^!7glPN3QH83MbUl2_xxp{1K~S7mp!5@U7JaXClYvFY2I5mdQwLk z7$z?bSsT*Wz%^Ko@Bz*^$bjj>9U3q*pX5TT>S5;HLo-S{04y+Xw1(t>OcR;@NW5-k zy*pY}23kd#&`;OY;TWVmyZ>X72bieQDxO?~HBU0%fxYBf{>&w)gNBnGr-a!{$4GNz zrZ2TR99p*`d4R$*2#P=+mpXGKEBsvTDR^bqM!Xd1Mf9P4mQtgXa4azAQk)4dDsq6z zPpc6AZL9~`SwHpC*j2 z1s_13U^#_Sh5$n6xxR0OhZ4t0*Gz|T5$=D{`avIF`%0psig2>zO0W5#e_!qR`jV0v z?#IQnF-w%t5dI1zZyE&F*$+KFpv~3wK~#0DXtI~T{y;>uTXr()As4*14WnTG7;o6^ z5X4SV9q+g}v*?eN1j(IMz!6)k#gCMH%~?98?>Qouh6ri)fG+@Ki2)1jk1+W!b2C-f zobCDm@QtT~P#bA4i&wa4e_!1m`I8vj4t1p4haNQC`n?_@h+2aue(QAXA zMVMEe88y7u5Rudt$;HoV-js@(O5dO4gm*F<5xH7O!1Vl!X|z5p@a68-!0p|#Y)w*~ zlF}mMF{v27#Po_j6l#nnAxXyPxO6y$#Zmf;U3{47${Mo=W^kJ%fo6sPzCYGZNUTsY zBx8LL9)&wC#c4u=EYI* zYE#23o7di-P^F}NCA89|ynSMGOV47hZi~B|!1?p+4-&w$r1LTDs$Y%aFYkEk^vUjb z!n{+VP@O``2ChKfeUR?hOYd0SJhGXuS*M++tEZKYX#)wLKilwu&+O`55S(p*EeEm&$ z!Gqs-aSbM|*P8!JZ>^@duSrcSRK7Bd^+FmMtK3fTuOxx!9c7v4gE)vm8Y}W|z(zQ` z;4Jcm=TPJRSZ?K%pap}vX3eMOGG}UE-meYT73aaK01#({okR9gS9N_!*6T-!@p z=#Q>+anldG041>ZELXm^1?-v*vvxRis%bY%8dk@sYRUXC%g5$Vy<|TjA*M9wGW8Z; zM`cBox5xG91sW<)3vEjo93rSb^F#gSO<|L1@i|U)-6nt5Sl_AF zsI>=gsfu}M*cmN!byliV zig=SXeLOrKz7T#TVSZarqiuJ|wbS)3Aen{Fu>E_ePj4xxb=C$4N;O`F1ox*nfV)7{ zE06o3o%Al*;}8oCO7XGtNqh++JZH(}c*l%vKIl-5v?$nUX&tA@0M?tslklV&Hm;t`}Q5up?pUXy=+;?_b=7u`Xva!l}w#(d}=tzI4& zT6VAee$)DW9V;T+lKDgrfE^SVq#xwU=Nruvs`zUvYyaK@f0V?uxe#0SBnl}~0F8$& zJ;?HLQAeuxHdy0c%5ECjc;YW?mED>yN98ZRBtlMNC<&vu4DM|ya;7?6%vzJ;r;SDI z+jyL_mn@L4Y}stxB#87HZ;TvY7XhY}=uUo_gHTh{nS}mKl+AO}B22Q?&dQVR^&ucrSZaC+=VB$HhOhn9PDU_#mc~pGcf>1SI49Fe z1Gogo3f|_)(ZjPdY#x|GD=Z-wJ${kY*%hDp&W&1r!tjnru3$)e%q%Wfg3XDkT@PdM z*R2MhoEP4fJkw^9#a4dJ(rHUC^CM0-ajz(m8futZv*0hH+zQ>Ayw9Q2zm0?JH0WH$ zb?9n-GFZceFJEpyaHC%+QdmDAT^Y4<4wH^1kaE_7PJ~w!=r9yBqoxPr$_62x-IIrW zjf57@7Yv7~Q@K45N{MmBcHzuM8}3*2LA?L;9Fgdd^4#qJICsGPbCE9=7#a6%m;e@Y0rP8`0HU-a#8eX7l#jW_xt6QU-A2l`>;ys`d z|9!2A^#nZmQFGv{-BPQJY>&39u4M~=cv06DvtWdM#jUHH!;KLQ;Niqo@`15)v4JCt; z?aqT}EFS}a-R=8M_yk@HiDR4TpvTz>M$2UXUmh2QO- zG|$2aBT@oq)$Vgjy#_Ml;4S;ix18sl7Cc&)$92Mw;)htgpc1_-Qs~1b9l@EbBXRIi zGZ75YcM!+W)phl`nAtIZmy=?8)e6?HHZ=BY3a1yi z5_jAWYfHtlIUI?v9WVuT^Z}cvC~EckaljaEV5(f0jrfHti+UO4M0v5!qts!O(j}b{6gB%4w)i9yB5Q8R{ioLR!;>>) zr{v=nj*pM`-CoGuQwk~O_&t4rnFE~NF51GFee-sH?C2bMb;gqPTH#^*Q_GD+I!&12 zi~paD7BKpKQ!(Wt=~zbccwr6lcJXMZu$N49H7}^OM{P*MZ~m$9v7_e3Z1u8Z=C z8`n}Gw3)p5o}oQ5B7zZ%k`$aHnqVw{uaWF>#p5rQ)8GvDZw!+fg<|dFH0K3`B**mv zj<_eg^~SN6)VCX6>wGu#VI1VZrLkqGFvUi-cLActfIHo{Gm7Dyt zKaS)E*4CsBIAAwI6-J7gB1u$P%omC47~uopD+yO-_t&C#nBr4LigqZu6MubAxQr@< zx;w>-@>SUe6(d0;s0hkcd(8^H=)9Rcs?PV99nI9xa}G zwB~<@hcym*MOL{x@)xKor`BMW90;)e1Loi zxLZg{fxvl)-`EXnBDWy$mNJYpB2cLpf}Fc|R{tQ~4$F$qS&xAlxm_;c^>zS~tZ}>o zWIngJGD)udmc;zan9=CpK=%IfMwL3_3FnI@y#xTO5$>hMo}R9KFrwU?_Bg|k@B^!W z`+qv}&>wPloHC@>u?Ky5R=Dh?mlv<3)!QkOqDk;q8?q-#aa1WXxz@R&1;Va`sYsqP zar(4-k9xDlXLl$?S>oIu6rt_CzmECSa{q+yC$e3J?`N=73Tg2$!L80*D(|~sHm*%Z zU=5WMjsSJhG&@EqWszH@2)!ii%StC{b`eZ697<@3|fv`@Xa# zGRZ_?Pkm1$F%AWw_Rv}h)+ltwCU2$?NNG^Xoj49fjpz?AOl?qZCA56({&PX6Wm=RE zDqCTh#U?y~p&6|A0GanKc4`Eqkdgyrm1^5|uN^sPuAOrz*x9et@E0RK|MchKwsAxUkm|Ittq|muxz4Hv-Xr$_B;*6U0dCf@$Yp;BH9bWQNFR4KAs!*G`m~%@wq$330 z8Sn=<~knT66xvaOBZBw*q_2 zK6QkEuC_Tsb{swb<3My@ZK}m3`!K#NN7gL3#Mjo>+t;kbB$Jq+*hvFH1Sou2Bv*Z! z+{~mu$^MFQ_xsy;O8R0LQ+3SM2Fn;i&(0slhhM}IEb~?wa84wJuW=-qW4|%8)*e10 zVoz~8>PExjC(I!FM4u$NPY3sZt{gt3+RmLyu9T9j{I;8%K0#I1(Xq~FL8dGh=><`_ z?aybm5pGSN>0Q`Z&RAPeP3(ESQCVJ$si6?$S}gf~)BJm3cDtABp;3{&jlNgyZw^NJsy}TLOfg5(>W(dnC>4B^VpnVn_1vxuV zo23yhbD_>tqmbHSIM8tW1H2ZB7fG+`N2?ThwgS|w^=}(dSxG70N#ld&isDb7Anq0x}zb-9rT(>nferxEUiQiWz z;wq=lQXy}7g-{$rv77gr<6#@4($FGTq4ZK>!t)A5I+y@Y@b~wXN!P%t_g>RP)+X+) z6a|U=(cNFjb7(pCaI`cQ6i?qe$vU1BWcXsLew~`7SJNW`qiT+X>OC8f?iM4&kza68 zaD;75A&T;e&ej(NtHQErE~ZKme)6O6)YJMJ^}ARqfrK2Q0#fmb_^obn?1rjo)x*V# z4Vic7+LEx*S#>q8h!SUL`qCd^510q4ZFD$Um-Y&d$>owo#z+zrLy8cbKxXhDuRzhH z;)U#24Mf2wmY7X&fikDK3Ys?;I;zBAyMWX-OJ_(XN?Y~!mNcA_R~?5qd*WM#enqBD z>ct%@)?u{98M)i9x~HcttG%Uey3dc7Hx=C?6KX(JAyG*UtBgtqS2N?o1<_3ucbZdn z_NGE;AkhL*FFqgV>B-AhPEZ#pVxb{&BKh!1DDT8TeCTpi)W_@Udo**!q}z$=6UW@D=z>pr`L0GNdy!9LFq5d z+L=qSI$3ULPTU~ZJG-@7ZXJd?hJ)cftjXIH2cm`)PI(x@WrVmQ>SpY0kp~+1GKE8| zL=0_U=3;1^+WRn`_A6l8%N-Z|{dwHyN^RnjY`{oufMM8%S33)QA`WI4yR9|h=S0SC z%!JmLsZY(2gNP)bCfTDe_oq_Ov? z9&8rfOfA#Zk$$scQ2fF?G)2GFK#wr{D9OmEut~EP<0^*J30$>g&}ZO%IKVZc+nO{svJl+-y zfl}^aZ$1#5DQww3XM&B$oRufp$68jfP*;N)%otNqgvf(~C>fj zUU1y6f_%c^kcAZ83+ZQtt;wTSjU=uP&Otp_%-8FgPA4b^s@@P3;eUQ?%KRWG2!;1mcl-c0=pnf+X$5J@A z#Z*h5r7EY=D7aMfI4Q5c(`P8h?X;|sHqlvM@}c%X?fxvKEY~b~^sk%^D-eZ(Q;7$X z?<%lpVu@P{9OR;*R8>oMz^ajWW><}d8?S*I%iCKdnpqZG)Gtr?I}u(@7pnFLmI?=c zXzFeo#?w=jfBU0v9p6-$&!?BA)DSGO*K7^}q3S#*g94W2IYX(u=>QIX#NlPTl2Or= z_w8I+2k3|Z0j2gE8F0)8vZenRa z+Qm;^AS%@TYNRe!=v%M|ajOVOlTwiw#g#B=_f1q2u>yn1njR|qH9VOf%%T`vk^ip` z<1>-x@?wc{PMJycpWl|7JObKoecidi$%ZM3j7?RQU!fC`g<^jUaAh+J8=`4xJ~vs} zrba#{ol$YONZ0vIzNfN{uy`^Cr~ZZE!=*LqAyqt!pQbF8B$N+Dh}CN|$EbUqY3_Bi z1bk$vn81cUEE&HRDX62C7}1O)RZusjJ3j4{eYjeH7uOy$ZU*8gKgb>)?=bv1{N8+q zpqDswM`oL-VAYm}YbH1{L<6Zz#Xt&eTa8z$%@pi@SC|y`o`+e(B=-c1ut-87&ay#N z>U|}qiJMQjSS_@$W#91}t3yE&`D6TcGk42G!Nrtb+wV%D(Rk)Z?i_oxbiOem+j5tg=O8TCnj&DiBN{WcW+Ea_b3;w( zj5&Pb7SFNBhIaZx4D`pah3ahik!_7rP>pymV^mgG>B@#yKJqgWXgx=Q$@bNJSVTnu^Hb9u$Kkp8CcphE!*?r z3&J%t(O>it(b*O>Q)~kHsfKmhc|e&!D~edhX^$WJYdThaoUt1^!xupti!**dDrXEW zbR!lx`>Hnhr^GiEgQDvrmzi~?+!MFLGO!aozQpWi?O@RMr+fKNqTvv{kY@)&qUiAja>&md%ylY&32|4ras35Ep$A z={t`!(3NtKvGt=w8#MKkF%3MExePQm-dLMwJr}SpK99Lnofq)NT+DsloD1BI&tWbR zCOunHenhtkny0W({lP^tj*Dc>iu?O>lmND)nLQIJ7fXm`pOBJe7&BlA{1%lPEUfIh zbOclTgc48{2KrUD`BLyV??2qQ-K2Y^Tu{>%b0W- z=YeVaUj}y4cE)VB619TQ#v>CtPr#-&ikN=tk&wV2qeAH%t1p#GJB`sM{39xCfM3ul z^g7s%ja%=?2&X)!U-V3n}e-ebO~rQQ>euDgz-Q8hMJ$C|`lbPu=DKvsF^Xl`?% zT~lxu1$F5IR5HbExV@imw=+qWE_u=UI?x}MvwV&wZWSK6mVw6T-$S$|Aw%>Ox6`Es zO)(1sxJ?^F=&~?%4h{g?ln!FsCL{$^VemxsmGJ7`f-fRI^rl+IDdVwe=P}u5J7u0d zw6tR|lXL5STFLt@zFt*VbTd3s}3W95=r-^sNud7`)bPVDa@ zTqnn;S+Sxg~5XVNSzrQ>%T(gfI zz)k_V(yXNUp3u}q!`!p^O3K@wMvu3EDB9)}fq(W&%h9}PbBFp1lQvX;EBWBLsy=b@XHv| z=`V%hefk5>Mlbb%f8Vxi7`?mh#`E`+me1cbFrwd|Z1??d$q9L!_n$mfhzA+XFB6{% zgnH$}v=v8FSKAsSQ?CdBD_$T{Zu5r^G{^-Y9T~C67vq|z0Uy&>ww;SK<_=E_copbdT)Em|2k650lhbT4(PoH^xgw{ z?*YB{fZp5u%>ljlS?er4w@%5Ftjgpe0WEIVKDtK2lmq*jcPwCxLe90CL@lGs!UbY~ znNvZRZ0d(Qt)uT=KBP0Z(rGYm2-GASaoymETU+TDV3UmUF2i^{-Jd~AkRnq1;2=uC z&w3JOvH?SIU*XG>PF_eg+;PAnfMIhL3T`p%PY{|Ft8NC^9Y}J%7BuIHBbn38rBt_= zW8Y|S1ZG%yUTHed*o<6il2Cz2enC=oOdS2Ne3Xyjs*{o}SjmK;e<^Hqql*|cd(`-K z=l#C743!9PMJ-d;z`{=OZ%nI@>1+$_5!sdYijNIr^YQHAKB81|X}Z645N4i<&^yXA zv&p?(_#mNZl_XkyvYc$}g|}|=hUYz5NyP+00(cJUU453^Qe-~;PMk{{?KmOqj+JY@ z0^>(1(YOSSnBH@m1}i;5AOa&H_I3a#4#%bpf0b5FbzaGFc3_m8qdjI6<+PaIvUSJV z?HS!e{+*mY1igzF>cbt@iPTq7tfkjB5pS7@wo4RLYgURk@BZ{n#mbIeJmE{Hp za{VgF)m~E@1Z!2PE`oK8yT`QQd*F)5oNH|pqG>=V78l9^!M7NIm>gd?t za$waIb=o={hN%;{K~~q3cYKQTaoyb1b5R=A-Br!a!A-38IayT4jKw`dw z{jS%g3x6we72Pt~Zl=)9_C@Pq04iCrw92|NrkY9X;qQY92h;OSJ}ejX3`)z4A(6yr z@Hh3ZA$O@66=`jV^>4N>eW$7v%TI2swGhO_dRU-jYE8|$fCU=A0q4}3n7!0^&_PG( zFrR>-x8y~}$nS62dV+aAm^=6fQhAUJXa`_k3e%A+$?x62{_qgx#b{~RQ?x?aqb6J+ z2s{q}o|#<@ix00QPGv?eAg?KBR4A*-(JX8HxF^!@2bVf+NyFtz2KXiKr6cYMxdMHp z{sgQ7@8t!ip>%Z>b>ZH!4z!nHtN3TOc1jj5$1PjeP}w!z2PHnbm(vLQ-D`P?PP1F= z*(zb4)SMCg;xkuT{Xfnklh2*!0=sp43q8AC5GnlGH-xA)b^{t~p+C3@maHh5g5+^( zhI*td?4A@*+rj!@@ow#G(jmiP!$^g=Y_Jp;KUOrGHLSh(QTMUJ2F%X@*z&EKSd<0hprz(XmC=%zlf0B=XEsk<+NP)6UMu{t zeY|Nm9B!yyd7qw9_W|_1d5n(HVsELuvnRQ5jLdV6Nhp~$Eq+TOOO~Dlw}U^^GxE*a z35Z=%A0c%zIFx^}W3>m)?q1C*7P4e_tR%;&gm>5-PxQ%*{J*S}y?MUg!DSDxFRfE2 ze!U{c&}8V`I&p3_n@ZQw$GCW2y-~bFi0G5^vDAx+ZkHCd*c;t2-58eYnJ8EG95*tj zJvf2mxt^B(4r%qIWJ@@Oc(xvS1|vo7hYU(s&*hy_pkk!BE1B9?@NNLbi;FFgg+U(X zBSpDS^$;7;{LnebPlmgFawVPgssm;*!ZsTtVnNqeJ(0~rC>6qUMqY4YaY0wBJDI| zHS1{gwp&jVTP4YK;BX{2K<}L)t{zrrN9`eBh;6F_F2${5@ib@-!&3MW3h$NXan&+LiObfaffeSUa}P>KI4s zZJIF=%hFQ4EBOqgeppMR5RZl_6F40z)*%eN=pJ#LLQL<~MMjn8$v2ryZm+fMu2(*U zL2PTgu`Ys~{%~zL|7{($Ezpr%JolNA@tX^6rX^6lGV_YCy9DlJR8 zWG^i({*2x$hO?fVS(~rB#IM-!y@k5zEy^-HjtpCMmbTjTgB4pEb(3*t*0S=j7;w`1 zBgt-f@rGOkg*~@*wN1=~?L?a$;3f51H1WCkO!FdR>J53pKI>n-n2;Br7s~uk=mpdp z&b_yviv=r7B#t?=FAT(ed}@jzL*fs?chG0$Av;ixZ`ct}@PQbEY%UirpreVu!vh26 z+*MGIgvh8#+t1nY__fns+3Xy|Aub#Qx(yR*YPp`#elmDz3B_(oZcWg=h;@6DoZq%Y*?1ABt~A*ehsTqJXHO&rM{nIN?}IjOGY=>f z=@X%Ykh1F)HT<3ARi=3nk>~@YXlPfNyU-D(QL=$ql5>V8+w=`lTr7vQxV^!55QLjr zW=}9Cw-4Z*1ZV6R?cTz@>T+#(#x;mWC$m`K3WBN2X30ilmhtkTJ&E9!*_SZ6;X5P8 z=DU~Rmphr&o*PIdtL4oouKe+l@@RCCg($JbT{yiz`u^kPD5A~Vo?5}1qK$cDt7rpX zSnSx&MYHJvPbJ;#UD-+h73dS{Y?n+AmXYe$oJ8SsMe(a{gQ$C~1yosLy0$F$)UsSG zgCJ7!XVwjjIDpFm(;Plu_m-P3-U0A2={dUth8M>#>c#K=nH%$Cc}BvD)gnHq)cpMI zb<=2m#)=hrd%ej8I@is@>eCX7jzfAv#urTU&Pm1ch+GP;WgiCba30~>HMH{4?I?K{ zd`d|ovy36wmR#WSxYu^k(wQ}e&2E1Odb-ufU*gui{CIJC{_bLyr$1ZC`>B@GO0g;L zJD*XIzk@(nV}VW|0d*P!cG?z7!Rg5rse#45SIpz(S|?Qh)gYywkdoRe%NF!!QDqtK zuPzyzgr?7zUqE!*@wRI$&a)QHp6+-Iy=42OF0#W0)h#!-=(_Pan!gd#_m-_m3HkrB zggRYKl4Lr8Re4z{R?e{bXSU)ompqL7BkO_L+jYG^vc{gMZ-pkobO3)Z@xmxL*&)Tn`O)b~7RjIAkLh*jE~axHY`TClH*HR){i6liPb* zrjWE%P|d9>-&@fLpP&e#OH`PVOQAGHEEyYz1x6$Uq7z}&GGwo`*Tn8R%Z<{Nu+m7} z*_zy!vODjgeg?V)E948!R;%j}2{UMz#!&UTN=JzE(eGQyjsxNb8LoK|lHuVJgqd;QkDP zPsZ4A@~m`;XKguqf<1`W81^l|*7uFA!Ko#oPCiAp>P&QIDGOTCoM~2Cn9&+@JH}h_ zpa`VZY>-t(zOp}6@Iadm> ziliWk%{KyJ&j@z5xdDQrATrU^La2~A)Ax)C^6J&AS0Jmu`t5IjO8{;xWeLwWe+a+& z_3KWoXng+T^xbz5?q6tyrQxw>(?FwfH6j1|`osGgMYs7a7>WGy^!FQDW?%j0SHFsQ z!`6U?G|xKir*LU2LTWg_KKqHo9bQQ>;&4HuEq!A`7)@5> zmaV52uAg$BYXUKeD0MWYmL$T}apu{T%a$xk`FA7|4@BYgVP#b`a=((w;@hJ3*W1r6 z!z$QD2xY=UH;!w)P+^y?tyMc?}tE&VfRTs>DX?m|_~y1MqnmFm+f^ zbsv~kXY(xWZ{05Wh3>|(Ay)fwxp+)hc55)FwGtUq%4Tlnw7g}h=f5*Zi&RlCJp^vp z48d%`zCqUwObauJquJ~T8Hg+~{T5HV`MmGMMYBDcHtKs;R1_*c|@Nd%gpfz(YcaYrLlvdO@#LW43HtFZqy~vZY;P%;C4nvAK%g- zb{JbH*r<`WygR7!K>scwK&uW#5aU@p(eC zEJDj_iWTk-LiNN!yCA_q#W}Y+Jg9qNL2Dzuv#o_yB$o)cA)pW!{s?f{-3tLO5+UNBWJ?iGB&MZV_gDLeg6g!>%1>MEzd0w+k z)MnZl`hkKKBZx_R)@wIH5cuhEl|gtO*+B|zMyRt}*5Ig4uDxcNkv~Z@q#@R=BGWfm z!WL@!VR@nn$>^MAYGRWvK%^~bL>MwY5Hi41IM4oS??pDU0~)k~&|ASju~J1~1J+Ly z=@j2~nw3gcET{0#w`M^*P-M3~ToEIEYK8!Utc3E=lSb(Gi@O*MKKBfCX%k0TeeluE zr&(sD;aD?N1(#%omDmQXMl+-Ycn7Zb9;YAg#v&j9E(Y!L0A&oH-BmJt_8p?IYiPp6 zI}nzWu#HlWCWE_9k7ZVOHs)(w3nUxfm!WI(&P7 zP+Btc>ln8RKF_jFee>D5YJKx@i?!?ds_(eEa)Fcd_joLZis)`|&rw1j5;gY~#=}L8 z2E2FYd^D&5*IqMIOtZX@B`wz^wN}J+Cr;X4>tnzWQ{KgJqruo%%By^IC^l#{T)}&& zT45_Ux)fu?f?>kIAQ512w!NE}uYW8-`L-~K3zEqs z2&oGNJ0!ph#{*)W!G;V?uP7OV`8rb=j>HqFp#_RO&`C7ynMQN^ndenbDrH6rwdsR4 z>Ve+7GGG9m#|6C9Fkxy^Wtt^7o0lU)$5~O8+UJ51*aMLN?qioKg-tr-`UNL zH-^~@J6R_dv5z;3Tw#|;Zji<|HUDu&Gv}q9vjql<;X7bxP~C}xXa>xfhCD>(%aCF8 zS}yN;i>Q9@%JcIc#!OEdpaKdl0)nv=b0D*N<cB8L35t?Dn+HdFGTK zJ2{FQHFtAE~XU9bPe#-HZZyo;^YdbVDo*JtK40fmS}n=d?0p74V+rE<)lveVu;N7pNq0AnwDpupUtI6($o!Jay1;yy zjLqC%zA1N)X}QF#AeS-NAF&|Q8BQhiQG{*(?e}Jq=G#9^e;<^o`@`SNwHXnm`?uCx zVG0S{Pv)<`UCH#iV95-ZP5mv(szC@7x?Io5m4w(qp5lpvv6t-c&gBK$xkk4d^;%9msjn!S zDT)-DwlV3EhjSxE#H@o9NiEaCW~&)_529MuPt+Lnh87u{l`L4ol%nMt1NFIf=@*q^ ziz)*Nwtq3Gz(UL=OQcY`tP&f z$)CM4)V~PQ?+;xV^@3~IsJRiBoaeNEpdWpLgLgxk<>c9zIU+gJG^Lux<%tY;G`^$0 zWU`ITf`txZwyYH5)FN5%5Y^@C(^*tMpa;R)0)C`6G;3Y^u-BMlxIQ_@UZ*eX|}C!Gn+OBSH{M0I8Nu@ZQmnmdYHzGPa} z!OD1@Mz*f4=!GK$0nhESwH|GqP*mGUCra9D(+CPNn>^IbjCIA|$u!g^lCM*|jFMA=gU9C2!x1ZGma!6+&Ki4K0i{r25M9IlC}Hk# z=0mR$3U=>>TdEMU+=cLf`O`%PZ|gVyNdMrBjHhCai*_Cm37YiC(hSLHi-OKGBeevC zo$4|G``bO1DjH2S11MX)hFEl4M;hDr z@bD%GJcp3T_&0WECj_l*(4SMFm+*&^J$(GeoS8#fBO1mpKfx^s``Hr0TAzYHLj^-#ORfVCAk^@^A2w4kM4qeW}d7@1En9};G?OT<%5q1W(fY%wg6 zbAVdU`=DSJ_q2C)-?NU*dUsD_)?u|kXIAG$DLpE^;ZR;4qFJM(vWbQvA%u)}wI+ zxKS{gpo^Nxkz)~+Xoe3kNNn>2(;Xi0#?0eEFG($3gjSh=aXaybi`1MbxHiFFqpBof z%v(n-kBS%k8yA9Is+{U%#S~EzAk>wnS;kWDp2LllAn{q(U37nez!<+Yfvw6KhKr0^ z``n@u35Eo7y$(y=H-vXEwkR+|#*3K?^u`2+6Hh#FldkdZG@F`Uy(0QvdOKO|leBOtlV+y_E_U0xVa;ZL1AaO&XY`(B znfZSgSB*~M+B!$R6o{R{m9vD}M6u(vltp3vA^FMfbA-T~@{TEp>2gVGW~-J=u##$U z$R= z-Y*^UlQYx!+{S&ISL$rzAc{LoOQ?VFSWo5Uf_!9&ykjMj$i?S^3ZtUGr93kP;s~Yo zYCuKbW-LMA67#3tZgk#@&>TJnACqS7p2W0sK&y8;KEvj$k{%bW_jWK%zcX`OrZ za_|i}$ktChxWnPlZGR4jZpbh`T54(^J1Z-69lKrBP%AUoKDipbar_^>I7mh3HuD*6CBu2 zg7*x51;HfZ5l|Y0>|VBDlyuHA@?*uyHIXru3*6)LlAvt2;wk3gIDgnatz_T~yZCH` zaKKR8S+j08RO53FqxXefM!&S7n2n*JI|<&zMtz>XKOe>p2F4naM>0D?PCHfrSJi(5 ze8xw6dP6+9W$TFz!6t?#sQdB2poUv{bX@ zfT)i8$yvDsH1H(4?qdDyaFO8i2=d$|kiC9J2x)iEP^&0DafKY4Z#b$O^x95Z?yieF z(z1fY^&O;JJ*}R-BRxKbUfWmsX}60?beA}iD^X@#Az+8f4Hd1LMmI_u1(~FvYBjZnadHyY-vyV=Dc{I z>C+a39y-JVm@gr>Pa=9eF5nZDH;?3mL&4PbBaEvFTz^K@(P31Umd$ZOK0RWOMI(z7 zq?1b25j&2nZx0RxV6(Zbge55li8juHm>D|o0pGnP$ROE`FU&kGWWbPlHiDoah@iAJ z_}1G`EJ-E9oq*mZxc=Lh#!e!hX=8jVC07hjhSF5C307`hWI=f!Msk_*g^jzr4t%E* zM31oG49E0I&mr*d$&lM zX2G|7UNUmFqC&7N0PyZJLmT~0YnrU^exStgxQv;5c_o!5IZakvc)U^JpxHYct-)`` z00`S_@oh`^Vb$tJP43=yJRtE#8+9d1%XDBtQN4s~5PsX=!^2g<$z|?co?HaCJhLwt z2_lerMk~afRlNnan%MGLCCpOS^@ZM^5lzb_)21yVs(jAMK9KqSl*HS6vD?PHd#cou zkM1qV;Q>)=A!EcGj16`5b+JgIxrRR3JF*h}!cTk=T->`Ket18unBYuoaFVOlY*Z_D&ZzYfsGL6P_}m4bvzU zIfgg-0X;4_;tfLkP>U#^b5Y}7)_ZSm6LapJH$gFtSY`m!Tsg2_r{y}55+ z<@Uyj1R48`M|dny=eotF1OeG(HF9o4gy_p4t5b2)mtLIku!8I}ODvj7kyT6H3Go$O ze;DjLgU&{c>Dsm#+HbHtLBHtWVCmz~L8GbeAm}_O=!B^BLH8*um`I@(;oF+5aeYSH z7F#>-dDVG8auxj=*JxlYSjj9RTbzB?B{hsXH!Rt=xgb=md#kiYt6WbjVx|8YB9>xF z$#g^#_TXw9uHojgH9p<|GR1FQu2WqUIifeAxaM~a9BTmaph|g75v~P$a8(0$qo0Jq zHp~}9$kr&sj4mH1(fCPk>AW==uH0+?0*3fWor{BTHE zS8T?jJt}X=iNGzG8(n1Q)M&3$Jvfi2~s+r&XGM0S>&8!};~A+Bsq?n@aPc{)) z$JzZ$o5MIYgP+=L`x|G&3VYTBqqAiFZz49<~h%3nIYnw zhv4(=nA^~G2*Ho9UXWv1LeB{mBx4Sp&T@n`O28431W!zY`*9Q%={^J+Y&E!V8?|@N z>pO=Fa}mwR_x75w_cBt0Srqq+=e8Ts3}NqPqL#sD6oU@gbm=|Q9q2bY#c5PXv5yw! zxcN1;!q!&YC@6UB~?a^a)wTe?)^HyxcYc;c6xJh{)T*Od*Pwn zKO;9`*{~pK*;)$xI41UP=&}6yBC=57g9>sfxIXiZVjf~9g^dI=En8>Hl4}c%jvbk= zU2C0P*0n^SYPO%jD4rTuJ$iIq&{zI;sAA|su0M~vdf&NXK(4HgEU{`R?%&aDb~J-a z|7dpPM8V7l8{2!d?sP~^wIYr&;iD{71!)Tmh0lahj1`NBsf*Xlo;dYQb^q$6@tpW#Y<5fOW1`OusJ$Jv!wtg8kVtjsy4^*xpBz1LkCOYHXN zx?4D>x(k**inANmdew8Jh7gErugTYol1+UbsRhplPtE7t=ksW zTBP*`Zp6>-E5hmPrdop_U1A-m(qi)#CBNetTSE0`E|#i>9kfoxJ_nDh6y0?0yaN}Z zhw{Ng_5Q;RRA2{h&FXP3TMU}(F*-^!BEQu75(^a&{{DBc34DeUEKPuv3@>dwL3)EX^B94VRI znbtKFN(KeIDy(}O^FD0_psOm&u4KlOwXbi0_LtObNN1l@a+=-KwR#hdno#(RP_0>B zAVHd`wjr2n6AYHcb;ZROlxLosRQ7`&?mQ*$*&QpJ;}GT}XhJpnh9w8-s8XQnU`d6I zE$SH~U$mOV0wk6i`UZTtcrW!8{>c?Mwzrg7U= zugZT4GXjUCbmzkCvZqPhcMGx;9v19AlgVu;IY7B8<;t_e>7{Ez)08N_6i~WYXp&LQ zM6#X{i`A@xdd|A!KA>AbnXL&0q0JY6xcaaiY_CVL%U84uDZn) za16+S2DqS_(&d8Ixt^ZR%1UUSvlgx+0i*4PTi$eKK`$Xq!RB_lDl9y;&?V9Mm-56`g%?xynI)Bm^8xO5axA3ztjC>r zUCmKsgXUX9b)~g*uq(YQrK-7Uu&3Xfl`GEz?J1U2CD&`i#Moy&TPUc;aR#^x_HtTL zMTE4Wi+C7nLN&xQhIPI0m*Logot?$PavnoIHI1qnxspn8vwa>HCnDzbnOwi-`N}R} z0xHY%zJmM4ABy})38t4ffP1Nlrps0p4p}y)Tm$ru=$o*!8~gNpbQ%J=Y!gW{y53^s z^e^5wY(?+bR(@T`bhd@f0eSW}Y~*Pl#_lm&lwvsmM?7#pY`f~_Ym!L`3IA0=giM(s zEC^JfnVOL6Q~Sdr=n?_8K#VC^vgHS(tJwiSGTv?dQDTi?bZL_&>S`{wXhg>D7`WyZ zd`S?OIky02E=EX}09RbbWVp7kV6zqXvb@bCO#_@5MKXTN$T=%AxwfcdA|=xd;BGfb1KMDfuyR?}EZW4*jSyT1GyA~GMcE5oEl^(G}NTAWsTb208ByJ146|DF5J`ZgBIz^Vm6k!`o9xG@PH&Ls)FHev88?U%_|eJFYas z0^pA!M-=f#jD&b)g_qoq^}4WC$8XNApe03q_nTKB zGXC=4fAgxBM!rgzlAlgo_P%rT8yWV1Q)}eKYfMNtmkbbkKY8FmFjVKS&S6sLA(i}) zN`6QsZ*~HQ3NM$@BP4>h0XB5=KEtc*>g&16Mvar;KX!4fsS$xXzYiDqvDT|Tem7+E z!&I&qtf(4d>HPime|>lQ&Bb@HBQ`V4oHs7^k{>+E*^Z(KF|z#CvGT58so3)hk!K&-Y?ca_@7cvP?_(R{MB{QOE?*R8 zRT)6{nNy9twKVsmxfz*Kj?;dP^gBvC;nYt1u~%TR4fcO5roFXs3;6Hq6$DY@4OeNm zL+61)4uSU)F0g_o!*hdntoqU}?f?MjV_XW*&^|1@zTq}ZCLOZAgeo8TRn=t-;_x!R z=mwByb?5xBC!5H)_K-N{}e&n6gwgTZF{-NajxJOXWqLW$;}Xg!MmSZG1AuA ztfKQ-M&J<#IN4{1n_r{@F}=(SZmloRt;p^Gxs;_<5=pFNlVG1zdPy(OAAR;^sqbYu z6cW4nS}PCDerIo|&J~F|-p~ICU&9d&R-x-CKgW~&eVYWsK5H{Z+{E%za(V|&o_Svzxws-*Zp1o$LYK8 z2u(om!9p3>GwfB^xSEjvef{BmT{EuvEs%*{o(R3cCZ2Fe3#!e~RIhx>1qHuakdkAW zOeErH&X+4#PA-59GoDxg9&}(haC*$H#+(XwZ}s+IraHkeS(~X*m-Q`~GAfa+oQa*) zloJ7&x}Vqzdu~U!q*-=gh02U89hAzm+yo4j2~)^WH92RHWuC0a7}2 zH&S54aIf)|pHCxstY(@tlVTZ?SuL{!i8@hbBQHKv03I;YMpLA+DMKnObkpC|5cfPG z34&DIl$FxoRA%MliUICcbn$GXH}rQ~&k9)Sl7uOwuD@Ycg06r4H(Zg(-sECihb|qE z3&VP8rR5ar`Z^$qo>b8KIn9a{oj|cnFQRVt7tl~xUNgFvju{x9V_m&K9Ca~NL30NA zFF35ou38nB6kiHfq6rUD8c9ayEVCWSGI|Psc3DQOG|WyBx?Do-^*d(sLHR%GI!`M` zt}foWoLEJS=&D&iC>cH;iq4Qztx?PfxlmF=i6Yaw-3sMP!a^Y66p~-#SF6sJz^vI7 zqp3?HG@@ZquyjVqnK_ONo=}Z>ZX{)CWfTy=|BLMIX2pmsQ-(#17Z9`I8x1a1z(V1s zpE=hhE!TKe**%Et%rH7E0u60KzmRxB`mwRxGmH;htS2DTW!4-Dk={dqj+z$a2jYCgLkEqqQpR>Gp@ zxNi`b)D_HeS_mb#%yKZJCxPw9{&?_UN(PytnE1-p&?xpZY$bQ4ReWw)uk~l!)7S|! zdMAOy`rfj>0N(A7PpI+RfW`I|(Q@dbkX>p}hmYIcM=#F~gc|nWH1OWC^^ro_FOb|N5rJ^>Dd>lRP;rI?WSQk(x>K*@O^({stxd#T}*C*K#G;+NVLYjNNZ zp#`DpAe+Ke-0HjheZPZTkxKyL#iiWwYy{W@rdy|l0sMrG%iZkP?wTwcn>O+gZ1-UK zz;6HpsmN%;QU~oq%_>pNmOa6%4?Q_6mtdqxBB07PpPeood>%!vL(&?yJ&#Cr*nDD( z#kZg74lI4@>2TnS145*?y*PZKM_Xc$*}gMZ;6O^-t=wkw*_lco#}i~k`7<)xaDQ-}N$erNT>pqaq~C?N3j$BN(4%mNfpK^USUnX$BG=?S@CNrhsU z&!l*9%hoR@1MCy-3EAz7OYx#M#x|Ix7bYQy@&$Zni@}Z|Q15hVc6w>|yoCCRO~k?H z@hY{Wye;Od`Pv~>S+#ak>mAS`=*NLB4aLDjLqhzn?r@+Tmd8^ptr0fLicLr=#gVoq zrx3rb4Y`wUS^eXPO6>JRO9lu)4vWs(AP~t7Y>WVQ;UfLEbr_rXWIMFkPeKA$0fgVM z(|V(WuA6qt0ljsR4e-`F7YofM|6rvY(Eb*q(okxQ@s`(5OtgRfG)VCyXgGJ)D+i!| zb>hi@IhPW*4k8Yqi8;)Cug9?7$Y*Fa4D9GbZkQfh;LVEFHgdVJ#e~rX@Hi6n2nLlv zPylof4>wuwu$9B)<=&X~_DG^HUW{aTM~fZ2VgB7l&1yQAm3BZ7_c$-OTAb& zBUfhP&TcZ~8gtj71H26@JelDf&1OdyM}D*3Q*=jzjU@~ct{R|0rYd`VfZ|!H8 zVua6Q4{KNhs%&UiR*V4w19ZzGU}QU-QF}e%F{N`Qvr5O7nR=+P@UDYFJM-SK3$ER4 za8rVWf>+lrZSKgpcd^J1wZ;$7OYnJwEpeFjeH6?4P+Q^+ze7VHO~ zCx!fRUG5J2gU^?Q|H0?!&9xr6*_BdvfUm>!!derRB{A11XkyN{YwJ(!(M~v++dS3* z;qpLEuP%46rvz_rx2VM0QG#}!zfI*5%_J@c&2WM9dIFI}vg&7$VE8z#bYqxrjhs^l z)eO4l!wuKcrrXrgla*42>TY#_aC|g5d4#qL{X_u%wV%ei9@Rww&;pG{s$nz0NBWvY zZ_qnRg^*a!MorKU(v#}7?Lm)F0OKH1Qt=eSp}X)Sj|sM;1^>j#7<}(-Ph<6M zJ8tPzvSN8RqR2y!dHt=~wkxY3*a&y9kg>8CU}F0t7n4Tz=Zm}71EOz((LE;iX5psw z=;gj@=t)K#(?dLO^@BbU4>aO!l;#$=!##cCW!gO^$S?0x1^A2;dw|cEu$S#>C!2U_ ztZDZeJb;6p>|iJRN!iH)A;n#9J4ASqmccB9!n9Vi9gd<))}EnN+nLOqy=-vYBxyQ? zj%KqXbP3J8*}T$=>F)+N%S0kmGfpfUS1Ju6d2QFgwb{)gnY+1%PAeGuP|0lPJJ7)j zyd6U<-r-uP#b;ju$S$p~nPlbS8UP{VIk(aHhHo-8P?0guF*Y(wifUpToaHQ+<$B^9 zHD5M*vA;M0F*>Qrk_nxy1A+Pp!$NK9r^zeo0&-wd^pep|f}XjxEO6VTw$zwU znYi1j#XDN6p=W|TR)kLZ9ap<0!BDaOF!(GH#6aHp#%Q*%bIg8&Fs`lWfW8x1>7p8* z?nV41>JGQ7$>k6AZdn-(E$el>QM-N$sx>Rc8}fgT|MM6BoSyvg`1micrvK}&zc~KS z8T{ktCx1Nor~Bgo4O`4>^;xA@mTkN?6h{ux&D|7oX|=>BKJ2?If;&$48R&t#0kjHB^|{~v zh23cb$BH;iJ6O^2#RozX@yl2{`m?v4e--hFwsOlM81V}RBjO$$A`uUfh=ZO(B;p|w z@nB&+SXd8{h^^-kiMV%F=n#n*JcmfcLnPu)I}#BIq!o`iP@l1yXdR6BuN;f0%LkzOJb4AxzSb$rkEUmw= zEK{2Wn2}RbvV<4h0*g@md_adl$`%NIZ3vuY3(vbeKztb(46LZp8qkn4Wrm1?D~PH0 zTrr|nvdU6YvcEeC+0O7L>?`yw4C^$Qid{ik@Yq6Te&oX$8T(xD5-9$fB~qkMVzb-m zw3cqcX^J3N9#i&?W;`{Qp$p!_qc((W@l<-MRP_!LS`?+E$%?69T&R-S${xc}6pE}` za)J6AwC=8csa^c5okV+RH$k`N^#PrtM6Am-l-!bGXE;;72>(z-W~r+U`@2_0)lcx4 z4zj)~z?3!pWkxMAx6s4g{QIxSN>-(sg=G$3!)HTwQ>1l(mnIpbN|V>GNX|v2hhTIB zD&%i|HSm-TQyw3I>@nzMg{mU_+v|(gjEeaepu=qRpF^_#;OlT;|0vQ^tpe6M?lgK=$5ny5HUZW6Bhu4M? z0!o_IGY%rN#v*6U1Qoq*@WG|_p;aR91r{A;WXbQCXw$J(5WbYx?ksJ!uO`--g0S$ev;>NTV=Z8%#xJvM+I%eUQ|LG<`CY=CWNX?TYukVs|`I{OR&7d=SE=q_|5tED#msfC=DCq{@OMwmx60qj$XFT zwTqW{BD2hvVhE#$!pGrg^V*X(xkF<3ff{24Bc9wr9d;Mzz3zO4a!bCzw9g z0HYF`vC9w9Mi~41rlsZDeps+laRq545uRFUve%Vbjlm}xccjen6wy_0N(sTt0s* zuaUPIT@nU=yiB<=|0mbi-+}AD64t=byD~r!_SWV~=<(y^Bb6^r!k}#l=0H#*RqRKE}+;7I<(25~G^j_4GSgA2cnI8LO zgxT1`)8*Y^0ULnP_ib|n><3SmEk9Ove7}vQkv-o~0bl**SHFt5HhBp(J~b2r3F*jos|Vc@pP1XWdCXNdrgxck`^DI$q?ny9vq|s&^?z=1l{u_fIUwE)3bkY zo+p6hc`6{Dy~6KA#(Bg-?RfZ{2z53VU}so5X*8J5m=HPxvw`B_a&}9jdMap~F9wM7 zWnpmk3V!ockT*{NbTb^@W@uhwd`{wS2Wii+H6wu4j0#aR7BI~qb{L-+83#QRnL7Er zG9lyPI0k@l1kz0Ja_n9QY^aNAAJ(6`<*imQ2R!OZ4jrkWMv#_^x`MRPBwWy-uCq1ZSQ<)Z@X$h*gI<} zFgp-?<=ob-!)wAH^3$u!-d>WD0oV)8)NGqP?0j%m%64)v)tco+M8$}Mv;E*~KRDYP z&r`b2R#i^L)bLgC$TwoWTEf7LkjP4)u<&5q2DF7r@En*f#ZaWZYKgQByMyH}aOmSKz@e;Ww%*Le0y51R$$44I z(z-pv>0mS;U#>JOQHRGEtNyco&9tQ=ph7JP6_|}~{_XNZV5;tBnX?tW#ACMzz+fY{l$if~X5$WEa=8Bf-i=evdT;Jfaw zQa+T&d``ZhD#DTWjtaU&``GdI)i>`>3_*b{@A7Walae;2ov#e6)H|_7{Yg;>f z8?kQcsWi(-Vc)IQRGh^&w%2{_YmLyXPT8cED&G#nvgDn-(1{NkMrtWM)21*HauSwN)prS=HfnVJ$Ya@N>g36 z7O+(@-B8Ch{LEUjZ9%^|ju4svF&%%}XzJn3{*EerS`-;)=^4aJ{)wtBysX>*)#d97 zI`}q|bDHg^8w~b5Te0N!Be?uF8}Bk{m5i!wb?qM{-L3n$@gZ(aP} Date: Fri, 21 Nov 2025 13:45:17 +0200 Subject: [PATCH 304/316] ci: add golangci-lint configuration and GitHub Actions workflow (#205) - Add .golangci.yml with standard linters (gosimple, govet, staticcheck, etc.) - Add GitHub Actions workflow for lint checks on PRs - Fix existing linting issues: - Fix errors.Is argument order in event_collector - Fix race condition with wg.Add in evgen - Remove unnecessary type assertions in pipeline_controller - Replace deprecated pointer package with ptr - Format all Go files with goimports --- .github/workflows/lint.yaml | 67 +++++++++++++++++++ .golangci.yml | 25 +++++++ Makefile | 2 +- api/v1alpha1/clustervectorpipeline.go | 4 +- api/v1alpha1/vectorpipeline.go | 4 +- api/v1alpha1/zz_generated.deepcopy.go | 2 +- cmd/event_collector/main.go | 18 ++--- cmd/evgen/main.go | 7 +- cmd/manager/main.go | 3 +- internal/config/agent.go | 8 ++- internal/config/aggregator.go | 10 +-- internal/config/config.go | 10 +-- internal/config/configcheck/configcheck.go | 3 +- .../config/configcheck/configcheck_config.go | 3 +- internal/config/types.go | 1 + .../clustervectoraggregator_controller.go | 16 +++-- ...clustervectoraggregator_controller_test.go | 1 + internal/controller/pipeline_controller.go | 21 +++--- .../controller/pipeline_controller_test.go | 1 + internal/controller/suite_test.go | 4 +- internal/controller/vector_controller.go | 6 +- internal/controller/vector_controller_test.go | 1 + .../controller/vectoraggregator_controller.go | 16 +++-- .../vectoraggregator_controller_test.go | 3 +- internal/evcollector/collector.go | 6 +- internal/evcollector/event.go | 6 +- internal/pipeline/hash.go | 1 + internal/pipeline/pipeline.go | 4 +- internal/utils/hash/hash_test.go | 3 +- internal/utils/k8s/k8s_test.go | 3 +- internal/utils/k8s/label.go | 28 ++++---- internal/vector/aggregator/config.go | 6 +- internal/vector/aggregator/controller.go | 9 +-- internal/vector/aggregator/deployment.go | 7 +- internal/vector/aggregator/event_collector.go | 5 +- internal/vector/aggregator/podmonitor.go | 3 +- internal/vector/aggregator/rbac.go | 4 +- internal/vector/aggregator/service.go | 3 +- internal/vector/gen/event.pb.go | 5 +- internal/vector/gen/vector.pb.go | 5 +- internal/vector/gen/vector_grpc.pb.go | 1 + internal/vector/vectoragent/vectoragent.go | 6 +- .../vector/vectoragent/vectoragent_config.go | 3 +- .../vectoragent/vectoragent_controller.go | 6 +- .../vectoragent/vectoragent_daemonset.go | 3 +- .../vector/vectoragent/vectoragent_default.go | 3 +- .../vector/vectoragent/vectoragent_service.go | 3 +- 47 files changed, 252 insertions(+), 107 deletions(-) create mode 100644 .github/workflows/lint.yaml create mode 100644 .golangci.yml diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml new file mode 100644 index 00000000..b3841489 --- /dev/null +++ b/.github/workflows/lint.yaml @@ -0,0 +1,67 @@ +name: Lint + +on: + push: + branches: [main] + pull_request: + branches: [main] + +permissions: + contents: read + +jobs: + golangci-lint: + name: golangci-lint + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + cache: true + + - name: Run golangci-lint + uses: golangci/golangci-lint-action@v6 + with: + version: v1.64 + args: --timeout=5m + + go-fmt: + name: go fmt + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + cache: true + + - name: Check formatting + run: | + if [ -n "$(gofmt -l .)" ]; then + echo "The following files are not formatted:" + gofmt -l . + exit 1 + fi + + go-vet: + name: go vet + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + cache: true + + - name: Run go vet + run: go vet ./... diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 00000000..11c66482 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,25 @@ +run: + timeout: 5m + modules-download-mode: readonly + +linters: + enable: + - gosimple + - govet + - ineffassign + - staticcheck + - unused + - errcheck + - gofmt + - goimports + +linters-settings: + goimports: + local-prefixes: github.com/kaasops/vector-operator + +issues: + exclude-rules: + # Exclude some linters from running on tests files + - path: _test\.go + linters: + - errcheck diff --git a/Makefile b/Makefile index c05a018a..dbce874b 100644 --- a/Makefile +++ b/Makefile @@ -165,7 +165,7 @@ GOLANGCI_LINT = $(LOCALBIN)/golangci-lint KUSTOMIZE_VERSION ?= v5.4.3 CONTROLLER_TOOLS_VERSION ?= v0.16.1 ENVTEST_VERSION ?= release-0.19 -GOLANGCI_LINT_VERSION ?= v1.59.1 +GOLANGCI_LINT_VERSION ?= v1.64.8 .PHONY: kustomize kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. diff --git a/api/v1alpha1/clustervectorpipeline.go b/api/v1alpha1/clustervectorpipeline.go index da07cbe8..02a40016 100644 --- a/api/v1alpha1/clustervectorpipeline.go +++ b/api/v1alpha1/clustervectorpipeline.go @@ -2,10 +2,12 @@ package v1alpha1 import ( "context" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/kaasops/vector-operator/internal/utils/k8s" "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/kaasops/vector-operator/internal/utils/k8s" ) func (vp *ClusterVectorPipeline) GetSpec() VectorPipelineSpec { diff --git a/api/v1alpha1/vectorpipeline.go b/api/v1alpha1/vectorpipeline.go index 5fa963c0..a21b295c 100644 --- a/api/v1alpha1/vectorpipeline.go +++ b/api/v1alpha1/vectorpipeline.go @@ -2,10 +2,12 @@ package v1alpha1 import ( "context" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/kaasops/vector-operator/internal/utils/k8s" "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/kaasops/vector-operator/internal/utils/k8s" ) type VectorPipelineRole string diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 88f3346a..5b439271 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -21,7 +21,7 @@ limitations under the License. package v1alpha1 import ( - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" ) diff --git a/cmd/event_collector/main.go b/cmd/event_collector/main.go index ae2f07f0..8d986c0c 100644 --- a/cmd/event_collector/main.go +++ b/cmd/event_collector/main.go @@ -5,20 +5,22 @@ import ( "errors" "flag" "fmt" - "github.com/fsnotify/fsnotify" - "github.com/kaasops/vector-operator/internal/buildinfo" - "github.com/kaasops/vector-operator/internal/evcollector" - "github.com/prometheus/client_golang/prometheus/promhttp" - "github.com/spf13/viper" - "k8s.io/client-go/kubernetes" "log/slog" "net" "net/http" "os" "os/signal" - ctrl "sigs.k8s.io/controller-runtime" "strings" "syscall" + + "github.com/fsnotify/fsnotify" + "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/spf13/viper" + "k8s.io/client-go/kubernetes" + ctrl "sigs.k8s.io/controller-runtime" + + "github.com/kaasops/vector-operator/internal/buildinfo" + "github.com/kaasops/vector-operator/internal/evcollector" ) func main() { @@ -112,7 +114,7 @@ func main() { http.Handle("/metrics", promhttp.Handler()) go func() { - if err = http.ListenAndServe(net.JoinHostPort("", *port), nil); err != nil && !errors.Is(http.ErrServerClosed, err) { + if err = http.ListenAndServe(net.JoinHostPort("", *port), nil); err != nil && !errors.Is(err, http.ErrServerClosed) { log.Error("failed to start http server", "error", err) os.Exit(1) } diff --git a/cmd/evgen/main.go b/cmd/evgen/main.go index 21c50a48..41c0ef94 100644 --- a/cmd/evgen/main.go +++ b/cmd/evgen/main.go @@ -4,11 +4,12 @@ import ( "context" "flag" "fmt" - "k8s.io/apimachinery/pkg/util/rand" - ctrl "sigs.k8s.io/controller-runtime" "sync" "time" + "k8s.io/apimachinery/pkg/util/rand" + ctrl "sigs.k8s.io/controller-runtime" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" @@ -34,8 +35,8 @@ func main() { wg := sync.WaitGroup{} for i := 0; i < *workers; i++ { + wg.Add(1) go func() { - wg.Add(1) defer wg.Done() clientset, err := kubernetes.NewForConfig(config) diff --git a/cmd/manager/main.go b/cmd/manager/main.go index 2f8ea057..61be4773 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -21,10 +21,11 @@ import ( "crypto/tls" "flag" "fmt" - "github.com/kaasops/vector-operator/internal/buildinfo" "os" "time" + "github.com/kaasops/vector-operator/internal/buildinfo" + monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" diff --git a/internal/config/agent.go b/internal/config/agent.go index e98afb4a..3f53e8cf 100644 --- a/internal/config/agent.go +++ b/internal/config/agent.go @@ -2,12 +2,14 @@ package config import ( "fmt" - vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" - "github.com/kaasops/vector-operator/internal/pipeline" - "github.com/kaasops/vector-operator/internal/utils/k8s" + "gopkg.in/yaml.v2" "k8s.io/apimachinery/pkg/labels" goyaml "sigs.k8s.io/yaml" + + vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" + "github.com/kaasops/vector-operator/internal/pipeline" + "github.com/kaasops/vector-operator/internal/utils/k8s" ) const ( diff --git a/internal/config/aggregator.go b/internal/config/aggregator.go index ec469162..c5b06344 100644 --- a/internal/config/aggregator.go +++ b/internal/config/aggregator.go @@ -3,13 +3,15 @@ package config import ( "errors" "fmt" - "github.com/kaasops/vector-operator/internal/common" - "github.com/kaasops/vector-operator/internal/pipeline" - "github.com/stoewer/go-strcase" - corev1 "k8s.io/api/core/v1" "net" "strconv" "strings" + + "github.com/stoewer/go-strcase" + corev1 "k8s.io/api/core/v1" + + "github.com/kaasops/vector-operator/internal/common" + "github.com/kaasops/vector-operator/internal/pipeline" ) func BuildAggregatorConfig(params VectorConfigParams, pipelines ...pipeline.Pipeline) (*VectorConfig, error) { diff --git a/internal/config/config.go b/internal/config/config.go index eaaa52ce..0177ba5c 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -20,13 +20,15 @@ import ( "encoding/json" "errors" "fmt" - vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" - "github.com/kaasops/vector-operator/internal/evcollector" + "net" + "strconv" + "github.com/mitchellh/mapstructure" "gopkg.in/yaml.v2" - "net" goyaml "sigs.k8s.io/yaml" - "strconv" + + vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" + "github.com/kaasops/vector-operator/internal/evcollector" ) var ( diff --git a/internal/config/configcheck/configcheck.go b/internal/config/configcheck/configcheck.go index cd331922..ffc68bf4 100644 --- a/internal/config/configcheck/configcheck.go +++ b/internal/config/configcheck/configcheck.go @@ -24,7 +24,6 @@ import ( api_errors "k8s.io/apimachinery/pkg/api/errors" - "github.com/kaasops/vector-operator/internal/utils/k8s" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" @@ -36,6 +35,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/log" + "github.com/kaasops/vector-operator/internal/utils/k8s" + vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" ) diff --git a/internal/config/configcheck/configcheck_config.go b/internal/config/configcheck/configcheck_config.go index 3d3a7107..6db0bad3 100644 --- a/internal/config/configcheck/configcheck_config.go +++ b/internal/config/configcheck/configcheck_config.go @@ -19,10 +19,11 @@ package configcheck import ( "context" - "github.com/kaasops/vector-operator/internal/utils/compression" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/kaasops/vector-operator/internal/utils/compression" ) func (cc *ConfigCheck) createVectorConfigCheckConfig(ctx context.Context) (*corev1.Secret, error) { diff --git a/internal/config/types.go b/internal/config/types.go index a81fc54a..b5b52f1e 100644 --- a/internal/config/types.go +++ b/internal/config/types.go @@ -19,6 +19,7 @@ package config import ( "encoding/json" "fmt" + "github.com/kaasops/vector-operator/internal/utils/hash" corev1 "k8s.io/api/core/v1" diff --git a/internal/controller/clustervectoraggregator_controller.go b/internal/controller/clustervectoraggregator_controller.go index 7537ecfa..a2aad04d 100644 --- a/internal/controller/clustervectoraggregator_controller.go +++ b/internal/controller/clustervectoraggregator_controller.go @@ -19,12 +19,8 @@ package controller import ( "context" "errors" - "github.com/kaasops/vector-operator/internal/config" - "github.com/kaasops/vector-operator/internal/config/configcheck" - "github.com/kaasops/vector-operator/internal/pipeline" - "github.com/kaasops/vector-operator/internal/utils/hash" - "github.com/kaasops/vector-operator/internal/utils/k8s" - "github.com/kaasops/vector-operator/internal/vector/aggregator" + "time" + monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -38,7 +34,13 @@ import ( "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/source" - "time" + + "github.com/kaasops/vector-operator/internal/config" + "github.com/kaasops/vector-operator/internal/config/configcheck" + "github.com/kaasops/vector-operator/internal/pipeline" + "github.com/kaasops/vector-operator/internal/utils/hash" + "github.com/kaasops/vector-operator/internal/utils/k8s" + "github.com/kaasops/vector-operator/internal/vector/aggregator" v1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" ) diff --git a/internal/controller/clustervectoraggregator_controller_test.go b/internal/controller/clustervectoraggregator_controller_test.go index 75e9643f..5b416c92 100644 --- a/internal/controller/clustervectoraggregator_controller_test.go +++ b/internal/controller/clustervectoraggregator_controller_test.go @@ -18,6 +18,7 @@ package controller import ( "context" + "sigs.k8s.io/controller-runtime/pkg/event" . "github.com/onsi/ginkgo/v2" diff --git a/internal/controller/pipeline_controller.go b/internal/controller/pipeline_controller.go index adf9ca0d..a1d3fa3d 100644 --- a/internal/controller/pipeline_controller.go +++ b/internal/controller/pipeline_controller.go @@ -23,16 +23,14 @@ import ( "reflect" "time" - "github.com/kaasops/vector-operator/internal/config/configcheck" - "github.com/kaasops/vector-operator/internal/vector/aggregator" - "github.com/kaasops/vector-operator/internal/vector/vectoragent" "golang.org/x/sync/errgroup" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/predicate" - "github.com/kaasops/vector-operator/api/v1alpha1" - "github.com/kaasops/vector-operator/internal/config" - "github.com/kaasops/vector-operator/internal/pipeline" + "github.com/kaasops/vector-operator/internal/config/configcheck" + "github.com/kaasops/vector-operator/internal/vector/aggregator" + "github.com/kaasops/vector-operator/internal/vector/vectoragent" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes" ctrl "sigs.k8s.io/controller-runtime" @@ -40,6 +38,10 @@ import ( "sigs.k8s.io/controller-runtime/pkg/event" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/kaasops/vector-operator/api/v1alpha1" + "github.com/kaasops/vector-operator/internal/config" + "github.com/kaasops/vector-operator/internal/pipeline" ) type PipelineReconciler struct { @@ -335,14 +337,11 @@ func (r *PipelineReconciler) SetupWithManager(mgr ctrl.Manager) error { var specAndAnnotationsPredicate = predicate.Funcs{ UpdateFunc: func(e event.UpdateEvent) bool { - oldObject := e.ObjectOld.(client.Object) - newObject := e.ObjectNew.(client.Object) - - if oldObject.GetGeneration() != newObject.GetGeneration() { + if e.ObjectOld.GetGeneration() != e.ObjectNew.GetGeneration() { return true } - if !reflect.DeepEqual(oldObject.GetAnnotations(), newObject.GetAnnotations()) { + if !reflect.DeepEqual(e.ObjectOld.GetAnnotations(), e.ObjectNew.GetAnnotations()) { return true } diff --git a/internal/controller/pipeline_controller_test.go b/internal/controller/pipeline_controller_test.go index d0e58d36..28a624e6 100644 --- a/internal/controller/pipeline_controller_test.go +++ b/internal/controller/pipeline_controller_test.go @@ -18,6 +18,7 @@ package controller import ( "context" + "sigs.k8s.io/controller-runtime/pkg/event" . "github.com/onsi/ginkgo/v2" diff --git a/internal/controller/suite_test.go b/internal/controller/suite_test.go index bb5fd174..b82c3251 100644 --- a/internal/controller/suite_test.go +++ b/internal/controller/suite_test.go @@ -25,7 +25,7 @@ import ( "time" "k8s.io/client-go/kubernetes" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -66,7 +66,7 @@ var _ = BeforeSuite(func() { By("bootstrapping test environment") testEnv = &envtest.Environment{ - UseExistingCluster: pointer.Bool(true), + UseExistingCluster: ptr.To(true), CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, ErrorIfCRDPathMissing: true, diff --git a/internal/controller/vector_controller.go b/internal/controller/vector_controller.go index 54961e39..37158a43 100644 --- a/internal/controller/vector_controller.go +++ b/internal/controller/vector_controller.go @@ -19,10 +19,11 @@ package controller import ( "context" "errors" + "time" + "sigs.k8s.io/controller-runtime/pkg/event" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/source" - "time" "github.com/kaasops/vector-operator/internal/config" "github.com/kaasops/vector-operator/internal/config/configcheck" @@ -36,7 +37,6 @@ import ( rbacv1 "k8s.io/api/rbac/v1" - "github.com/kaasops/vector-operator/api/v1alpha1" monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" api_errors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" @@ -47,6 +47,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/predicate" + + "github.com/kaasops/vector-operator/api/v1alpha1" ) // VectorReconciler reconciles a Vector object diff --git a/internal/controller/vector_controller_test.go b/internal/controller/vector_controller_test.go index 5c6a2b62..24e1d23e 100644 --- a/internal/controller/vector_controller_test.go +++ b/internal/controller/vector_controller_test.go @@ -18,6 +18,7 @@ package controller import ( "context" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "k8s.io/apimachinery/pkg/api/errors" diff --git a/internal/controller/vectoraggregator_controller.go b/internal/controller/vectoraggregator_controller.go index e5296867..93f7ffe6 100644 --- a/internal/controller/vectoraggregator_controller.go +++ b/internal/controller/vectoraggregator_controller.go @@ -19,12 +19,8 @@ package controller import ( "context" "errors" - "github.com/kaasops/vector-operator/internal/config" - "github.com/kaasops/vector-operator/internal/config/configcheck" - "github.com/kaasops/vector-operator/internal/pipeline" - "github.com/kaasops/vector-operator/internal/utils/hash" - "github.com/kaasops/vector-operator/internal/utils/k8s" - "github.com/kaasops/vector-operator/internal/vector/aggregator" + "time" + monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -37,7 +33,13 @@ import ( "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/source" - "time" + + "github.com/kaasops/vector-operator/internal/config" + "github.com/kaasops/vector-operator/internal/config/configcheck" + "github.com/kaasops/vector-operator/internal/pipeline" + "github.com/kaasops/vector-operator/internal/utils/hash" + "github.com/kaasops/vector-operator/internal/utils/k8s" + "github.com/kaasops/vector-operator/internal/vector/aggregator" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" diff --git a/internal/controller/vectoraggregator_controller_test.go b/internal/controller/vectoraggregator_controller_test.go index d65b3f0e..757d2278 100644 --- a/internal/controller/vectoraggregator_controller_test.go +++ b/internal/controller/vectoraggregator_controller_test.go @@ -18,9 +18,10 @@ package controller import ( "context" - "sigs.k8s.io/controller-runtime/pkg/event" "time" + "sigs.k8s.io/controller-runtime/pkg/event" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "k8s.io/apimachinery/pkg/api/errors" diff --git a/internal/evcollector/collector.go b/internal/evcollector/collector.go index bb503a7c..f1a3412c 100644 --- a/internal/evcollector/collector.go +++ b/internal/evcollector/collector.go @@ -2,14 +2,16 @@ package evcollector import ( "context" - "github.com/kaasops/vector-operator/internal/vector/gen" + "time" + "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/fields" "k8s.io/client-go/rest" "k8s.io/client-go/tools/cache" - "time" + + "github.com/kaasops/vector-operator/internal/vector/gen" ) type Logger interface { diff --git a/internal/evcollector/event.go b/internal/evcollector/event.go index 3ccfa258..a46ae39f 100644 --- a/internal/evcollector/event.go +++ b/internal/evcollector/event.go @@ -1,9 +1,11 @@ package evcollector import ( - "github.com/kaasops/vector-operator/internal/vector/gen" - corev1 "k8s.io/api/core/v1" "time" + + corev1 "k8s.io/api/core/v1" + + "github.com/kaasops/vector-operator/internal/vector/gen" ) func k8sEventToVectorLog(ev *corev1.Event) *gen.Log { diff --git a/internal/pipeline/hash.go b/internal/pipeline/hash.go index 84c6178b..0219849a 100644 --- a/internal/pipeline/hash.go +++ b/internal/pipeline/hash.go @@ -18,6 +18,7 @@ package pipeline import ( "encoding/json" + "github.com/kaasops/vector-operator/api/v1alpha1" "github.com/kaasops/vector-operator/internal/common" "github.com/kaasops/vector-operator/internal/utils/hash" diff --git a/internal/pipeline/pipeline.go b/internal/pipeline/pipeline.go index 559bef6e..3a42ab6b 100644 --- a/internal/pipeline/pipeline.go +++ b/internal/pipeline/pipeline.go @@ -19,10 +19,12 @@ package pipeline import ( "context" "fmt" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/kaasops/vector-operator/api/v1alpha1" "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/kaasops/vector-operator/api/v1alpha1" ) type Pipeline interface { diff --git a/internal/utils/hash/hash_test.go b/internal/utils/hash/hash_test.go index 46ce0239..09ab20b7 100644 --- a/internal/utils/hash/hash_test.go +++ b/internal/utils/hash/hash_test.go @@ -19,8 +19,9 @@ package hash_test import ( "testing" - "github.com/kaasops/vector-operator/internal/utils/hash" "github.com/stretchr/testify/require" + + "github.com/kaasops/vector-operator/internal/utils/hash" ) func TestGet(t *testing.T) { diff --git a/internal/utils/k8s/k8s_test.go b/internal/utils/k8s/k8s_test.go index 2c5fd442..d02814db 100644 --- a/internal/utils/k8s/k8s_test.go +++ b/internal/utils/k8s/k8s_test.go @@ -24,7 +24,6 @@ import ( // . "github.com/onsi/ginkgo/v2" // . "github.com/onsi/gomega" - "github.com/kaasops/vector-operator/internal/utils/k8s" "github.com/stretchr/testify/require" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -36,6 +35,8 @@ import ( fakeclientset "k8s.io/client-go/kubernetes/fake" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" + + "github.com/kaasops/vector-operator/internal/utils/k8s" ) type objCase struct { diff --git a/internal/utils/k8s/label.go b/internal/utils/k8s/label.go index 72c63ebc..9f75c8e2 100644 --- a/internal/utils/k8s/label.go +++ b/internal/utils/k8s/label.go @@ -42,18 +42,18 @@ const ( // being merged into the destination (dst) labels. If a key exists in both maps, // the destination value is preserved. func MergeLabels(dst, src map[string]string) map[string]string { - if dst == nil { + if dst == nil { dst = make(map[string]string) - } - - if src == nil { - return dst - } - - for k, v := range src { - if _, ok := dst[k]; !ok { - dst[k] = v - } - } - return dst -} \ No newline at end of file + } + + if src == nil { + return dst + } + + for k, v := range src { + if _, ok := dst[k]; !ok { + dst[k] = v + } + } + return dst +} diff --git a/internal/vector/aggregator/config.go b/internal/vector/aggregator/config.go index d0593bb6..cb2fab5b 100644 --- a/internal/vector/aggregator/config.go +++ b/internal/vector/aggregator/config.go @@ -2,10 +2,12 @@ package aggregator import ( "context" - "github.com/kaasops/vector-operator/internal/utils/compression" - "github.com/kaasops/vector-operator/internal/utils/k8s" + corev1 "k8s.io/api/core/v1" "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/kaasops/vector-operator/internal/utils/compression" + "github.com/kaasops/vector-operator/internal/utils/k8s" ) func (ctrl *Controller) ensureVectorAggregatorConfig(ctx context.Context) error { diff --git a/internal/vector/aggregator/controller.go b/internal/vector/aggregator/controller.go index 09dae11a..8678906b 100644 --- a/internal/vector/aggregator/controller.go +++ b/internal/vector/aggregator/controller.go @@ -3,10 +3,6 @@ package aggregator import ( "context" - vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" - "github.com/kaasops/vector-operator/internal/buildinfo" - "github.com/kaasops/vector-operator/internal/config" - "github.com/kaasops/vector-operator/internal/utils/k8s" monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" corev1 "k8s.io/api/core/v1" resourcev1 "k8s.io/apimachinery/pkg/api/resource" @@ -17,6 +13,11 @@ import ( "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" + + vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" + "github.com/kaasops/vector-operator/internal/buildinfo" + "github.com/kaasops/vector-operator/internal/config" + "github.com/kaasops/vector-operator/internal/utils/k8s" ) type Aggregator interface { diff --git a/internal/vector/aggregator/deployment.go b/internal/vector/aggregator/deployment.go index d40087bb..8957dd64 100644 --- a/internal/vector/aggregator/deployment.go +++ b/internal/vector/aggregator/deployment.go @@ -4,13 +4,14 @@ import ( "context" "time" - "github.com/kaasops/vector-operator/internal/common" - "github.com/kaasops/vector-operator/internal/config" - "github.com/kaasops/vector-operator/internal/utils/k8s" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/kaasops/vector-operator/internal/common" + "github.com/kaasops/vector-operator/internal/config" + "github.com/kaasops/vector-operator/internal/utils/k8s" ) func (ctrl *Controller) ensureVectorAggregatorDeployment(ctx context.Context) error { diff --git a/internal/vector/aggregator/event_collector.go b/internal/vector/aggregator/event_collector.go index 8ca92994..52cab981 100644 --- a/internal/vector/aggregator/event_collector.go +++ b/internal/vector/aggregator/event_collector.go @@ -4,8 +4,6 @@ import ( "context" "encoding/json" - "github.com/kaasops/vector-operator/internal/evcollector" - "github.com/kaasops/vector-operator/internal/utils/k8s" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" @@ -14,6 +12,9 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/kaasops/vector-operator/internal/evcollector" + "github.com/kaasops/vector-operator/internal/utils/k8s" ) func (ctrl *Controller) ensureEventCollector(ctx context.Context) error { diff --git a/internal/vector/aggregator/podmonitor.go b/internal/vector/aggregator/podmonitor.go index cab43e4a..cbf69552 100644 --- a/internal/vector/aggregator/podmonitor.go +++ b/internal/vector/aggregator/podmonitor.go @@ -3,10 +3,11 @@ package aggregator import ( "context" - "github.com/kaasops/vector-operator/internal/utils/k8s" monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/kaasops/vector-operator/internal/utils/k8s" ) func (ctrl *Controller) ensureVectorAggregatorPodMonitor(ctx context.Context) error { diff --git a/internal/vector/aggregator/rbac.go b/internal/vector/aggregator/rbac.go index c5bf2a21..82f5be63 100644 --- a/internal/vector/aggregator/rbac.go +++ b/internal/vector/aggregator/rbac.go @@ -2,10 +2,12 @@ package aggregator import ( "context" - "github.com/kaasops/vector-operator/internal/utils/k8s" + corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/kaasops/vector-operator/internal/utils/k8s" ) const ApiPort = 8686 diff --git a/internal/vector/aggregator/service.go b/internal/vector/aggregator/service.go index a0b0cae4..cdae4ed0 100644 --- a/internal/vector/aggregator/service.go +++ b/internal/vector/aggregator/service.go @@ -4,13 +4,14 @@ import ( "context" "maps" - "github.com/kaasops/vector-operator/internal/utils/k8s" "github.com/stoewer/go-strcase" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/util/intstr" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/kaasops/vector-operator/internal/utils/k8s" ) func (ctrl *Controller) ensureVectorAggregatorService(ctx context.Context) error { diff --git a/internal/vector/gen/event.pb.go b/internal/vector/gen/event.pb.go index 9d55d371..359453c6 100644 --- a/internal/vector/gen/event.pb.go +++ b/internal/vector/gen/event.pb.go @@ -7,11 +7,12 @@ package gen import ( + reflect "reflect" + sync "sync" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" timestamppb "google.golang.org/protobuf/types/known/timestamppb" - reflect "reflect" - sync "sync" ) const ( diff --git a/internal/vector/gen/vector.pb.go b/internal/vector/gen/vector.pb.go index 4c49c5b6..89a63029 100644 --- a/internal/vector/gen/vector.pb.go +++ b/internal/vector/gen/vector.pb.go @@ -7,10 +7,11 @@ package gen import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" + + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" ) const ( diff --git a/internal/vector/gen/vector_grpc.pb.go b/internal/vector/gen/vector_grpc.pb.go index d0e4691f..67b1c8f6 100644 --- a/internal/vector/gen/vector_grpc.pb.go +++ b/internal/vector/gen/vector_grpc.pb.go @@ -8,6 +8,7 @@ package gen import ( context "context" + grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" diff --git a/internal/vector/vectoragent/vectoragent.go b/internal/vector/vectoragent/vectoragent.go index 08011bc4..3a864de6 100644 --- a/internal/vector/vectoragent/vectoragent.go +++ b/internal/vector/vectoragent/vectoragent.go @@ -18,11 +18,13 @@ package vectoragent import ( "context" + + "k8s.io/client-go/kubernetes" + "sigs.k8s.io/controller-runtime/pkg/client" + vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" "github.com/kaasops/vector-operator/internal/config" "github.com/kaasops/vector-operator/internal/utils/k8s" - "k8s.io/client-go/kubernetes" - "sigs.k8s.io/controller-runtime/pkg/client" ) type Controller struct { diff --git a/internal/vector/vectoragent/vectoragent_config.go b/internal/vector/vectoragent/vectoragent_config.go index ce6b1950..28dcfcc2 100644 --- a/internal/vector/vectoragent/vectoragent_config.go +++ b/internal/vector/vectoragent/vectoragent_config.go @@ -19,9 +19,10 @@ package vectoragent import ( "context" - "github.com/kaasops/vector-operator/internal/utils/compression" corev1 "k8s.io/api/core/v1" "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/kaasops/vector-operator/internal/utils/compression" ) func (ctrl *Controller) createVectorAgentConfig(ctx context.Context) (*corev1.Secret, error) { diff --git a/internal/vector/vectoragent/vectoragent_controller.go b/internal/vector/vectoragent/vectoragent_controller.go index 3a27e41b..d443e8e0 100644 --- a/internal/vector/vectoragent/vectoragent_controller.go +++ b/internal/vector/vectoragent/vectoragent_controller.go @@ -21,12 +21,13 @@ import ( "time" - "github.com/kaasops/vector-operator/internal/common" - "github.com/kaasops/vector-operator/internal/utils/k8s" monitorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/kaasops/vector-operator/internal/common" + "github.com/kaasops/vector-operator/internal/utils/k8s" ) func (ctrl *Controller) EnsureVectorAgent(ctx context.Context) error { @@ -162,7 +163,6 @@ func (ctrl *Controller) matchLabelsForVectorAgent() map[string]string { func (ctrl *Controller) labelsForVectorAgent() map[string]string { basicLabels := ctrl.matchLabelsForVectorAgent() - labels := k8s.MergeLabels(basicLabels, ctrl.Vector.Spec.Agent.Labels) return labels diff --git a/internal/vector/vectoragent/vectoragent_daemonset.go b/internal/vector/vectoragent/vectoragent_daemonset.go index 42616866..33c318ea 100644 --- a/internal/vector/vectoragent/vectoragent_daemonset.go +++ b/internal/vector/vectoragent/vectoragent_daemonset.go @@ -17,10 +17,11 @@ limitations under the License. package vectoragent import ( - "github.com/kaasops/vector-operator/internal/config" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/kaasops/vector-operator/internal/config" ) func (ctrl *Controller) createVectorAgentDaemonSet() *appsv1.DaemonSet { diff --git a/internal/vector/vectoragent/vectoragent_default.go b/internal/vector/vectoragent/vectoragent_default.go index d537a0e0..bb0dd489 100644 --- a/internal/vector/vectoragent/vectoragent_default.go +++ b/internal/vector/vectoragent/vectoragent_default.go @@ -17,10 +17,11 @@ limitations under the License. package vectoragent import ( - "github.com/kaasops/vector-operator/api/v1alpha1" corev1 "k8s.io/api/core/v1" resourcev1 "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/util/intstr" + + "github.com/kaasops/vector-operator/api/v1alpha1" ) func (ctrl *Controller) SetDefault() { diff --git a/internal/vector/vectoragent/vectoragent_service.go b/internal/vector/vectoragent/vectoragent_service.go index 0d18d526..4ba14672 100644 --- a/internal/vector/vectoragent/vectoragent_service.go +++ b/internal/vector/vectoragent/vectoragent_service.go @@ -17,9 +17,10 @@ limitations under the License. package vectoragent import ( - "github.com/kaasops/vector-operator/internal/config" corev1 "k8s.io/api/core/v1" + "github.com/kaasops/vector-operator/internal/config" + "k8s.io/apimachinery/pkg/util/intstr" ) From 30ddb9282638a914d4a3a3f1afca1b025e9e9a07 Mon Sep 17 00:00:00 2001 From: aa1ex <115736127+aa1ex@users.noreply.github.com> Date: Fri, 21 Nov 2025 16:02:05 +0200 Subject: [PATCH 305/316] feat: add e2e test framework and CI workflow (#206) * feat: add e2e test framework and CI workflow - Add comprehensive e2e test framework with Ginkgo/Gomega - Add GitHub Actions workflow for automated e2e testing - Add artifact collection for test failures - Add HTML report generation script - Add normal mode tests as baseline - Update Makefile with test-e2e, test-report, deploy-helm-e2e targets - Update Ginkgo to v2.20.2 and Gomega to v1.34.1 - Add CI/CD documentation * fix(makefile): add ginkgo dependency for test-e2e target - Add GINKGO binary to tool binaries section - Add GINKGO_VERSION (v2.20.2) to tool versions - Add ginkgo target to download ginkgo if necessary - Update test-e2e target to depend on ginkgo and use $(GINKGO) This fixes CI failure where ginkgo command was not found. * chore(ci): remove event-collector from e2e workflow Event collector is not used in current e2e tests. * fix(makefile): use absolute path for ginkgo binary --- .github/workflows/e2e-tests.yaml | 76 + .gitignore | 3 + Makefile | 84 +- docs/ci-cd.md | 174 + go.mod | 20 +- go.sum | 40 +- scripts/kind-config-ci.yaml | 18 + test/e2e/e2e_suite_test.go | 345 ++ test/e2e/e2e_test.go | 58 +- test/e2e/framework/README.md | 612 +++ test/e2e/framework/artifacts/README.md | 178 + test/e2e/framework/artifacts/collector.go | 621 +++ test/e2e/framework/artifacts/config.go | 137 + test/e2e/framework/artifacts/metadata.go | 118 + test/e2e/framework/artifacts/storage.go | 327 ++ test/e2e/framework/artifacts/storage_test.go | 55 + test/e2e/framework/assertions/matchers.go | 300 ++ test/e2e/framework/config/constants.go | 50 + test/e2e/framework/config/timeouts.go | 92 + test/e2e/framework/errors/errors.go | 100 + test/e2e/framework/framework.go | 1154 +++++ test/e2e/framework/kubectl/client.go | 485 ++ test/e2e/framework/kubectl/validation.go | 143 + test/e2e/framework/kubectl/wait.go | 111 + test/e2e/framework/lifecycle.go | 91 + test/e2e/framework/recorder/recorder.go | 258 + test/e2e/framework/resources.go | 36 + test/e2e/normal_mode_e2e_test.go | 222 + test/e2e/scripts/README.md | 40 + test/e2e/scripts/generate_report.py | 4554 +++++++++++++++++ test/e2e/testdata/normal-mode/agent.yaml | 7 + test/e2e/testdata/normal-mode/aggregator.yaml | 11 + .../normal-mode/cluster-pipeline-pod-ns1.yaml | 20 + .../normal-mode/cluster-pipeline-pod-ns2.yaml | 21 + .../normal-mode/cluster-pipeline.yaml | 24 + .../normal-mode/namespace-isolation-ns.yaml | 4 + .../namespace-isolation-pipeline.yaml | 17 + .../namespace-isolation-pod-isolated.yaml | 20 + .../namespace-isolation-pod-main.yaml | 19 + .../normal-mode/pipeline-aggregator-role.yaml | 28 + .../testdata/normal-mode/pipeline-basic.yaml | 16 + .../normal-mode/pipeline-complex.yaml | 34 + .../normal-mode/pipeline-deletable.yaml | 16 + .../normal-mode/pipeline-kubernetes-logs.yaml | 24 + .../normal-mode/pipeline-template.yaml | 16 + .../testdata/normal-mode/test-app-pod.yaml | 20 + test/e2e/testdata_helper.go | 17 + 47 files changed, 10736 insertions(+), 80 deletions(-) create mode 100644 .github/workflows/e2e-tests.yaml create mode 100644 docs/ci-cd.md create mode 100644 scripts/kind-config-ci.yaml create mode 100644 test/e2e/framework/README.md create mode 100644 test/e2e/framework/artifacts/README.md create mode 100644 test/e2e/framework/artifacts/collector.go create mode 100644 test/e2e/framework/artifacts/config.go create mode 100644 test/e2e/framework/artifacts/metadata.go create mode 100644 test/e2e/framework/artifacts/storage.go create mode 100644 test/e2e/framework/artifacts/storage_test.go create mode 100644 test/e2e/framework/assertions/matchers.go create mode 100644 test/e2e/framework/config/constants.go create mode 100644 test/e2e/framework/config/timeouts.go create mode 100644 test/e2e/framework/errors/errors.go create mode 100644 test/e2e/framework/framework.go create mode 100644 test/e2e/framework/kubectl/client.go create mode 100644 test/e2e/framework/kubectl/validation.go create mode 100644 test/e2e/framework/kubectl/wait.go create mode 100644 test/e2e/framework/lifecycle.go create mode 100644 test/e2e/framework/recorder/recorder.go create mode 100644 test/e2e/framework/resources.go create mode 100644 test/e2e/normal_mode_e2e_test.go create mode 100644 test/e2e/scripts/README.md create mode 100644 test/e2e/scripts/generate_report.py create mode 100644 test/e2e/testdata/normal-mode/agent.yaml create mode 100644 test/e2e/testdata/normal-mode/aggregator.yaml create mode 100644 test/e2e/testdata/normal-mode/cluster-pipeline-pod-ns1.yaml create mode 100644 test/e2e/testdata/normal-mode/cluster-pipeline-pod-ns2.yaml create mode 100644 test/e2e/testdata/normal-mode/cluster-pipeline.yaml create mode 100644 test/e2e/testdata/normal-mode/namespace-isolation-ns.yaml create mode 100644 test/e2e/testdata/normal-mode/namespace-isolation-pipeline.yaml create mode 100644 test/e2e/testdata/normal-mode/namespace-isolation-pod-isolated.yaml create mode 100644 test/e2e/testdata/normal-mode/namespace-isolation-pod-main.yaml create mode 100644 test/e2e/testdata/normal-mode/pipeline-aggregator-role.yaml create mode 100644 test/e2e/testdata/normal-mode/pipeline-basic.yaml create mode 100644 test/e2e/testdata/normal-mode/pipeline-complex.yaml create mode 100644 test/e2e/testdata/normal-mode/pipeline-deletable.yaml create mode 100644 test/e2e/testdata/normal-mode/pipeline-kubernetes-logs.yaml create mode 100644 test/e2e/testdata/normal-mode/pipeline-template.yaml create mode 100644 test/e2e/testdata/normal-mode/test-app-pod.yaml create mode 100644 test/e2e/testdata_helper.go diff --git a/.github/workflows/e2e-tests.yaml b/.github/workflows/e2e-tests.yaml new file mode 100644 index 00000000..cfc09b59 --- /dev/null +++ b/.github/workflows/e2e-tests.yaml @@ -0,0 +1,76 @@ +name: E2E Tests + +on: + push: + branches: + - main + - master + pull_request: + branches: + - main + - master + workflow_dispatch: # Allow manual trigger + +jobs: + e2e-tests: + name: Run E2E Tests + runs-on: ubuntu-latest + timeout-minutes: 30 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' + cache: true + + - name: Install dependencies + run: | + # Install kubebuilder for CRD generation + curl -L -o kubebuilder https://go.kubebuilder.io/dl/latest/$(go env GOOS)/$(go env GOARCH) + chmod +x kubebuilder && sudo mv kubebuilder /usr/local/bin/ + + - name: Create kind cluster + uses: helm/kind-action@v1 + with: + cluster_name: kind + config: scripts/kind-config-ci.yaml + wait: 300s + + - name: Verify cluster + run: | + kubectl cluster-info + kubectl get nodes + kubectl get pods -A + + - name: Build operator image + run: | + docker build -t example.com/vector-operator:v0.0.1 . + + - name: Load image into kind + run: | + kind load docker-image example.com/vector-operator:v0.0.1 --name kind + + - name: Run E2E tests + run: make test-e2e + env: + KUBECONFIG: /home/runner/.kube/config + + - name: Upload test results + if: always() + uses: actions/upload-artifact@v4 + with: + name: e2e-results-${{ github.run_number }} + path: test/e2e/results/ + retention-days: 7 + + - name: Publish test results + if: always() + uses: EnricoMi/publish-unit-test-result-action@v2 + with: + files: test/e2e/results/run-*/reports/junit-report.xml + check_name: E2E Test Results + comment_mode: off diff --git a/.gitignore b/.gitignore index 37afa32b..3e3a6cd5 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,6 @@ testbin/* __debug_bin vendor + +# E2E test results and artifacts +test/e2e/results/ diff --git a/Makefile b/Makefile index dbce874b..833ccf93 100644 --- a/Makefile +++ b/Makefile @@ -64,10 +64,79 @@ vet: ## Run go vet against code. test: manifests generate fmt vet envtest ## Run tests. KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out -# Utilize Kind or modify the e2e tests to load the image locally, enabling compatibility with other vendors. -.PHONY: test-e2e # Run the e2e tests against a Kind k8s instance that is spun up. -test-e2e: - go test ./test/e2e/ -v -ginkgo.v +# E2E test configuration +E2E_FAIL_FAST ?= false +E2E_RUN_DESCRIPTION ?= +E2E_LABEL_FILTER ?= +NAMESPACE ?= vector + +.PHONY: test-e2e # Run e2e tests with comprehensive reporting (JUnit XML + JSON + logs + artifacts) +test-e2e: ginkgo + @TIMESTAMP=$$(date +%Y-%m-%d-%H%M%S); \ + RUN_DIR="test/e2e/results/run-$$TIMESTAMP"; \ + echo "==> Running e2e tests..."; \ + echo "==> Results will be saved to: $$RUN_DIR"; \ + mkdir -p "$$RUN_DIR/reports"; \ + export E2E_ARTIFACTS_DIR="$$RUN_DIR/artifacts"; \ + export E2E_ARTIFACTS_ENABLED=true; \ + export E2E_GIT_COMMIT=$$(git rev-parse HEAD 2>/dev/null || echo "unknown"); \ + export E2E_GIT_BRANCH=$$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown"); \ + export E2E_GIT_DIRTY=$$(git diff --quiet 2>/dev/null || echo "dirty"; git diff --cached --quiet 2>/dev/null || echo "staged"); \ + export E2E_RUN_DESCRIPTION="$(E2E_RUN_DESCRIPTION)"; \ + echo "==> Git info: commit=$$E2E_GIT_COMMIT branch=$$E2E_GIT_BRANCH dirty=$$E2E_GIT_DIRTY"; \ + if [ -n "$$E2E_RUN_DESCRIPTION" ]; then \ + echo "==> Run description: $$E2E_RUN_DESCRIPTION"; \ + fi; \ + GINKGO_FLAGS="-v -timeout=30m"; \ + if [ "$(E2E_FAIL_FAST)" = "true" ]; then \ + echo "==> Fail-fast mode enabled (stop on first failure)"; \ + GINKGO_FLAGS="$$GINKGO_FLAGS --fail-fast"; \ + fi; \ + if [ -n "$(E2E_LABEL_FILTER)" ]; then \ + echo "==> Label filter: $(E2E_LABEL_FILTER)"; \ + GINKGO_FLAGS="$$GINKGO_FLAGS --label-filter=\"$(E2E_LABEL_FILTER)\""; \ + fi; \ + cd test/e2e && $(GINKGO) $$GINKGO_FLAGS \ + --junit-report="../../$$RUN_DIR/reports/junit-report.xml" \ + --json-report="../../$$RUN_DIR/reports/report.json" \ + | tee "../../$$RUN_DIR/reports/test-output.log"; \ + EXIT_CODE=$$?; \ + echo ""; \ + echo "==> Test run complete!"; \ + echo "==> All results in one place: $$RUN_DIR"; \ + echo " Reports:"; \ + echo " - JUnit XML: $$RUN_DIR/reports/junit-report.xml"; \ + echo " - JSON: $$RUN_DIR/reports/report.json"; \ + echo " - Logs: $$RUN_DIR/reports/test-output.log"; \ + if [ -d "$$RUN_DIR/artifacts" ] && [ "$$(find $$RUN_DIR/artifacts -mindepth 1 -maxdepth 1 2>/dev/null | wc -l)" -gt 1 ]; then \ + echo " Artifacts: $$RUN_DIR/artifacts/ (collected for failed tests)"; \ + else \ + echo " Artifacts: None (all tests passed)"; \ + fi; \ + echo ""; \ + echo "Quick commands:"; \ + echo " View summary: cat $$RUN_DIR/artifacts/metadata.json 2>/dev/null || echo 'All tests passed'"; \ + echo " View failures: grep -A 5 'FAILED' $$RUN_DIR/reports/test-output.log 2>/dev/null || echo 'No failures'"; \ + exit $$EXIT_CODE + +.PHONY: test-report +test-report: ## Generate interactive HTML report from e2e test results + @echo "==> Generating test report..." + @cd test/e2e/results && python3 ../scripts/generate_report.py + @echo "==> Report generated: test/e2e/results/test_results_report.html" + +.PHONY: deploy-helm-e2e +deploy-helm-e2e: manifests ## Deploy operator using Helm for e2e tests (use IMG and NAMESPACE variables) + @echo "==> Installing CRDs..." + $(KUBECTL) apply -f config/crd/bases + @echo "==> Creating namespace $(NAMESPACE)..." + $(KUBECTL) create namespace $(NAMESPACE) || true + @echo "==> Deploying operator via Helm to namespace $(NAMESPACE)..." + helm upgrade --install vector-operator ./helm/charts/vector-operator \ + --namespace $(NAMESPACE) \ + --set image.repository=$$(echo $(IMG) | cut -d: -f1) \ + --set image.tag=$$(echo $(IMG) | cut -d: -f2) \ + --wait --timeout 5m .PHONY: lint lint: golangci-lint ## Run golangci-lint linter @@ -160,12 +229,14 @@ KUSTOMIZE ?= $(LOCALBIN)/kustomize CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen ENVTEST ?= $(LOCALBIN)/setup-envtest GOLANGCI_LINT = $(LOCALBIN)/golangci-lint +GINKGO ?= $(LOCALBIN)/ginkgo ## Tool Versions KUSTOMIZE_VERSION ?= v5.4.3 CONTROLLER_TOOLS_VERSION ?= v0.16.1 ENVTEST_VERSION ?= release-0.19 GOLANGCI_LINT_VERSION ?= v1.64.8 +GINKGO_VERSION ?= v2.20.2 .PHONY: kustomize kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. @@ -187,6 +258,11 @@ golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary. $(GOLANGCI_LINT): $(LOCALBIN) $(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION)) +.PHONY: ginkgo +ginkgo: $(GINKGO) ## Download ginkgo locally if necessary. +$(GINKGO): $(LOCALBIN) + $(call go-install-tool,$(GINKGO),github.com/onsi/ginkgo/v2/ginkgo,$(GINKGO_VERSION)) + # go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist # $1 - target path with name of binary # $2 - package url which can be installed diff --git a/docs/ci-cd.md b/docs/ci-cd.md new file mode 100644 index 00000000..5a4e3dbb --- /dev/null +++ b/docs/ci-cd.md @@ -0,0 +1,174 @@ +# CI/CD Documentation + +## GitHub Actions Workflows + +### E2E Tests Workflow + +The E2E tests workflow automatically runs end-to-end tests for every pull request and push to the main branch. + +**Workflow File:** `.github/workflows/e2e-tests.yaml` + +#### Triggers + +- **Push to main/master**: Runs on every push to the main or master branch +- **Pull Requests**: Runs on PRs targeting main/master +- **Manual**: Can be triggered manually via GitHub Actions UI (workflow_dispatch) + +#### Workflow Steps + +1. **Checkout code**: Clones the repository +2. **Set up Go**: Installs Go using version from `go.mod` +3. **Install dependencies**: Installs kubebuilder for CRD generation +4. **Create kind cluster**: Creates a single-node Kubernetes cluster using `scripts/kind-config-ci.yaml` +5. **Verify cluster**: Checks cluster health and connectivity +6. **Build image**: Builds operator Docker image +7. **Load image**: Loads image into the kind cluster +8. **Run E2E tests**: Executes `make test-e2e` with JUnit reporting +9. **Upload test results**: Saves test results as artifacts (retained for 7 days) +10. **Publish test results**: Publishes JUnit results as GitHub check + +#### Configuration + +**Kind Cluster (CI):** `scripts/kind-config-ci.yaml` +- Single control-plane node +- Control-plane allows scheduling workloads for faster execution +- Port mappings for ingress (80, 443) + +#### Test Reports + +Test results are available in multiple formats: + +1. **JUnit XML**: `test/e2e/results/run-*/reports/junit-report.xml` + - Machine-readable format + - Used by GitHub Actions to display test results + +2. **JSON Report**: `test/e2e/results/run-*/reports/report.json` + - Detailed test execution data + - Suitable for programmatic analysis + +3. **Plain text log**: `test/e2e/results/run-*/reports/test-output.log` + - Human-readable test output + - Contains full test execution logs + +4. **HTML Report**: Generated via `make test-report` + - Interactive visualization + - Requires Python 3 + +#### Artifacts + +**Test Results** (7 days retention): +- JUnit XML report +- JSON report +- Plain text test output +- Failure artifacts (pod logs, events, resource states) +- Available for all workflow runs + +#### Viewing Results + +1. **GitHub UI**: + - Go to Actions tab → E2E Tests workflow + - Click on a specific run to view results + +2. **PR Checks**: + - Test results appear as a check on PRs + - Click "Details" to view full report + +#### Running E2E Tests Locally + +```bash +# Run e2e tests with full reporting +make test-e2e + +# Run with fail-fast (stop on first failure) +make test-e2e E2E_FAIL_FAST=true + +# Run with label filter +make test-e2e E2E_LABEL_FILTER="smoke" + +# Run with description +make test-e2e E2E_RUN_DESCRIPTION="Testing new feature" + +# Generate HTML report from results +make test-report +``` + +#### Troubleshooting + +**Tests fail in CI but pass locally:** +- Check timing issues (CI may be slower) +- Verify kind-config-ci.yaml configuration +- Check resource limits in CI environment + +**Cluster creation timeout:** +- Increase `wait` timeout in workflow +- Check Docker daemon health in CI +- Verify kind version compatibility + +**Image loading fails:** +- Ensure Docker build succeeds +- Check image names match between build and load steps +- Verify kind cluster name is correct + +**Tests timeout:** +- Default timeout is 30 minutes +- Adjust `timeout-minutes` in workflow if needed +- Check for hanging pods or resources + +#### Manual Trigger + +To manually trigger the E2E tests workflow: + +1. Go to Actions tab in GitHub +2. Select "E2E Tests" workflow +3. Click "Run workflow" button +4. Select branch and click "Run workflow" + +#### Performance + +**Typical execution time:** +- Cluster creation: ~1-2 minutes +- Image build: ~2-3 minutes +- Image load: ~30 seconds +- E2E tests: ~10-15 minutes +- **Total: ~15-20 minutes** + +### Lint Workflow + +**Workflow File:** `.github/workflows/lint.yaml` + +#### Jobs + +1. **golangci-lint**: Runs golangci-lint with project configuration +2. **go fmt**: Checks code formatting +3. **go vet**: Runs Go static analysis + +#### Configuration + +Linter configuration is defined in `.golangci.yml`: + +```yaml +linters: + enable: + - gosimple + - govet + - ineffassign + - staticcheck + - unused + - errcheck + - gofmt + - goimports + +linters-settings: + goimports: + local-prefixes: github.com/kaasops/vector-operator +``` + +#### Running Locally + +```bash +# Run linter +make lint + +# Run linter with auto-fix +make lint-fix +``` diff --git a/go.mod b/go.mod index 2e47d2db..c618e4a8 100644 --- a/go.mod +++ b/go.mod @@ -7,14 +7,14 @@ require ( github.com/fsnotify/fsnotify v1.7.0 github.com/go-logr/logr v1.4.2 github.com/mitchellh/mapstructure v1.5.0 - github.com/onsi/ginkgo/v2 v2.19.0 - github.com/onsi/gomega v1.33.1 + github.com/onsi/ginkgo/v2 v2.20.2 + github.com/onsi/gomega v1.34.1 github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.60.1 github.com/prometheus/client_golang v1.19.1 github.com/spf13/viper v1.19.0 github.com/stoewer/go-strcase v1.2.0 github.com/stretchr/testify v1.9.0 - golang.org/x/sync v0.7.0 + golang.org/x/sync v0.8.0 google.golang.org/grpc v1.65.0 google.golang.org/protobuf v1.34.2 gopkg.in/yaml.v2 v2.4.0 @@ -58,7 +58,7 @@ require ( github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af // indirect + github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 // indirect github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect @@ -109,14 +109,14 @@ require ( go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 // indirect - golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect - golang.org/x/net v0.26.0 // indirect + golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect + golang.org/x/net v0.28.0 // indirect golang.org/x/oauth2 v0.21.0 // indirect - golang.org/x/sys v0.21.0 // indirect - golang.org/x/term v0.21.0 // indirect - golang.org/x/text v0.16.0 // indirect + golang.org/x/sys v0.24.0 // indirect + golang.org/x/term v0.23.0 // indirect + golang.org/x/text v0.17.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + golang.org/x/tools v0.24.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect diff --git a/go.sum b/go.sum index f749aa74..c6e55c2d 100644 --- a/go.sum +++ b/go.sum @@ -191,8 +191,8 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM= -github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= +github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 h1:5iH8iuqE5apketRbSFBy+X1V0o+l+8NF1avt4HWl7cA= +github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -261,10 +261,10 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA= -github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= -github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= -github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= +github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4= +github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag= +github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= +github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -412,8 +412,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY= -golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -467,8 +467,8 @@ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -487,8 +487,8 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -528,19 +528,19 @@ golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= -golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= +golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= +golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -588,8 +588,8 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= +golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/scripts/kind-config-ci.yaml b/scripts/kind-config-ci.yaml new file mode 100644 index 00000000..8bd95f36 --- /dev/null +++ b/scripts/kind-config-ci.yaml @@ -0,0 +1,18 @@ +kind: Cluster +apiVersion: kind.x-k8s.io/v1alpha4 +nodes: + - role: control-plane + kubeadmConfigPatches: + - | + kind: InitConfiguration + nodeRegistration: + kubeletExtraArgs: + node-labels: "ingress-ready=true" + # Allow scheduling workloads on control-plane for faster CI + extraPortMappings: + - containerPort: 80 + hostPort: 80 + protocol: TCP + - containerPort: 443 + hostPort: 443 + protocol: TCP diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index ce823b49..3989de43 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -17,13 +17,358 @@ limitations under the License. package e2e import ( + "context" "fmt" + "os/exec" + "strings" "testing" + "time" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + + "github.com/kaasops/vector-operator/test/e2e/framework" + "github.com/kaasops/vector-operator/test/e2e/framework/artifacts" + "github.com/kaasops/vector-operator/test/utils" +) + +const ( + operatorNamespace = "vector-operator-system" + operatorImage = "example.com/vector-operator:v0.0.1" ) +// artifactCollector manages artifact collection for failed tests +var artifactCollector artifacts.Collector + +// readinessTestNamespace is created during controller readiness check +// and cleaned up in AfterSuite to avoid interfering with actual tests +var readinessTestNamespace string + +// SynchronizedBeforeSuite ensures setup runs only once across all parallel processes +var _ = SynchronizedBeforeSuite(func() []byte { + // This function runs ONLY on process #1 + By("building and loading operator image") + cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", operatorImage)) + _, err := utils.Run(cmd) + Expect(err).NotTo(HaveOccurred()) + + cmd = exec.Command("kind", "load", "docker-image", operatorImage, "--name", "kind") + _, err = utils.Run(cmd) + Expect(err).NotTo(HaveOccurred()) + + By("deploying operator via Helm") + cmd = exec.Command("make", "deploy-helm-e2e", + fmt.Sprintf("IMG=%s", operatorImage), + fmt.Sprintf("NAMESPACE=%s", operatorNamespace), + ) + _, err = utils.Run(cmd) + Expect(err).NotTo(HaveOccurred()) + + By("verifying operator is ready") + // Wait a bit for controllers to start watching CRs + cmd = exec.Command("kubectl", "wait", "--for=condition=ready", + "--timeout=60s", + "pod", "-l", "app.kubernetes.io/name=vector-operator", + "-n", operatorNamespace, + ) + _, err = utils.Run(cmd) + Expect(err).NotTo(HaveOccurred()) + + // Install shared dependencies once for all tests + framework.InstallSharedDependencies() + + By("verifying controllers are ready to process resources") + // Pod being Ready doesn't guarantee controllers are initialized (leader election, cache sync, etc.) + // Create a test VectorAggregator and verify the controller creates its deployment + // This ensures the VectorAggregator controller is fully operational before tests start + verifyControllersReady() + + // Initialize artifact collector + By("initializing artifact collector") + config := artifacts.LoadConfigFromEnv() + collector, err := artifacts.NewCollector(config) + Expect(err).NotTo(HaveOccurred()) + + runID := fmt.Sprintf("%d", time.Now().Unix()) + err = collector.Initialize(runID) + Expect(err).NotTo(HaveOccurred()) + + artifactCollector = collector + + return nil +}, func(data []byte) { + // This function runs on ALL processes after process #1 completes + + // Initialize artifact collector on all processes (skip if already initialized on process #1) + if artifactCollector == nil { + config := artifacts.LoadConfigFromEnv() + collector, err := artifacts.NewCollector(config) + if err == nil { + runID := fmt.Sprintf("%d", time.Now().Unix()) + _ = collector.Initialize(runID) + artifactCollector = collector + } + } +}) + +// ReportAfterEach collects artifacts for failed tests +var _ = ReportAfterEach(func(report SpecReport) { + // Skip if collector not initialized or artifacts disabled + if artifactCollector == nil { + return + } + + // Try to get framework from report entries first (preferred method) + // This is more reliable and works correctly with parallel tests + f := framework.FromReportEntries(report.ReportEntries) + + // Fallback to registry-based matching for backward compatibility + // This path will be deprecated once all tests use the new approach + if f == nil { + // Find framework for this test by matching namespace in ContainerHierarchyTexts + var matchedFrameworks []*framework.Framework + var matchScores []int + + containerTexts := report.ContainerHierarchyTexts + fullTestPath := strings.Join(containerTexts, " ") + " " + report.LeafNodeText + fullTestPathLower := strings.ToLower(fullTestPath) + + // Try to find framework by matching namespace patterns + // Priority: exact namespace match > timestamp suffix match > pattern match + framework.GetFrameworkRegistry().Range(func(key, value interface{}) bool { + namespace := key.(string) + fw := value.(*framework.Framework) + + score := 0 + + // Remove timestamp suffix for pattern matching + // e.g., "test-dataflow-1763129228782243000" -> "test-dataflow" + baseNamespace := namespace + if idx := strings.LastIndex(namespace, "-"); idx > 0 { + possibleBase := namespace[:idx] + // Check if suffix is a timestamp (all digits) + suffix := namespace[idx+1:] + isTimestamp := true + for _, c := range suffix { + if c < '0' || c > '9' { + isTimestamp = false + break + } + } + if isTimestamp && len(suffix) > 10 { // timestamps are long + baseNamespace = possibleBase + } + } + + // Extract pattern from namespace: "test-normal-mode" -> "normal mode" (with space) + // This matches Ginkgo's Describe("Normal Mode") to namespace test-normal-mode + namespacePattern := strings.TrimPrefix(baseNamespace, "test-") + namespacePattern = strings.ReplaceAll(namespacePattern, "-", " ") // "normal-mode" -> "normal mode" + + // Scoring: higher score = better match + // Exact namespace match in text - highest priority (1000 points) + if strings.Contains(fullTestPathLower, strings.ToLower(namespace)) { + score += 1000 + } + + // Base namespace match (without timestamp) - very high priority (500 points) + if baseNamespace != namespace && strings.Contains(fullTestPathLower, strings.ToLower(baseNamespace)) { + score += 500 + } + + // Pattern match: "test-normal-mode" matches "Normal Mode" (50 points) + if strings.Contains(fullTestPathLower, strings.ToLower(namespacePattern)) { + score += 50 + } + + // Check if namespace words appear in test path (5 points per word, reduced to avoid false positives) + namespaceWords := strings.Fields(namespacePattern) + for _, word := range namespaceWords { + // Only count meaningful words (skip common words) + if len(word) > 3 && strings.Contains(fullTestPathLower, strings.ToLower(word)) { + score += 5 + } + } + + if score > 0 { + matchedFrameworks = append(matchedFrameworks, fw) + matchScores = append(matchScores, score) + } + + return true // Continue searching all frameworks + }) + + // Select framework with highest match score + if len(matchedFrameworks) > 0 { + bestIdx := 0 + bestScore := matchScores[0] + for i := 1; i < len(matchScores); i++ { + if matchScores[i] > bestScore { + bestScore = matchScores[i] + bestIdx = i + } + } + f = matchedFrameworks[bestIdx] + + if report.Failed() { + fmt.Fprintf(GinkgoWriter, "🔍 Framework matched via registry (fallback) with score %d: namespace=%s\n", + bestScore, f.Namespace()) + } + } + } else { + // Successfully retrieved from report entries (preferred path) + if report.Failed() { + fmt.Fprintf(GinkgoWriter, "✓ Framework retrieved from report entries: namespace=%s\n", + f.Namespace()) + } + } + + if f == nil { + // No framework found - can't collect artifacts + if report.Failed() { + fmt.Fprintf(GinkgoWriter, "⚠️ Cannot collect artifacts: no framework found for test\n") + } + return + } + + // Log artifact collection for failed tests only + if report.Failed() { + fmt.Fprintf(GinkgoWriter, "📦 Collecting artifacts for FAILED test: %s (namespace: %s)\n", + report.LeafNodeText, f.Namespace()) + } + + // Build test info + testInfo := artifacts.TestInfo{ + Name: report.FullText(), + Namespace: f.Namespace(), + Failed: report.Failed(), + FailureMessage: report.FailureMessage(), + Duration: report.RunTime, + StartTime: report.StartTime, + EndTime: report.EndTime, + Labels: report.Labels(), + KubectlClient: f.Kubectl(), + } + + // Collect artifacts with timeout + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + if err := artifactCollector.CollectForTest(ctx, testInfo); err != nil { + fmt.Fprintf(GinkgoWriter, "⚠️ Failed to collect artifacts: %v\n", err) + } else { + if report.Failed() { + fmt.Fprintf(GinkgoWriter, "✓ Artifacts collected successfully\n") + } + } +}) + +// SynchronizedAfterSuite ensures cleanup runs only once across all parallel processes +var _ = SynchronizedAfterSuite(func() { + // This function runs on ALL processes + // Nothing needed here +}, func() { + // This function runs ONLY on process #1 after all others finish + + // Close artifact collector + if artifactCollector != nil { + if err := artifactCollector.Close(); err != nil { + fmt.Fprintf(GinkgoWriter, "⚠️ Failed to close artifact collector: %v\n", err) + } + } + + // Clean up readiness test namespace if it was created + // We defer this cleanup until after all tests to avoid controller overload + // during test execution (namespace deletion triggers cascading reconciliations) + if readinessTestNamespace != "" { + By("cleaning up controller readiness test namespace") + cmd := exec.Command("kubectl", "delete", "namespace", readinessTestNamespace, "--timeout=30s") + if _, err := utils.Run(cmd); err != nil { + fmt.Fprintf(GinkgoWriter, "⚠️ Failed to delete readiness test namespace %s: %v\n", readinessTestNamespace, err) + } + } + + // Uninstall shared dependencies + framework.UninstallSharedDependencies() + + By("undeploying operator via Helm") + cmd := exec.Command("make", "undeploy-helm-e2e", + fmt.Sprintf("NAMESPACE=%s", operatorNamespace), + ) + _, _ = utils.Run(cmd) +}) + +// verifyControllersReady ensures controllers are fully initialized by creating a test resource +// and verifying the controller processes it. This prevents flaky tests caused by controllers +// still initializing (leader election, cache sync, informers) when pod becomes Ready. +func verifyControllersReady() { + const ( + testNamespace = "controller-readiness-test" + testAggregator = "readiness-test-aggregator" + readinessTimeout = 60 * time.Second + pollInterval = 2 * time.Second + ) + + // Store namespace name for cleanup in AfterSuite + // We don't clean up immediately to avoid controller overload during test execution + // (namespace deletion triggers cascading reconciliations that can interfere with tests) + readinessTestNamespace = testNamespace + + // Create temporary namespace for readiness test (idempotent) + // First delete if exists and wait for full deletion + deleteCmd := exec.Command("kubectl", "delete", "namespace", testNamespace, "--ignore-not-found=true", "--wait=true", "--timeout=30s") + _ = deleteCmd.Run() // Ignore errors, namespace might not exist + + // Now create the namespace + cmd := exec.Command("kubectl", "create", "namespace", testNamespace) + if _, err := utils.Run(cmd); err != nil { + Fail(fmt.Sprintf("Failed to create readiness test namespace: %v", err)) + } + + // Create a minimal VectorAggregator CR + aggregatorYAML := fmt.Sprintf(`apiVersion: observability.kaasops.io/v1alpha1 +kind: VectorAggregator +metadata: + name: %s + namespace: %s +spec: + selector: {} + replicas: 1 + image: timberio/vector:0.40.0-alpine +`, testAggregator, testNamespace) + + cmd = exec.Command("kubectl", "apply", "-f", "-") + cmd.Stdin = strings.NewReader(aggregatorYAML) + if _, err := utils.Run(cmd); err != nil { + Fail(fmt.Sprintf("Failed to create test VectorAggregator: %v", err)) + } + + // Wait for controller to create the deployment + deploymentName := testAggregator + "-aggregator" + startTime := time.Now() + + Eventually(func() error { + cmd := exec.Command("kubectl", "get", "deployment", deploymentName, + "-n", testNamespace, "-o", "name") + output, err := utils.Run(cmd) + if err != nil { + return fmt.Errorf("deployment not found: %w", err) + } + if !strings.Contains(string(output), "deployment") { + return fmt.Errorf("deployment not found") + } + return nil + }, readinessTimeout, pollInterval).Should(Succeed(), + "VectorAggregator controller should create deployment %s in namespace %s within %v. "+ + "This indicates controller is not ready. Pod may be Ready but controllers are still initializing "+ + "(leader election, cache sync, webhook registration, informers startup).", + deploymentName, testNamespace, readinessTimeout) + + elapsed := time.Since(startTime) + fmt.Fprintf(GinkgoWriter, "✓ Controllers ready in %.2fs (deployment %s created)\n", + elapsed.Seconds(), deploymentName) +} + // Run e2e tests using the Ginkgo runner. func TestE2E(t *testing.T) { RegisterFailHandler(Fail) diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index 901f0ade..9dc7e4ea 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -30,63 +30,29 @@ import ( const namespace = "vector-operator-system" var _ = Describe("controller", Ordered, func() { - BeforeAll(func() { - By("installing prometheus operator") - Expect(utils.InstallPrometheusOperator()).To(Succeed()) - - By("installing the cert-manager") - Expect(utils.InstallCertManager()).To(Succeed()) + // NOTE: Dependencies (Prometheus Operator, cert-manager) are installed once + // in BeforeSuite via framework.InstallSharedDependencies() and shared across all tests. + // No need for BeforeAll/AfterAll here. - By("creating manager namespace") + BeforeAll(func() { + By("creating manager namespace (if not exists)") cmd := exec.Command("kubectl", "create", "ns", namespace) - _, _ = utils.Run(cmd) - }) - - AfterAll(func() { - By("uninstalling the Prometheus manager bundle") - utils.UninstallPrometheusOperator() - - By("uninstalling the cert-manager bundle") - utils.UninstallCertManager() - - By("removing manager namespace") - cmd := exec.Command("kubectl", "delete", "ns", namespace) - _, _ = utils.Run(cmd) + _, _ = utils.Run(cmd) // Ignore error if already exists }) Context("Operator", func() { It("should run successfully", func() { var controllerPodName string - var err error - - // projectimage stores the name of the image used in the example - var projectimage = "example.com/vector-operator:v0.0.1" - - By("building the manager(Operator) image") - cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", projectimage)) - _, err = utils.Run(cmd) - ExpectWithOffset(1, err).NotTo(HaveOccurred()) - By("loading the the manager(Operator) image on Kind") - err = utils.LoadImageToKindClusterWithName(projectimage) - ExpectWithOffset(1, err).NotTo(HaveOccurred()) - - By("installing CRDs") - cmd = exec.Command("make", "install") - _, err = utils.Run(cmd) - ExpectWithOffset(1, err).NotTo(HaveOccurred()) - - By("deploying the controller-manager") - cmd = exec.Command("make", "deploy", fmt.Sprintf("IMG=%s", projectimage)) - _, err = utils.Run(cmd) - ExpectWithOffset(1, err).NotTo(HaveOccurred()) + // NOTE: Operator is deployed once in BeforeSuite (e2e_suite_test.go) via Helm + // This test verifies that the already-deployed operator is running correctly By("validating that the controller-manager pod is running as expected") verifyControllerUp := func() error { - // Get pod name + // Get pod name (Helm deployment uses different labels) - cmd = exec.Command("kubectl", "get", - "pods", "-l", "control-plane=controller-manager", + cmd := exec.Command("kubectl", "get", + "pods", "-l", "app.kubernetes.io/name=vector-operator", "-o", "go-template={{ range .items }}"+ "{{ if not .metadata.deletionTimestamp }}"+ "{{ .metadata.name }}"+ @@ -101,7 +67,7 @@ var _ = Describe("controller", Ordered, func() { return fmt.Errorf("expect 1 controller pods running, but got %d", len(podNames)) } controllerPodName = podNames[0] - ExpectWithOffset(2, controllerPodName).Should(ContainSubstring("controller-manager")) + ExpectWithOffset(2, controllerPodName).Should(ContainSubstring("vector-operator")) // Validate pod status cmd = exec.Command("kubectl", "get", diff --git a/test/e2e/framework/README.md b/test/e2e/framework/README.md new file mode 100644 index 00000000..b40a9aaf --- /dev/null +++ b/test/e2e/framework/README.md @@ -0,0 +1,612 @@ +# E2E Test Framework + +A comprehensive testing framework for Vector Operator e2e tests, built on top of Ginkgo/Gomega. + +## Overview + +This framework provides a high-level API for writing maintainable and readable e2e tests. It handles common operations like namespace management, resource deployment, status checking, and cleanup, while providing custom matchers for intuitive assertions. + +## Key Features + +- **High-level API** - Simple methods for common operations +- **Automatic namespace management** - Creates and cleans up test namespaces +- **Shared dependencies** - Install Prometheus Operator and cert-manager once for all tests +- **Custom Gomega matchers** - Readable DSL-style assertions +- **Test metrics tracking** - Automatic timing measurements +- **YAML templating** - Dynamic test data generation +- **Centralized timeouts** - Consistent timeout configuration + +## Quick Start + +### Basic Test Structure + +```go +package e2e + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/kaasops/vector-operator/test/e2e/framework" + "github.com/kaasops/vector-operator/test/e2e/framework/assertions" + "github.com/kaasops/vector-operator/test/e2e/framework/config" +) + +var _ = Describe("My Feature", Label(config.LabelSmoke, config.LabelFast), Ordered, func() { + f := framework.NewFramework("my-feature-test") + + BeforeAll(f.Setup) + AfterAll(f.Teardown) + + Context("Basic Functionality", func() { + It("should work correctly", func() { + // Deploy resources + f.ApplyTestData("normal-mode/agent.yaml") + f.ApplyTestData("normal-mode/pipeline-basic.yaml") + + // Wait for readiness + f.WaitForPipelineValid("basic-pipeline") + + // Assert using custom matchers + Eventually(f.Pipeline("basic-pipeline")).Should(assertions.BeValid()) + }) + }) +}) +``` + +## Core Components + +### 1. Framework Object + +The main entry point for all test operations. + +```go +// Create a new framework instance +f := framework.NewFramework("test-namespace-prefix") + +// Setup creates namespace, initializes metrics, and registers framework +// for artifact collection via Ginkgo report entries +f.Setup() + +// Teardown cleans up namespace and resources +f.Teardown() +``` + +**Framework Registration** + +The framework uses Ginkgo's report entry system for context propagation instead of global state: + +```go +// In your test: +f := framework.NewFramework("test-ns") +f.Setup() // Automatically stores framework in Ginkgo report entries + +// In ReportAfterEach (for artifact collection): +// Framework is automatically retrieved from report entries +f := framework.FromReportEntries(report.ReportEntries) +if f != nil { + // Collect artifacts using framework's kubectl client and namespace +} +``` + +**Benefits:** +- ✅ No global state - eliminates race conditions in parallel tests +- ✅ Direct association between test and framework +- ✅ Works correctly with Ginkgo's parallel execution +- ✅ Backward compatible - still supports legacy registry-based matching as fallback + +**Context Support (Advanced)** + +For advanced use cases, the framework can be stored in Go contexts: + +```go +// Store in context +ctx := f.ToContext(context.Background()) + +// Retrieve from context +f := framework.FromContext(ctx) +if f != nil { + // Use framework +} +``` + +### 2. Resource Management + +#### Apply Test Data + +```go +// Load and apply YAML from test/e2e/testdata/ +f.ApplyTestData("normal-mode/agent.yaml") +f.ApplyTestData("normal-mode/pipeline-basic.yaml") +``` + +#### Create Multiple Resources + +```go +// Create 100 pipelines from template +creationTime := f.CreateMultiplePipelinesFromTemplate( + "scalability/pipeline-template.yaml", + "pipeline-NNNN", // Placeholder to replace + 100, // Count +) +fmt.Printf("Created 100 pipelines in %v\n", creationTime) +``` + +### 3. Wait Operations + +```go +// Wait for deployment to be ready (uses config.DeploymentReadyTimeout) +f.WaitForDeploymentReady("aggregator-name") + +// Wait for pipeline to become valid (uses config.PipelineValidTimeout) +f.WaitForPipelineValid("pipeline-name") +``` + +### 3.1. Log Polling Methods + +Standardized methods for waiting on log content, eliminating boilerplate Eventually blocks: + +```go +// Wait for substring to appear in pod logs +err := f.WaitForLogsContaining("pod-name", "expected text", 2*time.Minute) +Expect(err).NotTo(HaveOccurred()) + +// Wait for regex pattern to match in pod logs +err := f.WaitForLogsMatching("pod-name", `\d+ requests processed`, 1*time.Minute) +Expect(err).NotTo(HaveOccurred()) + +// Verify substring does NOT appear in logs (negative assertion) +err := f.AssertNoLogsContaining("pod-name", "ERROR", 30*time.Second) +Expect(err).NotTo(HaveOccurred()) + +// Get logs with options +logs, err := f.GetPodLogsWithOptions("pod-name", framework.LogOptions{ + TailLines: 100, +}) +Expect(err).NotTo(HaveOccurred()) +``` + +**Before (verbose):** +```go +var logs string +Eventually(func() bool { + l, err := f.GetPodLogs("pod-name") + if err != nil { + return false + } + logs = l + return strings.Contains(logs, "expected text") +}, 2*time.Minute, 1*time.Second).Should(BeTrue()) +``` + +**After (concise):** +```go +err := f.WaitForLogsContaining("pod-name", "expected text", 2*time.Minute) +Expect(err).NotTo(HaveOccurred()) +``` + +### 4. Status Queries + +```go +// Get pipeline status field +role := f.GetPipelineStatus("my-pipeline", "role") + +// Count valid pipelines +validCount, err := f.CountValidPipelines() + +// Count services with label +serviceCount := f.CountServicesWithLabel("app.kubernetes.io/component=Aggregator") +``` + +### 5. Custom Matchers + +The framework provides custom Gomega matchers for readable assertions: + +#### Pipeline Matchers + +```go +// Check if pipeline is valid +Eventually(f.Pipeline("test-pipeline")).Should(assertions.BeValid()) +Eventually(f.Pipeline("test-pipeline")).Should(assertions.BeInvalid()) + +// Check role +Expect(f.Pipeline("test-pipeline")).To(assertions.HaveRole("agent")) +Expect(f.Pipeline("test-pipeline")).To(assertions.HaveRole("aggregator")) + +// Check error message contains substring +Expect(f.Pipeline("invalid-pipeline")).To(assertions.HaveErrorContaining("validation")) +``` + +#### Service Matchers + +```go +// Check if service exists +Eventually(f.Service("my-service")).Should(assertions.Exist()) + +// Check service port +Expect(f.Service("my-service")).To(assertions.HavePort("9090")) +``` + +## Shared Dependencies + +Shared dependencies (Prometheus Operator, cert-manager) are installed once in `BeforeSuite` and shared across all tests. + +### Installation + +Handled automatically in `test/e2e/e2e_suite_test.go`: + +```go +var _ = BeforeSuite(func() { + // ... operator deployment + + // Install shared dependencies once + framework.InstallSharedDependencies() +}) +``` + +### Benefits + +- **Faster test execution** - ~3 minutes saved per test run +- **More stable** - Avoid repeated install/uninstall cycles +- **Cleaner logs** - No AlreadyExists errors + +### Usage in Tests + +Tests automatically use shared dependencies: + +```go +// No need to install/uninstall in individual tests +var _ = Describe("My Test", func() { + f := framework.NewFramework("test-ns") + + BeforeAll(f.Setup) // Just creates namespace + AfterAll(f.Teardown) // Just cleans up namespace + + // Dependencies are already available +}) +``` + +## Test Labels + +Ginkgo v2 provides a powerful label system for categorizing and filtering tests. Labels are simply strings that can be attached to test specs. + +### Standard Labels (defined in `config/constants.go`) + +```go +Label(config.LabelSmoke) // Quick smoke tests +Label(config.LabelFast) // Fast tests (<2 min) +Label(config.LabelSlow) // Slow tests (>5 min) +Label(config.LabelStress) // Stress/load tests +Label(config.LabelRegression) // Regression tests +``` + +### Priority Labels + +```go +Label(config.LabelP0) // P0: Critical, must always pass +Label(config.LabelP1) // P1: High priority +Label(config.LabelP2) // P2: Medium priority + +// Example usage: +var _ = Describe("Source Type Constraints [P0-Security]", + Label(config.LabelConstraint, config.LabelP0, config.LabelSecurity, config.LabelFast), func() { + // ... +}) +``` + +### Category Labels + +```go +Label(config.LabelSecurity) // Security-related tests +Label(config.LabelConstraint) // Constraint validation tests +``` + +### Combined Labels + +```go +// Multiple labels for fine-grained filtering +Label(config.LabelSmoke, config.LabelFast) // Quick smoke test +Label(config.LabelP0, config.LabelSecurity, config.LabelFast) // Critical security test +Label(config.LabelStress, config.LabelSlow) // Long-running load test +``` + +### Filtering Tests + +Run specific test categories: + +```bash +# Run only smoke tests +ginkgo --label-filter=smoke ./test/e2e/ + +# Run fast tests +ginkgo --label-filter=fast ./test/e2e/ + +# Exclude slow tests +ginkgo --label-filter="!slow" ./test/e2e/ + +# Run critical security tests +ginkgo --label-filter="p0 && security" ./test/e2e/ + +# Run smoke tests but exclude slow ones +ginkgo --label-filter="smoke && !slow" ./test/e2e/ + +# Run either constraint or security tests +ginkgo --label-filter="constraint || security" ./test/e2e/ +``` + +### Best Practices + +1. **Use descriptive labels**: Labels should clearly indicate what they categorize +2. **Combine standard + custom labels**: Mix project-standard labels with feature-specific ones +3. **Document critical labels**: If using priority labels (P0, P1), document their meaning +4. **Keep labels in test names**: Add labels to Describe text for better readability (e.g., `[P0-Security]`) + +### Available Labels + +List all labels in the test suite: +```bash +ginkgo labels ./test/e2e/ +``` + +## Test Metrics + +The framework automatically tracks test operation timing: + +```go +// Metrics are collected automatically +f.Setup() // Tracks setup time +f.WaitForDeploymentReady(...) // Tracks deployment wait time +f.WaitForPipelineValid(...) // Tracks pipeline validation time +f.Teardown() // Tracks cleanup time + +// Metrics are printed after each test +// Example output: +// 📊 Test Metrics: +// Setup: 60.777ms +// Deployment Wait: 4.299s +// Pipeline Validation: 5.098s +// Cleanup: 11.034s +// Total: 20.472s +``` + +## Environment Variables + +The framework supports several environment variables for customization: + +### E2E_TESTDATA_PATH + +Customize the location of test data files. Defaults to `test/e2e/testdata`. + +```bash +# Use custom test data directory +E2E_TESTDATA_PATH=/path/to/testdata make test-e2e + +# Run tests with test data in a different location +E2E_TESTDATA_PATH=/tmp/my-testdata ginkgo test/e2e/ +``` + +**Use cases:** +- Testing with different data sets +- CI/CD pipelines with mounted test data +- Temporary test data generation +- Isolated test environments + +### E2E_DRY_RUN + +Run tests in dry-run mode to generate test plans without executing them. + +```bash +E2E_DRY_RUN=true make test-e2e +``` + +### E2E_RECORD_STEPS + +Record test steps for debugging and reproducibility. + +```bash +E2E_RECORD_STEPS=true make test-e2e +``` + +## Timeouts Configuration + +Centralized timeout configuration in `config/timeouts.go`: + +```go +const ( + DeploymentCreateTimeout = 90 * time.Second // Wait for deployment to be created + DeploymentReadyTimeout = 120 * time.Second // Wait for deployment to be ready + PipelineValidTimeout = 2 * time.Minute // Wait for pipeline validation + ServiceCreateTimeout = 2 * time.Minute // Wait for service creation + DefaultPollInterval = 2 * time.Second // Default polling interval + SlowPollInterval = 5 * time.Second // Slower polling for heavy ops +) +``` + +## Advanced Examples + +### Example 1: Basic Pipeline Test + +```go +It("should create and validate a basic pipeline with agent", func() { + // Deploy resources + f.ApplyTestData("normal-mode/agent.yaml") + f.ApplyTestData("normal-mode/pipeline-basic.yaml") + + // Wait for readiness + f.WaitForPipelineValid("basic-pipeline") + + // Verify pipeline configuration + Eventually(f.Pipeline("basic-pipeline")).Should(assertions.BeValid()) + Expect(f.Pipeline("basic-pipeline")).To(assertions.HaveRole("agent")) + + // Verify agent processes the pipeline + Eventually(func() error { + return f.VerifyAgentHasPipeline("normal-agent", "basic-pipeline") + }, config.ServiceCreateTimeout, config.DefaultPollInterval).Should(Succeed()) +}) +``` + +### Example 2: Aggregator Test + +```go +It("should deploy aggregator and process pipelines", func() { + // Deploy aggregator + f.ApplyTestData("normal-mode/aggregator.yaml") + f.WaitForDeploymentReady("my-aggregator-aggregator") + + // Create pipeline with aggregator role + f.ApplyTestData("normal-mode/pipeline-aggregator-role.yaml") + f.WaitForPipelineValid("aggregator-pipeline") + + // Verify role + Expect(f.Pipeline("aggregator-pipeline")).To(assertions.HaveRole("aggregator")) +}) +``` + +### Example 3: Scalability Test + +```go +It("should handle 100 pipelines successfully", func() { + const pipelineCount = 100 + + // Deploy aggregator + f.ApplyTestData("scalability/aggregator.yaml") + f.WaitForDeploymentReady("scale-aggregator-aggregator") + + // Create 100 pipelines from template + creationTime := f.CreateMultiplePipelinesFromTemplate( + "scalability/pipeline-template.yaml", + "pipeline-NNNN", + pipelineCount, + ) + GinkgoWriter.Printf("✨ Created %d pipelines in %v\n", pipelineCount, creationTime) + + // Wait for all to become valid (with progress logging) + Eventually(func() (int, error) { + validCount, err := f.CountValidPipelines() + if validCount > 0 { + GinkgoWriter.Printf("📊 Validation progress: %d/%d pipelines valid (%.0f%%)\n", + validCount, pipelineCount, float64(validCount)/float64(pipelineCount)*100) + } + return validCount, nil + }, 7*time.Minute, 10*time.Second).Should(Equal(pipelineCount)) +}) +``` + +## Best Practices + +### 1. Use Descriptive Test Names + +```go +// Good +It("should create and validate a basic pipeline with agent", func() { ... }) + +// Bad +It("test1", func() { ... }) +``` + +### 2. Use Eventually for Async Operations + +```go +// Good - waits for condition to be met +Eventually(f.Pipeline("test-pipeline")).Should(assertions.BeValid()) + +// Bad - may fail if not ready immediately +Expect(f.Pipeline("test-pipeline")).To(assertions.BeValid()) +``` + +### 3. Use Appropriate Labels + +```go +// Mark fast smoke tests +var _ = Describe("Quick Validation", Label(config.LabelSmoke, config.LabelFast), ...) + +// Mark slow stress tests +var _ = Describe("Load Test", Label(config.LabelStress, config.LabelSlow), ...) +``` + +### 4. Leverage Test Metrics + +```go +// Metrics are automatically tracked and displayed +BeforeAll(f.Setup) // Tracks setup time +AfterAll(f.Teardown) // Tracks cleanup time + displays all metrics +``` + +### 5. Use Custom Matchers + +```go +// Good - readable and clear intent +Expect(f.Pipeline("test")).To(assertions.BeValid()) +Expect(f.Pipeline("test")).To(assertions.HaveRole("agent")) + +// Bad - verbose and less clear +status := f.GetPipelineStatus("test", "configCheckResult") +Expect(status).To(Equal("true")) +role := f.GetPipelineStatus("test", "role") +Expect(role).To(Equal("agent")) +``` + +## Directory Structure + +``` +test/e2e/framework/ +├── README.md # This file +├── framework.go # Main framework implementation +├── lifecycle.go # Shared dependencies management +├── resources.go # Resource utilities +├── config/ +│ ├── constants.go # Test labels and constants +│ └── timeouts.go # Timeout configuration +├── kubectl/ +│ ├── client.go # Kubectl wrapper +│ ├── wait.go # Wait utilities +│ └── validation.go # Validation helpers +├── assertions/ +│ └── matchers.go # Custom Gomega matchers +├── artifacts/ +│ ├── collector.go # Artifact collection +│ ├── storage.go # Artifact storage +│ └── config.go # Artifact configuration +├── errors/ +│ └── errors.go # Custom error types +└── recorder/ + └── recorder.go # Step recorder +``` + +## Contributing + +When adding new features to the framework: + +1. Keep the API simple and intuitive +2. Add appropriate error handling +3. Track timing metrics for new operations +4. Add custom matchers for common assertions +5. Update this README with examples + +## Troubleshooting + +### AlreadyExists Errors + +If you see `AlreadyExists` errors for Prometheus Operator or cert-manager: +- Ensure you're not installing dependencies in `BeforeAll` +- Dependencies are automatically installed in `BeforeSuite` via `framework.InstallSharedDependencies()` + +### Timeout Errors + +If tests timeout: +- Check `config/timeouts.go` and adjust as needed +- Use `SlowPollInterval` for expensive operations +- Consider increasing go test timeout: `-timeout=15m` + +### Namespace Not Found + +If you see namespace errors: +- Ensure `BeforeAll(f.Setup)` is called +- Verify namespace name matches test data YAML files + +## References + +- [Ginkgo Documentation](https://onsi.github.io/ginkgo/) +- [Gomega Matchers](https://onsi.github.io/gomega/) +- [Vector Operator E2E Tests](../README.md) diff --git a/test/e2e/framework/artifacts/README.md b/test/e2e/framework/artifacts/README.md new file mode 100644 index 00000000..97b774d5 --- /dev/null +++ b/test/e2e/framework/artifacts/README.md @@ -0,0 +1,178 @@ +# E2E Test Artifact Collection + +Automatic collection of debugging artifacts when e2e tests fail. + +## Features + +- **Automatic**: Collects artifacts only on test failures (configurable) +- **Safe**: Never fails tests due to collection errors +- **Fast**: < 1s overhead for passing tests, < 30s for failing tests +- **Comprehensive**: Logs, pod status, events, resources + +## Configuration (ENV Variables) + +All configuration is ENV-based with sensible defaults: + +### Collection Control +- `E2E_ARTIFACTS_ENABLED` (default: `true`) - Master switch +- `E2E_ARTIFACTS_ON_FAILURE_ONLY` (default: `true`) - Only collect on failures +- `E2E_ARTIFACTS_MINIMAL_ONLY` (default: `false`) - P0 artifacts only + +### Storage +- `E2E_ARTIFACTS_DIR` (default: `test/e2e/artifacts`) - Base directory + +### Size Limits +- `E2E_ARTIFACTS_MAX_LOG_LINES` (default: `500`) - Max log lines per pod +- `E2E_ARTIFACTS_MAX_RESOURCE_SIZE` (default: `10485760`) - Max 10MB per file +- `E2E_ARTIFACTS_MAX_TOTAL_SIZE` (default: `104857600`) - Max 100MB per test + +### Timeouts +- `E2E_ARTIFACTS_TIMEOUT` (default: `30s`) - Max collection time + +## Usage + +### Running Tests with Artifacts + +```bash +# Default behavior (enabled, on-failure-only) +make test-e2e + +# Disable artifacts +E2E_ARTIFACTS_ENABLED=false make test-e2e + +# Collect for all tests (even passing) +E2E_ARTIFACTS_ON_FAILURE_ONLY=false make test-e2e + +# Increase log lines +E2E_ARTIFACTS_MAX_LOG_LINES=1000 make test-e2e +``` + +### Artifact Location + +Artifacts are stored in: +``` +test/e2e/artifacts/ +└── run-{timestamp}/ + ├── metadata.json + └── {test-name}/ + ├── metadata.json + ├── logs/ + │ ├── operator-controller.log + │ └── pod-{name}.log + ├── pods/ + │ └── {pod-name}-status.json + ├── resources/ + │ ├── vectorpipeline-{name}-status.json + │ └── deployment-{name}.yaml + └── events/ + └── namespace-events.txt +``` + +### Unified Test Results + +When you run `make test-e2e`, all results are automatically saved in a unified structure with reports and artifacts correlated by timestamp: + +```bash +# Run tests - results automatically saved with timestamp +make test-e2e + +# Results structure: +test/e2e/results/run-{timestamp}/ +├── reports/ +│ ├── junit-report.xml # JUnit XML for CI integration +│ ├── report.json # Ginkgo JSON report +│ └── test-output.log # Full test output logs +└── artifacts/ # Debug artifacts (only for failed tests) + ├── metadata.json # Run-level metadata + └── {test-name}/ # Per-test artifacts + ├── metadata.json + ├── logs/ + ├── pods/ + ├── resources/ + └── events/ +``` + +**Benefits**: +- Single runID correlates all reports and artifacts +- Easy to navigate - everything in one directory +- CI/CD friendly - upload one directory +- Helpful output with quick analysis commands + +### CI Integration (GitHub Actions) + +```yaml +- name: Run E2E Tests + run: make test-e2e + +- name: Upload Test Results + if: always() # Upload even if tests fail + uses: actions/upload-artifact@v4 + with: + name: e2e-results-${{ github.run_number }} + path: test/e2e/results/ + retention-days: 30 +``` + +## Collected Artifacts (P0 - MVP) + +### Critical for Debugging +1. **Pod Status JSON** - Conditions, restarts, phase +2. **Operator Controller Logs** - Time-filtered logs (test duration + 1min buffer) +3. **VectorPipeline CR Status** - Validation results +4. **Namespace Events** - What happened in test namespace +5. **Resource Metadata** - Deployments, DaemonSets, Services + +### Future (Phase 2) +- Full pod logs (all containers) +- Full pod descriptions +- Vector agent/aggregator logs +- ConfigCheck pod logs +- Timeline reconstruction + +## Architecture + +- **Thread-safe**: Uses `sync.Map` for parallel test support +- **Graceful degradation**: Collection errors don't fail tests +- **Size limits**: Prevents CI artifact bloat +- **Atomic writes**: Temp file + rename for reliability + +## Performance + +- **Passing tests**: < 1s overhead (if `ON_FAILURE_ONLY=true`) +- **Failing tests**: < 30s collection time +- **Storage**: < 100MB per test, < 500MB per run + +## Troubleshooting + +### No artifacts collected +1. Check `E2E_ARTIFACTS_ENABLED=true` +2. Verify test is using `framework.NewFramework()` or `framework.Shared()` +3. Check GinkgoWriter output for warning messages + +### Artifacts too large +1. Reduce `E2E_ARTIFACTS_MAX_LOG_LINES` (default: 500) +2. Enable `E2E_ARTIFACTS_MINIMAL_ONLY=true` +3. Check individual file sizes with `E2E_ARTIFACTS_MAX_RESOURCE_SIZE` + +### Collection timeout +1. Increase `E2E_ARTIFACTS_TIMEOUT` (default: 30s) +2. Check kubectl connectivity +3. Review namespace resource count + +## Important Bug Fixes + +### Time-based Log Collection (Fixed) +**Problem**: Previously, operator logs were collected using `kubectl logs --tail 500`, which retrieved the last 500 lines from the entire pod lifetime. In long-running test suites (e.g., full e2e runs lasting 15+ minutes), the operator pod could generate thousands of log lines, causing the last 500 lines to exclude logs from earlier failing tests. + +**Example**: A test failing at 18:05-18:07 would collect operator logs from 16:02-16:03 (the pod's startup logs), completely missing the relevant reconciliation attempts. + +**Solution**: Implemented time-based log collection using `kubectl logs --since-time` with the test's start time (+ 1 minute buffer). This ensures operator logs are collected only for the relevant time period, regardless of how long the pod has been running. + +**Impact**: +- Fixes flaky test debugging where operator logs were missing +- Enables reliable root cause analysis for race conditions +- Reduces confusion when logs don't match test timeline + +## Development + +See architect design document for Phase 2+ enhancements. diff --git a/test/e2e/framework/artifacts/collector.go b/test/e2e/framework/artifacts/collector.go new file mode 100644 index 00000000..0be6e612 --- /dev/null +++ b/test/e2e/framework/artifacts/collector.go @@ -0,0 +1,621 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package artifacts + +import ( + "context" + "fmt" + "os" + "os/exec" + "strings" + "time" + + . "github.com/onsi/ginkgo/v2" //nolint:golint,revive + + "github.com/kaasops/vector-operator/test/e2e/framework/kubectl" +) + +// TestInfo contains information about a test execution +type TestInfo struct { + Name string + Namespace string + Failed bool + FailureMessage string + Duration time.Duration + StartTime time.Time + EndTime time.Time + Labels []string + + // Test sequence tracking (for degradation analysis) + SequenceNumber int // Which test in the run (1, 2, 3...) + OperatorAge time.Duration // How long operator has been running + + // Kubernetes context + KubectlClient *kubectl.Client +} + +// Collector manages artifact collection for e2e tests +type Collector interface { + // Initialize sets up the collector for a test run + Initialize(runID string) error + + // CollectForTest collects artifacts for a test + CollectForTest(ctx context.Context, testInfo TestInfo) error + + // Close finalizes the collector and writes summary + Close() error +} + +// collector implements the Collector interface +type collector struct { + config Config + storage *Storage + metadata *MetadataBuilder + runStart time.Time + + // Operator tracking (for degradation analysis) + operatorStartTime time.Time + + // Statistics + totalTests int + failedTests int + testCounter int // Counter for directory naming +} + +// NewCollector creates a new artifact collector +func NewCollector(config Config) (Collector, error) { + return &collector{ + config: config, + runStart: time.Now(), + }, nil +} + +// Initialize sets up the collector for a test run +func (c *collector) Initialize(runID string) error { + if !c.config.Enabled { + return nil + } + + // Create storage + storage, err := NewStorage(c.config.BaseDir, runID, c.config.MaxResourceSize) + if err != nil { + return fmt.Errorf("failed to create storage: %w", err) + } + c.storage = storage + + // Create metadata builder + c.metadata = NewMetadataBuilder(storage) + + // Get operator start time for degradation tracking + c.operatorStartTime = c.getOperatorStartTime() + + fmt.Fprintf(GinkgoWriter, "📦 Artifact collection initialized: %s\n", storage.GetRunDir()) + return nil +} + +// CollectForTest collects artifacts for a specific test +func (c *collector) CollectForTest(ctx context.Context, testInfo TestInfo) error { + if !c.config.Enabled { + return nil + } + + // Update statistics + c.totalTests++ + if testInfo.Failed { + c.failedTests++ + } + + // Skip passed tests if configured + if !testInfo.Failed && c.config.CollectOnFailureOnly { + return nil + } + + // Increment counter and create short directory name + c.testCounter++ + shortName := createShortTestName(testInfo.Name, c.testCounter) + + // Fill in tracking fields for degradation analysis + testInfo.SequenceNumber = c.totalTests + if !c.operatorStartTime.IsZero() { + testInfo.OperatorAge = time.Since(c.operatorStartTime) + } + + // Create test directory + testDir, err := c.storage.CreateTestDir(shortName) + if err != nil { + return fmt.Errorf("failed to create test directory: %w", err) + } + + fmt.Fprintf(GinkgoWriter, "📦 Collecting artifacts for test: %s\n", testInfo.Name) + collectionStart := time.Now() + + // Collect with timeout + ctx, cancel := context.WithTimeout(ctx, c.config.CollectionTimeout) + defer cancel() + + // Track collected artifacts + inventory := ArtifactInventory{ + LogFiles: []string{}, + ResourceFiles: []string{}, + EventFiles: []string{}, + } + + // P0 artifacts - critical for debugging + if err := c.collectP0Artifacts(ctx, testInfo, testDir, &inventory); err != nil { + fmt.Fprintf(GinkgoWriter, "⚠️ Warning: P0 artifact collection had errors: %v\n", err) + } + + // Write test metadata + collectionDuration := time.Since(collectionStart) + inventory.CollectionTime = collectionDuration.String() + + meta := BuildTestMetadata(testInfo, inventory) + if err := c.metadata.WriteTestMetadata(meta, testDir); err != nil { + fmt.Fprintf(GinkgoWriter, "⚠️ Warning: Failed to write test metadata: %v\n", err) + } + + fmt.Fprintf(GinkgoWriter, "✅ Artifacts collected in %v (%d files)\n", + collectionDuration, len(inventory.LogFiles)+len(inventory.ResourceFiles)+len(inventory.EventFiles)) + + return nil +} + +// collectP0Artifacts collects P0 (critical) artifacts +func (c *collector) collectP0Artifacts(ctx context.Context, testInfo TestInfo, testDir string, inventory *ArtifactInventory) error { + kubectl := testInfo.KubectlClient + namespace := testInfo.Namespace + + if kubectl == nil || namespace == "" { + return fmt.Errorf("missing kubectl client or namespace") + } + + // 1. Pod status (JSON) - fast, critical + if err := c.collectPodStatus(ctx, kubectl, namespace, testDir, inventory); err != nil { + fmt.Fprintf(GinkgoWriter, "⚠️ Failed to collect pod status: %v\n", err) + } + + // 2. Operator controller logs - critical for debugging + if err := c.collectOperatorLogs(ctx, testDir, inventory, testInfo.StartTime); err != nil { + fmt.Fprintf(GinkgoWriter, "⚠️ Failed to collect operator logs: %v\n", err) + } + + // 2a. Operator health (pod describe, events) - critical for degradation diagnosis + if err := c.collectOperatorHealth(ctx, testDir, inventory, testInfo.StartTime); err != nil { + fmt.Fprintf(GinkgoWriter, "⚠️ Failed to collect operator health: %v\n", err) + } + + // 3. Pipeline status - fast, shows validation state + if err := c.collectPipelineStatus(ctx, kubectl, namespace, testDir, inventory); err != nil { + fmt.Fprintf(GinkgoWriter, "⚠️ Failed to collect pipeline status: %v\n", err) + } + + // 4. Namespace events - fast, shows what happened + if err := c.collectEvents(ctx, kubectl, namespace, testDir, inventory); err != nil { + fmt.Fprintf(GinkgoWriter, "⚠️ Failed to collect namespace events: %v\n", err) + } + + // 5. Resource metadata (Deployment/DaemonSet/Service basic info) + if err := c.collectResourceMetadata(ctx, kubectl, namespace, testDir, inventory); err != nil { + fmt.Fprintf(GinkgoWriter, "⚠️ Failed to collect resource metadata: %v\n", err) + } + + return nil +} + +// collectPodStatus collects status of all pods in the namespace +func (c *collector) collectPodStatus(ctx context.Context, kubectl *kubectl.Client, namespace, testDir string, inventory *ArtifactInventory) error { + // Get all pods + pods, err := kubectl.GetPodsByLabel("") + if err != nil { + return fmt.Errorf("failed to get pods: %w", err) + } + + inventory.PodCount = len(pods) + + for _, podName := range pods { + // Get pod status as JSON + output, err := kubectl.GetWithJsonPath("pod", podName, ".status") + if err != nil { + fmt.Fprintf(GinkgoWriter, " ⚠️ Failed to get status for pod %s: %v\n", podName, err) + continue + } + + filename := fmt.Sprintf("%s-status.json", podName) + if err := c.storage.WriteFile(testDir, "pods", filename, []byte(output)); err != nil { + fmt.Fprintf(GinkgoWriter, " ⚠️ Failed to write pod status for %s: %v\n", podName, err) + continue + } + + inventory.ResourceFiles = append(inventory.ResourceFiles, "pods/"+filename) + + // Also get pod logs (last N lines) + logs, err := kubectl.GetPodLogsTail(podName, c.config.MaxLogLines) + if err != nil { + // Pod might not have logs yet, that's okay + continue + } + + // Truncate logs if needed + truncatedLogs := TruncateLogLines([]byte(logs), c.config.MaxLogLines) + + logFilename := fmt.Sprintf("%s.log", podName) + if err := c.storage.WriteFile(testDir, "logs", logFilename, truncatedLogs); err != nil { + fmt.Fprintf(GinkgoWriter, " ⚠️ Failed to write logs for %s: %v\n", podName, err) + continue + } + + inventory.LogFiles = append(inventory.LogFiles, "logs/"+logFilename) + } + + return nil +} + +// collectOperatorLogs collects operator controller logs +func (c *collector) collectOperatorLogs(ctx context.Context, testDir string, inventory *ArtifactInventory, testStart time.Time) error { + // Get operator pod name from vector-operator-system namespace + operatorNs := "vector-operator-system" + operatorClient := kubectl.NewClient(operatorNs) + + pods, err := operatorClient.GetPodsByLabel("app.kubernetes.io/name=vector-operator") + if err != nil || len(pods) == 0 { + return fmt.Errorf("failed to find operator controller pod: %w", err) + } + + // Get logs from first controller pod (should only be one) + podName := pods[0] + + // Add 1 minute buffer before test start to capture context + // This helps see what was happening just before the test started + logsSince := testStart.Add(-1 * time.Minute) + + // Use time-based log collection to get logs relevant to this test + // This fixes the issue where long-running operator pods would only return + // the last N lines from the entire pod lifetime, missing test-specific logs + logs, err := operatorClient.GetPodLogsSinceTime(podName, logsSince, c.config.MaxLogLines) + if err != nil { + return fmt.Errorf("failed to get operator logs: %w", err) + } + + // Truncate logs if still needed + truncatedLogs := TruncateLogLines([]byte(logs), c.config.MaxLogLines) + + if err := c.storage.WriteFile(testDir, "logs", "operator-controller.log", truncatedLogs); err != nil { + return fmt.Errorf("failed to write operator logs: %w", err) + } + + inventory.LogFiles = append(inventory.LogFiles, "logs/operator-controller.log") + return nil +} + +// collectOperatorHealth collects operator pod describe and events for degradation diagnosis +func (c *collector) collectOperatorHealth(ctx context.Context, testDir string, inventory *ArtifactInventory, testStart time.Time) error { + const operatorNs = "vector-operator-system" + operatorClient := kubectl.NewClient(operatorNs) + + // Get operator pod + pods, err := operatorClient.GetPodsByLabel("app.kubernetes.io/name=vector-operator") + if err != nil || len(pods) == 0 { + return fmt.Errorf("failed to find operator pod: %w", err) + } + podName := pods[0] + + // 1. Get pod describe (shows conditions, events, restarts, QoS, resource requests/limits) + describeCmd := exec.Command("kubectl", "describe", "pod", podName, "-n", operatorNs) + describeOutput, err := describeCmd.CombinedOutput() + if err == nil { + if err := c.storage.WriteFile(testDir, "operator", "pod-describe.txt", describeOutput); err != nil { + return fmt.Errorf("failed to write operator pod describe: %w", err) + } + inventory.ResourceFiles = append(inventory.ResourceFiles, "operator/pod-describe.txt") + } + + // 2. Get cluster-wide events related to operator (evictions, OOMKills, etc.) + // Use time window from 2 minutes before test start to catch context + sinceTime := testStart.Add(-2 * time.Minute).UTC().Format(time.RFC3339) + + // Get all Warning events in operator namespace + eventsCmd := exec.Command("kubectl", "get", "events", "-n", operatorNs, + "--field-selector", "type=Warning", + "--since-time", sinceTime) + eventsOutput, err := eventsCmd.CombinedOutput() + if err == nil && len(eventsOutput) > 0 { + if err := c.storage.WriteFile(testDir, "operator", "warning-events.txt", eventsOutput); err != nil { + return fmt.Errorf("failed to write operator warning events: %w", err) + } + inventory.EventFiles = append(inventory.EventFiles, "operator/warning-events.txt") + } + + // 3. Get deployment describe (shows replica status, conditions) + deployDescribeCmd := exec.Command("kubectl", "describe", "deployment", "vector-operator-controller-manager", "-n", operatorNs) + deployDescribeOutput, err := deployDescribeCmd.CombinedOutput() + if err == nil { + if err := c.storage.WriteFile(testDir, "operator", "deployment-describe.txt", deployDescribeOutput); err != nil { + return fmt.Errorf("failed to write deployment describe: %w", err) + } + inventory.ResourceFiles = append(inventory.ResourceFiles, "operator/deployment-describe.txt") + } + + // 4. Collect pprof profiles (goroutine, heap) for memory/goroutine leak diagnosis + if err := c.collectPprofProfiles(ctx, testDir, inventory, podName, operatorNs); err != nil { + // Non-fatal: pprof may not be enabled in production + fmt.Fprintf(GinkgoWriter, "⚠️ Failed to collect pprof profiles (may not be enabled): %v\n", err) + } + + return nil +} + +// collectPipelineStatus collects VectorPipeline CR status +func (c *collector) collectPipelineStatus(ctx context.Context, kubectl *kubectl.Client, namespace, testDir string, inventory *ArtifactInventory) error { + // Get all VectorPipeline CRs + pipelinesOutput, err := kubectl.GetAll("vectorpipeline", "") + if err != nil { + return fmt.Errorf("failed to list pipelines: %w", err) + } + + if pipelinesOutput == "" { + // No pipelines, that's okay + return nil + } + + pipelines := strings.Fields(pipelinesOutput) + for _, pipelineName := range pipelines { + // Get pipeline status + status, err := kubectl.GetWithJsonPath("vectorpipeline", pipelineName, ".status") + if err != nil { + fmt.Fprintf(GinkgoWriter, " ⚠️ Failed to get status for pipeline %s: %v\n", pipelineName, err) + continue + } + + filename := fmt.Sprintf("vectorpipeline-%s-status.json", pipelineName) + if err := c.storage.WriteFile(testDir, "resources", filename, []byte(status)); err != nil { + fmt.Fprintf(GinkgoWriter, " ⚠️ Failed to write pipeline status for %s: %v\n", pipelineName, err) + continue + } + + inventory.ResourceFiles = append(inventory.ResourceFiles, "resources/"+filename) + } + + return nil +} + +// collectEvents collects Kubernetes events from the namespace +func (c *collector) collectEvents(ctx context.Context, kubectl *kubectl.Client, namespace, testDir string, inventory *ArtifactInventory) error { + // Get events - use kubectl.Client to run kubectl get events + // Since we don't have a GetEvents method, we'll use a simple approach + eventsOutput, err := kubectl.Get("events", "") + if err != nil { + // Events might not exist, that's okay + return nil + } + + if err := c.storage.WriteFile(testDir, "events", "namespace-events.txt", eventsOutput); err != nil { + return fmt.Errorf("failed to write namespace events: %w", err) + } + + inventory.EventFiles = append(inventory.EventFiles, "events/namespace-events.txt") + return nil +} + +// collectResourceMetadata collects basic metadata about Deployments, DaemonSets, Services +func (c *collector) collectResourceMetadata(ctx context.Context, kubectl *kubectl.Client, namespace, testDir string, inventory *ArtifactInventory) error { + resourceTypes := []string{"deployment", "daemonset", "service"} + + for _, resourceType := range resourceTypes { + resources, err := kubectl.GetAll(resourceType, "") + if err != nil { + continue // Resource type might not exist + } + + if resources == "" { + continue + } + + resourceNames := strings.Fields(resources) + for _, resourceName := range resourceNames { + // Get resource metadata (name, labels, status) + output, err := kubectl.Get(resourceType, resourceName) + if err != nil { + continue + } + + filename := fmt.Sprintf("%s-%s.yaml", resourceType, resourceName) + if err := c.storage.WriteFile(testDir, "resources", filename, output); err != nil { + fmt.Fprintf(GinkgoWriter, " ⚠️ Failed to write %s/%s: %v\n", resourceType, resourceName, err) + continue + } + + inventory.ResourceFiles = append(inventory.ResourceFiles, "resources/"+filename) + } + } + + return nil +} + +// Close finalizes the collector and writes run summary +func (c *collector) Close() error { + if !c.config.Enabled || c.storage == nil { + return nil + } + + runEnd := time.Now() + runMeta := RunMetadata{ + RunID: c.storage.GetRunID(), + StartTime: c.runStart, + EndTime: runEnd, + TotalTests: c.totalTests, + FailedTests: c.failedTests, + PassedTests: c.totalTests - c.failedTests, + ArtifactsDir: c.storage.GetRunDir(), + Environment: map[string]string{ + "E2E_ARTIFACTS_ENABLED": fmt.Sprintf("%t", c.config.Enabled), + "E2E_ARTIFACTS_ON_FAILURE_ONLY": fmt.Sprintf("%t", c.config.CollectOnFailureOnly), + "E2E_ARTIFACTS_MAX_LOG_LINES": fmt.Sprintf("%d", c.config.MaxLogLines), + "E2E_ARTIFACTS_COLLECTION_TIME": runEnd.Sub(c.runStart).String(), + }, + GitCommit: os.Getenv("E2E_GIT_COMMIT"), + GitBranch: os.Getenv("E2E_GIT_BRANCH"), + GitDirty: os.Getenv("E2E_GIT_DIRTY"), + Description: os.Getenv("E2E_RUN_DESCRIPTION"), + } + + if err := c.metadata.WriteRunMetadata(runMeta); err != nil { + return fmt.Errorf("failed to write run metadata: %w", err) + } + + fmt.Fprintf(GinkgoWriter, "\n📦 Artifact Collection Summary:\n") + fmt.Fprintf(GinkgoWriter, " Location: %s\n", c.storage.GetRunDir()) + fmt.Fprintf(GinkgoWriter, " Total tests: %d\n", c.totalTests) + fmt.Fprintf(GinkgoWriter, " Failed tests with artifacts: %d\n", c.failedTests) + fmt.Fprintf(GinkgoWriter, " Duration: %v\n\n", runEnd.Sub(c.runStart)) + + return nil +} + +// createShortTestName creates a short, numbered directory name from full test name +// Input: "Artifact Verification should intentionally fail to test artifact collection" +// Output: "01-artifact-verification" +func createShortTestName(fullName string, counter int) string { + // Split by spaces to get the first part (Describe block name) + parts := strings.Fields(fullName) + if len(parts) == 0 { + return fmt.Sprintf("%02d-unknown", counter) + } + + // Stop at "should" or "[" - these mark the end of test suite name + var suiteParts []string + for _, word := range parts { + lower := strings.ToLower(word) + // Stop at common separators + if lower == "should" || strings.HasPrefix(word, "[") { + break + } + // Clean up and add word + clean := strings.Trim(word, "()[]{}") + if clean != "" { + suiteParts = append(suiteParts, clean) + } + // Limit to first 3-4 words + if len(suiteParts) >= 4 { + break + } + } + + // Fallback if nothing found + if len(suiteParts) == 0 { + suiteParts = parts[:1] + } + + // Join and lowercase + mainPart := strings.ToLower(strings.Join(suiteParts, "-")) + + // Remove any remaining special characters + replacer := strings.NewReplacer( + "(", "", ")", "", + "[", "", "]", "", + "{", "", "}", "", + ) + mainPart = replacer.Replace(mainPart) + + // Limit length to reasonable size + const maxLen = 40 + if len(mainPart) > maxLen { + mainPart = mainPart[:maxLen] + } + + // Add counter prefix for uniqueness and ordering + return fmt.Sprintf("%02d-%s", counter, mainPart) +} + +// getOperatorStartTime retrieves the operator pod's start time for degradation tracking +func (c *collector) getOperatorStartTime() time.Time { + const operatorNs = "vector-operator-system" + operatorClient := kubectl.NewClient(operatorNs) + + // Get operator pods + pods, err := operatorClient.GetPodsByLabel("app.kubernetes.io/name=vector-operator") + if err != nil || len(pods) == 0 { + // If we can't get operator pod, return zero time + return time.Time{} + } + + // Get pod start time + startTimeStr, err := operatorClient.GetWithJsonPath("pod", pods[0], ".status.startTime") + if err != nil { + return time.Time{} + } + + // Parse RFC3339 timestamp + startTime, err := time.Parse(time.RFC3339, strings.TrimSpace(startTimeStr)) + if err != nil { + return time.Time{} + } + + return startTime +} + +// collectPprofProfiles collects pprof profiles from the operator pod for leak diagnosis +// Uses kubectl port-forward since distroless image doesn't have wget/curl +func (c *collector) collectPprofProfiles(ctx context.Context, testDir string, inventory *ArtifactInventory, podName, namespace string) error { + const pprofPort = "6060" + const localPort = "16060" // Use high port to avoid conflicts + + // Start port-forward in background + portForwardCmd := exec.Command("kubectl", "port-forward", + fmt.Sprintf("pod/%s", podName), + "-n", namespace, + fmt.Sprintf("%s:%s", localPort, pprofPort)) + + if err := portForwardCmd.Start(); err != nil { + return fmt.Errorf("failed to start port-forward: %w", err) + } + defer func() { + if portForwardCmd.Process != nil { + _ = portForwardCmd.Process.Kill() + } + }() + + // Wait a bit for port-forward to establish + time.Sleep(2 * time.Second) + + // Collect goroutine profile (text format for readability) + goroutineCmd := exec.Command("curl", "-s", + fmt.Sprintf("http://localhost:%s/debug/pprof/goroutine?debug=1", localPort)) + goroutineOutput, err := goroutineCmd.CombinedOutput() + if err == nil && len(goroutineOutput) > 0 { + if err := c.storage.WriteFile(testDir, "operator", "pprof-goroutine.txt", goroutineOutput); err != nil { + return fmt.Errorf("failed to write goroutine profile: %w", err) + } + inventory.ResourceFiles = append(inventory.ResourceFiles, "operator/pprof-goroutine.txt") + } else { + return fmt.Errorf("failed to collect goroutine profile: %w", err) + } + + // Collect heap profile (text format for readability) + heapCmd := exec.Command("curl", "-s", + fmt.Sprintf("http://localhost:%s/debug/pprof/heap?debug=1", localPort)) + heapOutput, err := heapCmd.CombinedOutput() + if err == nil && len(heapOutput) > 0 { + if err := c.storage.WriteFile(testDir, "operator", "pprof-heap.txt", heapOutput); err != nil { + return fmt.Errorf("failed to write heap profile: %w", err) + } + inventory.ResourceFiles = append(inventory.ResourceFiles, "operator/pprof-heap.txt") + } else { + return fmt.Errorf("failed to collect heap profile: %w", err) + } + + return nil +} diff --git a/test/e2e/framework/artifacts/config.go b/test/e2e/framework/artifacts/config.go new file mode 100644 index 00000000..c6033566 --- /dev/null +++ b/test/e2e/framework/artifacts/config.go @@ -0,0 +1,137 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package artifacts + +import ( + "os" + "strconv" + "time" +) + +// Default configuration values +const ( + defaultBaseDir = "test/e2e/artifacts" + defaultMaxLogLines = 500 + defaultMaxResourceSize = 10 * 1024 * 1024 // 10MB + defaultMaxTotalSize = 100 * 1024 * 1024 // 100MB per test + defaultCollectionTimeout = 30 * time.Second + defaultEnabled = true + defaultOnFailureOnly = true + defaultMinimalOnly = false +) + +// Config defines artifact collection behavior +type Config struct { + // Collection control + Enabled bool // Master switch for artifact collection + CollectOnFailureOnly bool // Collect artifacts only for failed tests + CollectMinimalOnly bool // Collect only P0 artifacts (fast path) + + // Storage paths + BaseDir string // Base directory for artifact storage + + // Size limits (prevent artifact bloat) + MaxLogLines int // Maximum log lines per pod + MaxResourceSize int64 // Maximum size for single resource (bytes) + MaxTotalSize int64 // Maximum total size per test (bytes) + + // Timeouts + CollectionTimeout time.Duration // Maximum time to collect artifacts + + // Filters + NamespacePatterns []string // Namespace patterns to collect from + PodLabelSelectors []string // Pod label selectors for filtering +} + +// LoadConfigFromEnv loads configuration from environment variables +// Following Phase 1 pattern: ENV-based config with sensible defaults +func LoadConfigFromEnv() Config { + return Config{ + Enabled: getEnvBool("E2E_ARTIFACTS_ENABLED", defaultEnabled), + CollectOnFailureOnly: getEnvBool("E2E_ARTIFACTS_ON_FAILURE_ONLY", defaultOnFailureOnly), + CollectMinimalOnly: getEnvBool("E2E_ARTIFACTS_MINIMAL_ONLY", defaultMinimalOnly), + + BaseDir: getEnvString("E2E_ARTIFACTS_DIR", defaultBaseDir), + + MaxLogLines: getEnvInt("E2E_ARTIFACTS_MAX_LOG_LINES", defaultMaxLogLines), + MaxResourceSize: getEnvInt64("E2E_ARTIFACTS_MAX_RESOURCE_SIZE", defaultMaxResourceSize), + MaxTotalSize: getEnvInt64("E2E_ARTIFACTS_MAX_TOTAL_SIZE", defaultMaxTotalSize), + + CollectionTimeout: getEnvDuration("E2E_ARTIFACTS_TIMEOUT", defaultCollectionTimeout), + + NamespacePatterns: []string{"test-*"}, + PodLabelSelectors: []string{}, + } +} + +// Helper functions for ENV parsing + +func getEnvBool(key string, defaultValue bool) bool { + value := os.Getenv(key) + if value == "" { + return defaultValue + } + result, err := strconv.ParseBool(value) + if err != nil { + return defaultValue + } + return result +} + +func getEnvInt(key string, defaultValue int) int { + value := os.Getenv(key) + if value == "" { + return defaultValue + } + result, err := strconv.Atoi(value) + if err != nil { + return defaultValue + } + return result +} + +func getEnvInt64(key string, defaultValue int64) int64 { + value := os.Getenv(key) + if value == "" { + return defaultValue + } + result, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return defaultValue + } + return result +} + +func getEnvString(key string, defaultValue string) string { + value := os.Getenv(key) + if value == "" { + return defaultValue + } + return value +} + +func getEnvDuration(key string, defaultValue time.Duration) time.Duration { + value := os.Getenv(key) + if value == "" { + return defaultValue + } + result, err := time.ParseDuration(value) + if err != nil { + return defaultValue + } + return result +} diff --git a/test/e2e/framework/artifacts/metadata.go b/test/e2e/framework/artifacts/metadata.go new file mode 100644 index 00000000..7eb03003 --- /dev/null +++ b/test/e2e/framework/artifacts/metadata.go @@ -0,0 +1,118 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package artifacts + +import ( + "encoding/json" + "fmt" + "time" +) + +// RunMetadata contains metadata about an entire test run +type RunMetadata struct { + RunID string `json:"run_id"` + StartTime time.Time `json:"start_time"` + EndTime time.Time `json:"end_time,omitempty"` + TotalTests int `json:"total_tests"` + FailedTests int `json:"failed_tests"` + PassedTests int `json:"passed_tests"` + Environment map[string]string `json:"environment"` + ArtifactsDir string `json:"artifacts_dir"` + // Git information for tracking test run version + GitCommit string `json:"git_commit,omitempty"` + GitBranch string `json:"git_branch,omitempty"` + GitDirty string `json:"git_dirty,omitempty"` // "dirty", "staged", or empty if clean + Description string `json:"description,omitempty"` // Optional user description +} + +// TestMetadata contains metadata about a single test execution +type TestMetadata struct { + Name string `json:"name"` + Namespace string `json:"namespace"` + StartTime time.Time `json:"start_time"` + EndTime time.Time `json:"end_time"` + Duration time.Duration `json:"duration_ms"` // in milliseconds for JSON + Failed bool `json:"failed"` + FailureMessage string `json:"failure_message,omitempty"` + Labels []string `json:"labels"` + + // Test sequence tracking (for degradation analysis) + TestSequenceNumber int `json:"test_sequence_number"` // Which test in the run (1, 2, 3...) + OperatorAge time.Duration `json:"operator_age_seconds"` // How long operator has been running + + // Collected artifacts inventory + Artifacts ArtifactInventory `json:"artifacts"` +} + +// ArtifactInventory tracks what artifacts were collected +type ArtifactInventory struct { + PodCount int `json:"pod_count"` + LogFiles []string `json:"log_files"` + ResourceFiles []string `json:"resource_files"` + EventFiles []string `json:"event_files"` + TotalSizeBytes int64 `json:"total_size_bytes"` + CollectionTime string `json:"collection_time"` // Human-readable duration +} + +// MetadataBuilder helps build and write metadata files +type MetadataBuilder struct { + storage *Storage +} + +// NewMetadataBuilder creates a new metadata builder +func NewMetadataBuilder(storage *Storage) *MetadataBuilder { + return &MetadataBuilder{ + storage: storage, + } +} + +// WriteTestMetadata writes test metadata to JSON file +func (m *MetadataBuilder) WriteTestMetadata(meta TestMetadata, testDir string) error { + data, err := json.MarshalIndent(meta, "", " ") + if err != nil { + return fmt.Errorf("failed to marshal test metadata: %w", err) + } + + return m.storage.WriteFile(testDir, "", "metadata.json", data) +} + +// WriteRunMetadata writes run metadata to JSON file +func (m *MetadataBuilder) WriteRunMetadata(meta RunMetadata) error { + data, err := json.MarshalIndent(meta, "", " ") + if err != nil { + return fmt.Errorf("failed to marshal run metadata: %w", err) + } + + return m.storage.WriteFileInRunDir("metadata.json", data) +} + +// BuildTestMetadata creates TestMetadata from TestInfo +func BuildTestMetadata(info TestInfo, artifacts ArtifactInventory) TestMetadata { + return TestMetadata{ + Name: info.Name, + Namespace: info.Namespace, + StartTime: info.StartTime, + EndTime: info.EndTime, + Duration: info.Duration, + Failed: info.Failed, + FailureMessage: info.FailureMessage, + Labels: info.Labels, + TestSequenceNumber: info.SequenceNumber, + OperatorAge: info.OperatorAge, + Artifacts: artifacts, + } +} diff --git a/test/e2e/framework/artifacts/storage.go b/test/e2e/framework/artifacts/storage.go new file mode 100644 index 00000000..3ecca496 --- /dev/null +++ b/test/e2e/framework/artifacts/storage.go @@ -0,0 +1,327 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package artifacts + +import ( + "bytes" + "fmt" + "io" + "os" + "path/filepath" + "time" +) + +// Storage handles filesystem operations for artifact collection +type Storage struct { + baseDir string + runDir string + maxSize int64 + runID string +} + +// NewStorage creates a new storage instance with specified configuration +func NewStorage(baseDir string, runID string, maxSize int64) (*Storage, error) { + var runDir string + + // Check if baseDir already contains a run directory (e.g., from E2E_ARTIFACTS_DIR) + // This prevents nested run-{timestamp}/run-{timestamp}/ structure + if filepath.Base(baseDir) == "artifacts" && isRunDirectory(filepath.Dir(baseDir)) { + // baseDir is already inside a run directory (e.g., test/e2e/results/run-{timestamp}/artifacts/) + // Use it directly without creating another run-{runID} subdirectory + runDir = baseDir + } else { + // Standard case: create run-{runID} subdirectory + runDir = filepath.Join(baseDir, "run-"+runID) + } + + // Create run directory + if err := os.MkdirAll(runDir, 0755); err != nil { + return nil, fmt.Errorf("failed to create run directory %s: %w", runDir, err) + } + + return &Storage{ + baseDir: baseDir, + runDir: runDir, + maxSize: maxSize, + runID: runID, + }, nil +} + +// isRunDirectory checks if a directory name matches the run-{timestamp} pattern +func isRunDirectory(path string) bool { + base := filepath.Base(path) + return len(base) > 4 && base[:4] == "run-" +} + +// WriteFile writes content to a file within a test directory with size limits +// testDir: test-specific directory name (e.g., "test-normal-mode") +// category: subdirectory within test dir (e.g., "logs", "resources", "events") +// filename: name of the file to write +func (s *Storage) WriteFile(testDir, category, filename string, content []byte) error { + // Check and enforce size limit + if int64(len(content)) > s.maxSize { + content = s.truncateContent(content, "size limit exceeded") + } + + // Build full directory path + var dir string + if category != "" { + dir = filepath.Join(s.runDir, testDir, category) + } else { + dir = filepath.Join(s.runDir, testDir) + } + + // Create category directory + if err := os.MkdirAll(dir, 0755); err != nil { + return fmt.Errorf("failed to create directory %s: %w", dir, err) + } + + // Write file atomically (write to temp, then rename) + path := filepath.Join(dir, filename) + tempPath := path + ".tmp" + + if err := os.WriteFile(tempPath, content, 0644); err != nil { + return fmt.Errorf("failed to write temp file %s: %w", tempPath, err) + } + + if err := os.Rename(tempPath, path); err != nil { + // Clean up temp file if rename fails + _ = os.Remove(tempPath) + return fmt.Errorf("failed to rename temp file %s to %s: %w", tempPath, path, err) + } + + return nil +} + +// WriteFileInRunDir writes a file directly in the run directory (not test-specific) +// Used for run-level metadata +func (s *Storage) WriteFileInRunDir(filename string, content []byte) error { + path := filepath.Join(s.runDir, filename) + tempPath := path + ".tmp" + + if err := os.WriteFile(tempPath, content, 0644); err != nil { + return fmt.Errorf("failed to write temp file %s: %w", tempPath, err) + } + + if err := os.Rename(tempPath, path); err != nil { + _ = os.Remove(tempPath) + return fmt.Errorf("failed to rename temp file %s to %s: %w", tempPath, path, err) + } + + return nil +} + +// WriteStream writes content from a reader to a file with size limits +// Useful for streaming command output without loading all into memory +func (s *Storage) WriteStream(testDir, category, filename string, reader io.Reader, maxLines int) error { + // Build full directory path + var dir string + if category != "" { + dir = filepath.Join(s.runDir, testDir, category) + } else { + dir = filepath.Join(s.runDir, testDir) + } + + // Create category directory + if err := os.MkdirAll(dir, 0755); err != nil { + return fmt.Errorf("failed to create directory %s: %w", dir, err) + } + + // Write to temp file + path := filepath.Join(dir, filename) + tempPath := path + ".tmp" + + tempFile, err := os.Create(tempPath) + if err != nil { + return fmt.Errorf("failed to create temp file %s: %w", tempPath, err) + } + defer tempFile.Close() + + // Copy with size limit + written, err := io.CopyN(tempFile, reader, s.maxSize) + if err != nil && err != io.EOF { + // If we hit the limit, add truncation marker + if written >= s.maxSize { + truncationMarker := []byte("\n\n... [TRUNCATED - exceeds size limit] ...\n") + _, _ = tempFile.Write(truncationMarker) + } + } + + tempFile.Close() + + // Rename to final path + if err := os.Rename(tempPath, path); err != nil { + _ = os.Remove(tempPath) + return fmt.Errorf("failed to rename temp file %s to %s: %w", tempPath, path, err) + } + + return nil +} + +// GetRunDir returns the run directory path +func (s *Storage) GetRunDir() string { + return s.runDir +} + +// GetRunID returns the run ID +func (s *Storage) GetRunID() string { + return s.runID +} + +// truncateContent truncates content to fit within maxSize and adds a marker +func (s *Storage) truncateContent(content []byte, reason string) []byte { + marker := []byte(fmt.Sprintf("\n\n... [TRUNCATED: %s - max %d bytes] ...\n", reason, s.maxSize)) + + // If marker itself is too large, truncate it + if int64(len(marker)) >= s.maxSize { + return marker[:s.maxSize] + } + + // Calculate how much content we can keep + keepSize := s.maxSize - int64(len(marker)) + if keepSize < 0 { + keepSize = 0 + } + + // Keep the end of the content (most recent logs are usually most relevant) + // But also include first few bytes to show what file it is + headerSize := int64(100) + if headerSize > keepSize/2 { + headerSize = keepSize / 2 + } + + var truncated []byte + if headerSize > 0 && int64(len(content)) > headerSize { + // Include header + marker + tail + tailSize := keepSize - headerSize + tailStart := int64(len(content)) - tailSize + if tailStart < headerSize { + tailStart = headerSize + } + + truncated = append(truncated, content[:headerSize]...) + truncated = append(truncated, []byte("\n... [CONTENT SKIPPED] ...\n")...) + if tailStart < int64(len(content)) { + truncated = append(truncated, content[tailStart:]...) + } + } else { + // Just take what fits + truncated = content[:keepSize] + } + + return append(truncated, marker...) +} + +// TruncateLogLines truncates log output to specified number of lines +// Takes the LAST N lines (most recent logs are most relevant for debugging) +func TruncateLogLines(content []byte, maxLines int) []byte { + if maxLines <= 0 { + return content + } + + lines := []byte{} + lineCount := 0 + newlineCount := 0 + + // Count newlines from the end + for i := len(content) - 1; i >= 0; i-- { + if content[i] == '\n' { + newlineCount++ + if newlineCount >= maxLines { + // Found enough lines, this is our cut point + lines = content[i+1:] + lineCount = maxLines + break + } + } + } + + // If we didn't find enough newlines, return all content + if lineCount == 0 { + return content + } + + // Skip leading empty lines and trim leading whitespace from first line + start := 0 + for start < len(lines) { + // Find end of current line + end := start + for end < len(lines) && lines[end] != '\n' { + end++ + } + + // Check if line has any non-whitespace content + lineContent := bytes.TrimSpace(lines[start:end]) + if len(lineContent) > 0 { + // Found first non-empty line + // Build result: trimmed first line + rest + result := lineContent + if end < len(lines) { + // Append the rest (from \n onwards) + result = append(result, lines[end:]...) + } + lines = result + break + } + + // Move to next line (skip the \n) + start = end + 1 + } + + // Add truncation marker at the beginning + marker := []byte(fmt.Sprintf("... [Showing last %d lines] ...\n", lineCount)) + return append(marker, lines...) +} + +// CreateTestDir creates a directory for a specific test +func (s *Storage) CreateTestDir(testName string) (string, error) { + // Sanitize test name for filesystem + sanitized := sanitizeFilename(testName) + testDir := filepath.Join(s.runDir, sanitized) + + if err := os.MkdirAll(testDir, 0755); err != nil { + return "", fmt.Errorf("failed to create test directory %s: %w", testDir, err) + } + + return sanitized, nil +} + +// sanitizeFilename removes characters that are problematic in filenames +func sanitizeFilename(name string) string { + // Replace spaces and problematic characters with hyphens + result := []byte(name) + for i, c := range result { + switch c { + case '/', '\\', ':', '*', '?', '"', '<', '>', '|', ' ': + result[i] = '-' + } + } + + // Limit length to avoid filesystem issues + const maxLength = 200 + if len(result) > maxLength { + // Use a timestamp suffix to ensure uniqueness + suffix := fmt.Sprintf("-%d", time.Now().Unix()) + cutPoint := maxLength - len(suffix) + if cutPoint < 0 { + cutPoint = 0 + } + result = append(result[:cutPoint], []byte(suffix)...) + } + + return string(result) +} diff --git a/test/e2e/framework/artifacts/storage_test.go b/test/e2e/framework/artifacts/storage_test.go new file mode 100644 index 00000000..3af92c63 --- /dev/null +++ b/test/e2e/framework/artifacts/storage_test.go @@ -0,0 +1,55 @@ +package artifacts + +import ( + "bytes" + "testing" +) + +func TestTruncateLogLines_RemovesLeadingWhitespace(t *testing.T) { + tests := []struct { + name string + input string + maxLines int + want string + }{ + { + name: "removes leading newlines and spaces", + input: "\n\n \t2025-11-14T19:58:40Z\tINFO\tstart Reconcile\nline2\nline3", + maxLines: 3, + want: "... [Showing last 3 lines] ...\n2025-11-14T19:58:40Z\tINFO\tstart Reconcile\nline2\nline3", + }, + { + name: "handles logs without leading whitespace", + input: "line1\nline2\nline3\nline4\nline5", + maxLines: 3, + want: "... [Showing last 3 lines] ...\nline3\nline4\nline5", + }, + { + name: "keeps content when less than maxLines", + input: "line1\nline2", + maxLines: 5, + want: "line1\nline2", + }, + { + name: "trims leading whitespace from first line but preserves it in subsequent lines", + input: "line1\nline2\nline3 with content\n indented line4\n indented line5", + maxLines: 3, + want: "... [Showing last 3 lines] ...\nline3 with content\n indented line4\n indented line5", + }, + { + name: "handles real operator log format", + input: "line1\nline2\nline3\nline4\n2025-11-14T19:58:40Z\tINFO\tstart Reconcile", + maxLines: 1, + want: "... [Showing last 1 lines] ...\n2025-11-14T19:58:40Z\tINFO\tstart Reconcile", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := TruncateLogLines([]byte(tt.input), tt.maxLines) + if !bytes.Equal(got, []byte(tt.want)) { + t.Errorf("TruncateLogLines() = %q, want %q", string(got), tt.want) + } + }) + } +} diff --git a/test/e2e/framework/assertions/matchers.go b/test/e2e/framework/assertions/matchers.go new file mode 100644 index 00000000..227721d4 --- /dev/null +++ b/test/e2e/framework/assertions/matchers.go @@ -0,0 +1,300 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package assertions + +import ( + "fmt" + "strings" + + "github.com/onsi/gomega/types" + + "github.com/kaasops/vector-operator/test/e2e/framework/kubectl" +) + +// PipelineResource represents a pipeline for matching +type PipelineResource struct { + namespace string + name string + kubectl *kubectl.Client +} + +// NewPipelineResource creates a new pipeline resource wrapper +func NewPipelineResource(namespace, name string) *PipelineResource { + return &PipelineResource{ + namespace: namespace, + name: name, + kubectl: kubectl.NewClient(namespace), + } +} + +// resourceType returns the correct resource type based on namespace +// Empty namespace = cluster-scoped (ClusterVectorPipeline) +// Non-empty namespace = namespaced (VectorPipeline) +func (p *PipelineResource) resourceType() string { + if p.namespace == "" { + return "clustervectorpipeline" + } + return "vectorpipeline" +} + +// BeValid matcher for pipeline validity +type beValidMatcher struct{} + +func (m *beValidMatcher) Match(actual interface{}) (success bool, err error) { + pipeline, ok := actual.(*PipelineResource) + if !ok { + return false, fmt.Errorf("BeValid matcher expects a *PipelineResource") + } + + result, err := pipeline.kubectl.GetWithJsonPath(pipeline.resourceType(), pipeline.name, ".status.configCheckResult") + if err != nil { + return false, err + } + + return result == "true", nil +} + +func (m *beValidMatcher) FailureMessage(actual interface{}) string { + pipeline := actual.(*PipelineResource) + return fmt.Sprintf("Expected pipeline %s/%s to be valid", pipeline.namespace, pipeline.name) +} + +func (m *beValidMatcher) NegatedFailureMessage(actual interface{}) string { + pipeline := actual.(*PipelineResource) + return fmt.Sprintf("Expected pipeline %s/%s not to be valid", pipeline.namespace, pipeline.name) +} + +// BeValid returns a matcher that checks if a pipeline is valid +func BeValid() types.GomegaMatcher { + return &beValidMatcher{} +} + +// HaveSplitModeEnabled matcher +type haveSplitModeEnabledMatcher struct{} + +func (m *haveSplitModeEnabledMatcher) Match(actual interface{}) (success bool, err error) { + pipeline, ok := actual.(*PipelineResource) + if !ok { + return false, fmt.Errorf("HaveSplitModeEnabled matcher expects a *PipelineResource") + } + + result, err := pipeline.kubectl.GetWithJsonPath(pipeline.resourceType(), pipeline.name, ".status.splitMode.enabled") + if err != nil { + return false, err + } + + return result == "true", nil +} + +func (m *haveSplitModeEnabledMatcher) FailureMessage(actual interface{}) string { + pipeline := actual.(*PipelineResource) + return fmt.Sprintf("Expected pipeline %s/%s to have split mode enabled", pipeline.namespace, pipeline.name) +} + +func (m *haveSplitModeEnabledMatcher) NegatedFailureMessage(actual interface{}) string { + pipeline := actual.(*PipelineResource) + return fmt.Sprintf("Expected pipeline %s/%s not to have split mode enabled", pipeline.namespace, pipeline.name) +} + +// HaveSplitModeEnabled returns a matcher that checks if split mode is enabled +func HaveSplitModeEnabled() types.GomegaMatcher { + return &haveSplitModeEnabledMatcher{} +} + +// HaveRole matcher +type haveRoleMatcher struct { + expectedRole string +} + +func (m *haveRoleMatcher) Match(actual interface{}) (success bool, err error) { + pipeline, ok := actual.(*PipelineResource) + if !ok { + return false, fmt.Errorf("HaveRole matcher expects a *PipelineResource") + } + + result, err := pipeline.kubectl.GetWithJsonPath(pipeline.resourceType(), pipeline.name, ".status.role") + if err != nil { + return false, err + } + + return result == m.expectedRole, nil +} + +func (m *haveRoleMatcher) FailureMessage(actual interface{}) string { + pipeline := actual.(*PipelineResource) + return fmt.Sprintf("Expected pipeline %s/%s to have role %s", pipeline.namespace, pipeline.name, m.expectedRole) +} + +func (m *haveRoleMatcher) NegatedFailureMessage(actual interface{}) string { + pipeline := actual.(*PipelineResource) + return fmt.Sprintf("Expected pipeline %s/%s not to have role %s", pipeline.namespace, pipeline.name, m.expectedRole) +} + +// HaveRole returns a matcher that checks the pipeline role +func HaveRole(role string) types.GomegaMatcher { + return &haveRoleMatcher{expectedRole: role} +} + +// ServiceResource represents a service for matching +type ServiceResource struct { + namespace string + name string + kubectl *kubectl.Client +} + +// NewServiceResource creates a new service resource wrapper +func NewServiceResource(namespace, name string) *ServiceResource { + return &ServiceResource{ + namespace: namespace, + name: name, + kubectl: kubectl.NewClient(namespace), + } +} + +// Exist matcher for service existence +type existMatcher struct{} + +func (m *existMatcher) Match(actual interface{}) (success bool, err error) { + service, ok := actual.(*ServiceResource) + if !ok { + return false, fmt.Errorf("Exist matcher expects a *ServiceResource") + } + + _, err = service.kubectl.Get("service", service.name) + return err == nil, nil +} + +func (m *existMatcher) FailureMessage(actual interface{}) string { + service := actual.(*ServiceResource) + return fmt.Sprintf("Expected service %s/%s to exist", service.namespace, service.name) +} + +func (m *existMatcher) NegatedFailureMessage(actual interface{}) string { + service := actual.(*ServiceResource) + return fmt.Sprintf("Expected service %s/%s not to exist", service.namespace, service.name) +} + +// Exist returns a matcher that checks if a service exists +func Exist() types.GomegaMatcher { + return &existMatcher{} +} + +// HavePort matcher +type havePortMatcher struct { + expectedPort string +} + +func (m *havePortMatcher) Match(actual interface{}) (success bool, err error) { + service, ok := actual.(*ServiceResource) + if !ok { + return false, fmt.Errorf("HavePort matcher expects a *ServiceResource") + } + + port, err := service.kubectl.GetWithJsonPath("service", service.name, ".spec.ports[0].port") + if err != nil { + return false, err + } + + return port == m.expectedPort, nil +} + +func (m *havePortMatcher) FailureMessage(actual interface{}) string { + service := actual.(*ServiceResource) + return fmt.Sprintf("Expected service %s/%s to have port %s", service.namespace, service.name, m.expectedPort) +} + +func (m *havePortMatcher) NegatedFailureMessage(actual interface{}) string { + service := actual.(*ServiceResource) + return fmt.Sprintf("Expected service %s/%s not to have port %s", service.namespace, service.name, m.expectedPort) +} + +// HavePort returns a matcher that checks the service port +func HavePort(port string) types.GomegaMatcher { + return &havePortMatcher{expectedPort: port} +} + +// BeInvalid matcher for pipeline invalidity +type beInvalidMatcher struct{} + +func (m *beInvalidMatcher) Match(actual interface{}) (success bool, err error) { + pipeline, ok := actual.(*PipelineResource) + if !ok { + return false, fmt.Errorf("BeInvalid matcher expects a *PipelineResource") + } + + result, err := pipeline.kubectl.GetWithJsonPath(pipeline.resourceType(), pipeline.name, ".status.configCheckResult") + if err != nil { + return false, err + } + + return result == "false", nil +} + +func (m *beInvalidMatcher) FailureMessage(actual interface{}) string { + pipeline := actual.(*PipelineResource) + return fmt.Sprintf("Expected pipeline %s/%s to be invalid", pipeline.namespace, pipeline.name) +} + +func (m *beInvalidMatcher) NegatedFailureMessage(actual interface{}) string { + pipeline := actual.(*PipelineResource) + return fmt.Sprintf("Expected pipeline %s/%s not to be invalid", pipeline.namespace, pipeline.name) +} + +// BeInvalid returns a matcher that checks if a pipeline is invalid +func BeInvalid() types.GomegaMatcher { + return &beInvalidMatcher{} +} + +// HaveErrorContaining matcher for error messages +type haveErrorContainingMatcher struct { + expectedSubstring string +} + +func (m *haveErrorContainingMatcher) Match(actual interface{}) (success bool, err error) { + pipeline, ok := actual.(*PipelineResource) + if !ok { + return false, fmt.Errorf("HaveErrorContaining matcher expects a *PipelineResource") + } + + reason, err := pipeline.kubectl.GetWithJsonPath(pipeline.resourceType(), pipeline.name, ".status.reason") + if err != nil { + return false, err + } + + // Simple substring check (case-insensitive) + lowerReason := strings.ToLower(reason) + lowerExpected := strings.ToLower(m.expectedSubstring) + + return strings.Contains(lowerReason, lowerExpected), nil +} + +func (m *haveErrorContainingMatcher) FailureMessage(actual interface{}) string { + pipeline := actual.(*PipelineResource) + return fmt.Sprintf("Expected pipeline %s/%s to have error containing '%s'", + pipeline.namespace, pipeline.name, m.expectedSubstring) +} + +func (m *haveErrorContainingMatcher) NegatedFailureMessage(actual interface{}) string { + pipeline := actual.(*PipelineResource) + return fmt.Sprintf("Expected pipeline %s/%s not to have error containing '%s'", + pipeline.namespace, pipeline.name, m.expectedSubstring) +} + +// HaveErrorContaining returns a matcher that checks if error message contains substring +func HaveErrorContaining(substring string) types.GomegaMatcher { + return &haveErrorContainingMatcher{expectedSubstring: substring} +} diff --git a/test/e2e/framework/config/constants.go b/test/e2e/framework/config/constants.go new file mode 100644 index 00000000..8bde68e6 --- /dev/null +++ b/test/e2e/framework/config/constants.go @@ -0,0 +1,50 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package config + +// Test labels for selective test execution +const ( + // Execution speed labels + LabelSmoke = "smoke" + LabelFast = "fast" + LabelSlow = "slow" + LabelRegression = "regression" + LabelStress = "stress" + LabelParallel = "parallel" + + // Priority labels (P0 = critical, must always pass) + LabelP0 = "p0" + LabelP1 = "p1" + LabelP2 = "p2" + + // Category labels + LabelSecurity = "security" + LabelConstraint = "constraint" +) + +// Resource naming suffixes +const ( + AggregatorSuffix = "-aggregator" + AgentSuffix = "-agent" +) + +// Kubernetes labels +const ( + ComponentLabel = "app.kubernetes.io/component" + AggregatorComponent = "Aggregator" + AgentComponent = "Agent" +) diff --git a/test/e2e/framework/config/timeouts.go b/test/e2e/framework/config/timeouts.go new file mode 100644 index 00000000..82a0fe7f --- /dev/null +++ b/test/e2e/framework/config/timeouts.go @@ -0,0 +1,92 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package config + +import ( + "os" + "time" +) + +// Default timeout values +const ( + // Resource creation timeouts + defaultDeploymentCreateTimeout = 120 * time.Second // Increased for resource-heavy aggregator deployments + defaultDeploymentReadyTimeout = 120 * time.Second + defaultNamespaceDeleteTimeout = 120 * time.Second // Increased timeout to handle slow namespace termination + + // Pipeline validation timeouts + defaultPipelineValidTimeout = 2 * time.Minute + defaultConfigCheckTimeout = 30 * time.Second + + // Service check timeouts + defaultServiceCreateTimeout = 2 * time.Minute + + // Polling intervals + defaultDefaultPollInterval = 2 * time.Second + defaultFastPollInterval = 1 * time.Second + defaultSlowPollInterval = 2 * time.Second // Reduced from 5s - more responsive polling + + // Test spec timeouts + defaultDefaultTestTimeout = 5 * time.Minute + defaultLongTestTimeout = 10 * time.Minute +) + +// Configurable timeout variables (can be overridden via environment variables) +var ( + // Resource creation timeouts + DeploymentCreateTimeout = getEnvDuration("E2E_DEPLOYMENT_CREATE_TIMEOUT", defaultDeploymentCreateTimeout) + DeploymentReadyTimeout = getEnvDuration("E2E_DEPLOYMENT_READY_TIMEOUT", defaultDeploymentReadyTimeout) + NamespaceDeleteTimeout = getEnvDuration("E2E_NAMESPACE_DELETE_TIMEOUT", defaultNamespaceDeleteTimeout) + + // Pipeline validation timeouts + PipelineValidTimeout = getEnvDuration("E2E_PIPELINE_VALID_TIMEOUT", defaultPipelineValidTimeout) + ConfigCheckTimeout = getEnvDuration("E2E_CONFIG_CHECK_TIMEOUT", defaultConfigCheckTimeout) + + // Service check timeouts + ServiceCreateTimeout = getEnvDuration("E2E_SERVICE_CREATE_TIMEOUT", defaultServiceCreateTimeout) + + // Polling intervals + DefaultPollInterval = getEnvDuration("E2E_DEFAULT_POLL_INTERVAL", defaultDefaultPollInterval) + FastPollInterval = getEnvDuration("E2E_FAST_POLL_INTERVAL", defaultFastPollInterval) + SlowPollInterval = getEnvDuration("E2E_SLOW_POLL_INTERVAL", defaultSlowPollInterval) + + // Test spec timeouts + DefaultTestTimeout = getEnvDuration("E2E_DEFAULT_TEST_TIMEOUT", defaultDefaultTestTimeout) + LongTestTimeout = getEnvDuration("E2E_LONG_TEST_TIMEOUT", defaultLongTestTimeout) +) + +// getEnvDuration reads a duration from environment variable, falling back to default if not set or invalid +func getEnvDuration(envVar string, defaultValue time.Duration) time.Duration { + if val := os.Getenv(envVar); val != "" { + if duration, err := time.ParseDuration(val); err == nil { + return duration + } + // If parsing fails, fall back to default (silently to avoid test noise) + } + return defaultValue +} + +// GetPollInterval returns appropriate poll interval based on timeout +func GetPollInterval(timeout time.Duration) time.Duration { + if timeout < 30*time.Second { + return FastPollInterval + } + if timeout > 2*time.Minute { + return SlowPollInterval + } + return DefaultPollInterval +} diff --git a/test/e2e/framework/errors/errors.go b/test/e2e/framework/errors/errors.go new file mode 100644 index 00000000..c2bd06a0 --- /dev/null +++ b/test/e2e/framework/errors/errors.go @@ -0,0 +1,100 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package errors + +import ( + "strings" +) + +// Centralized error classification for e2e tests +// Provides consistent error handling across kubectl operations + +// IsAlreadyExists checks if error indicates resource already exists +func IsAlreadyExists(err error) bool { + if err == nil { + return false + } + errStr := err.Error() + return strings.Contains(errStr, "AlreadyExists") || + strings.Contains(errStr, "already exists") +} + +// IsNotFound checks if error indicates resource not found +func IsNotFound(err error) bool { + if err == nil { + return false + } + errStr := err.Error() + return strings.Contains(errStr, "NotFound") || + strings.Contains(errStr, "not found") || + strings.Contains(errStr, "(NotFound)") +} + +// IsConflict checks if error indicates resource conflict +func IsConflict(err error) bool { + if err == nil { + return false + } + errStr := err.Error() + return strings.Contains(errStr, "Conflict") || + strings.Contains(errStr, "the object has been modified") +} + +// IsTimeout checks if error indicates timeout +func IsTimeout(err error) bool { + if err == nil { + return false + } + errStr := err.Error() + return strings.Contains(errStr, "timeout") || + strings.Contains(errStr, "timed out") || + strings.Contains(errStr, "context deadline exceeded") +} + +// IsConnectionError checks if error indicates connection/network issue +func IsConnectionError(err error) bool { + if err == nil { + return false + } + errStr := err.Error() + return strings.Contains(errStr, "connection refused") || + strings.Contains(errStr, "i/o timeout") || + strings.Contains(errStr, "network") || + strings.Contains(errStr, "dial tcp") +} + +// IsTransient checks if error is likely transient and retriable +func IsTransient(err error) bool { + if err == nil { + return false + } + return IsTimeout(err) || + IsConnectionError(err) || + IsConflict(err) || + strings.Contains(err.Error(), "Internal error") || + strings.Contains(err.Error(), "TooManyRequests") || + strings.Contains(err.Error(), "ServerTimeout") +} + +// IsIgnorable checks if error can be safely ignored in test setup/teardown +func IsIgnorable(err error) bool { + if err == nil { + return true + } + // AlreadyExists and NotFound are often acceptable in test lifecycle + return IsAlreadyExists(err) || IsNotFound(err) +} diff --git a/test/e2e/framework/framework.go b/test/e2e/framework/framework.go new file mode 100644 index 00000000..32ba098d --- /dev/null +++ b/test/e2e/framework/framework.go @@ -0,0 +1,1154 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package framework + +import ( + "context" + "encoding/base64" + "encoding/json" + "fmt" + "os" + "os/exec" + "path/filepath" + "regexp" + "strings" + "sync" + "time" + + . "github.com/onsi/ginkgo/v2" + "github.com/onsi/ginkgo/v2/types" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/util/wait" + + "github.com/kaasops/vector-operator/test/e2e/framework/config" + "github.com/kaasops/vector-operator/test/e2e/framework/errors" + "github.com/kaasops/vector-operator/test/e2e/framework/kubectl" + "github.com/kaasops/vector-operator/test/e2e/framework/recorder" +) + +const ( + // MaxConfigSize is the maximum allowed size for base64-encoded config data (10MB) + // This prevents DoS attacks via extremely large config payloads + MaxConfigSize = 10 * 1024 * 1024 // 10MB +) + +// Framework provides a high-level API for e2e tests +type Framework struct { + namespace string + kubectl *kubectl.Client + isShared bool + metrics *TestMetrics + recorder *recorder.TestRecorder + dryRun bool + recordSteps bool + TestDataPath string // Path to test data directory (configurable via E2E_TESTDATA_PATH) +} + +// TestMetrics tracks timing information for test operations +type TestMetrics struct { + SetupTime time.Duration + DeploymentWaitTime time.Duration + PipelineValidationTime time.Duration + CleanupTime time.Duration +} + +// Global shared framework instance +var sharedFramework *Framework + +// frameworkRegistry stores framework instances for artifact collection +// Key: namespace, Value: *Framework +// DEPRECATED: This is being phased out in favor of Ginkgo report entries. +// New code should use AddReportEntry() in Setup() and retrieve from ReportAfterEach. +var frameworkRegistry sync.Map + +// FrameworkContextKey is the key type for storing Framework in context +// Using a custom type prevents collisions with other context keys +type FrameworkContextKey struct{} + +// frameworkReportEntryName is the name used when storing framework in Ginkgo report entries +const frameworkReportEntryName = "framework-instance" + +// NewFramework creates a new isolated test framework with its own namespace +func NewFramework(namespace string) *Framework { + // Get test data path from environment or use default + testDataPath := os.Getenv("E2E_TESTDATA_PATH") + if testDataPath == "" { + testDataPath = filepath.Join("test", "e2e", "testdata") + } + + f := &Framework{ + namespace: namespace, + kubectl: kubectl.NewClient(namespace), + isShared: false, + metrics: &TestMetrics{}, + TestDataPath: testDataPath, + } + + // Check for dry-run or recording mode + if os.Getenv("E2E_DRY_RUN") == "true" { + f.dryRun = true + f.recorder = recorder.NewTestRecorder(namespace) + f.recordSteps = true + } else if os.Getenv("E2E_RECORD_STEPS") == "true" { + f.recordSteps = true + f.recorder = recorder.NewTestRecorder(namespace) + } + + return f +} + +// NewUniqueFramework creates a new framework with a unique timestamped namespace +// This prevents namespace collisions when tests run in parallel or when cleanup is slow +func NewUniqueFramework(baseName string) *Framework { + // Use nanosecond timestamp + counter for uniqueness + timestamp := time.Now().UnixNano() + uniqueNS := fmt.Sprintf("%s-%d", baseName, timestamp) + + // Get test data path from environment or use default + testDataPath := os.Getenv("E2E_TESTDATA_PATH") + if testDataPath == "" { + testDataPath = filepath.Join("test", "e2e", "testdata") + } + + f := &Framework{ + namespace: uniqueNS, + kubectl: kubectl.NewClient(uniqueNS), + isShared: false, + metrics: &TestMetrics{}, + TestDataPath: testDataPath, + } + + // Check for dry-run or recording mode + if os.Getenv("E2E_DRY_RUN") == "true" { + f.dryRun = true + f.recorder = recorder.NewTestRecorder(uniqueNS) + f.recordSteps = true + } else if os.Getenv("E2E_RECORD_STEPS") == "true" { + f.recordSteps = true + f.recorder = recorder.NewTestRecorder(uniqueNS) + } + + return f +} + +// Shared returns a shared framework instance that reuses the same namespace +// This is useful for parallel tests that don't interfere with each other +func Shared(namespace string) *Framework { + if sharedFramework == nil { + // Get test data path from environment or use default + testDataPath := os.Getenv("E2E_TESTDATA_PATH") + if testDataPath == "" { + testDataPath = filepath.Join("test", "e2e", "testdata") + } + + sharedFramework = &Framework{ + namespace: namespace, + kubectl: kubectl.NewClient(namespace), + isShared: true, + metrics: &TestMetrics{}, + TestDataPath: testDataPath, + } + + // Check for dry-run or recording mode + if os.Getenv("E2E_DRY_RUN") == "true" { + sharedFramework.dryRun = true + sharedFramework.recorder = recorder.NewTestRecorder(namespace) + sharedFramework.recordSteps = true + } else if os.Getenv("E2E_RECORD_STEPS") == "true" { + sharedFramework.recordSteps = true + sharedFramework.recorder = recorder.NewTestRecorder(namespace) + } + } + return sharedFramework +} + +// Setup performs the test environment setup +func (f *Framework) Setup() { + // Store framework in Ginkgo report entries for artifact collection + // This is the preferred method as it directly associates framework with the current test + // and works correctly with parallel test execution + AddReportEntry(frameworkReportEntryName, f) + + // DEPRECATED: Also store in global registry for backward compatibility + // This will be removed in a future version once all code migrates to using report entries + frameworkRegistry.Store(f.namespace, f) + + start := time.Now() + defer func() { + f.metrics.SetupTime = time.Since(start) + }() + + By(fmt.Sprintf("creating test namespace: %s", f.namespace)) + err := kubectl.CreateNamespace(f.namespace) + if err != nil { + // Check if it's an ignorable error (AlreadyExists, NotFound) + if errors.IsIgnorable(err) { + GinkgoWriter.Printf("Warning: namespace creation failed (might already exist): %v\n", err) + } else { + // Only fail for non-ignorable errors + Expect(err).NotTo(HaveOccurred()) + } + } + + // Wait for namespace to be ready before proceeding + By(fmt.Sprintf("waiting for namespace to be ready: %s", f.namespace)) + Eventually(func() bool { + ns, err := kubectl.GetNamespace(f.namespace) + if err != nil { + GinkgoWriter.Printf("Failed to get namespace status: %v\n", err) + return false + } + // Check namespace is Active (not Terminating) + return ns.Status.Phase == "Active" + }, config.DeploymentReadyTimeout, config.DefaultPollInterval).Should(BeTrue(), + fmt.Sprintf("namespace %s should be Active", f.namespace)) +} + +// Teardown performs the test environment cleanup +func (f *Framework) Teardown() { + // Export test plan if recording is enabled + if f.recorder != nil && f.recordSteps { + f.ExportTestPlan() + } + + // Don't cleanup shared namespaces immediately + if f.isShared { + return + } + + start := time.Now() + defer func() { + f.metrics.CleanupTime = time.Since(start) + }() + + By(fmt.Sprintf("cleaning up test namespace: %s", f.namespace)) + err := kubectl.DeleteNamespace(f.namespace, fmt.Sprintf("%ds", int(config.NamespaceDeleteTimeout.Seconds()))) + if err != nil { + GinkgoWriter.Printf("Warning: namespace cleanup failed: %v\n", err) + } + + // NOTE: Do NOT delete from frameworkRegistry here! + // ReportAfterEach runs AFTER AfterAll/Teardown, and needs the framework + // for artifact collection. The registry will be cleaned up when the process exits. + // frameworkRegistry.Delete(f.namespace) +} + +// Namespace returns the test namespace +func (f *Framework) Namespace() string { + return f.namespace +} + +// ApplyTestData loads and applies a test manifest from testdata directory +// It automatically replaces any hardcoded namespace with the framework's namespace +func (f *Framework) ApplyTestData(path string) { + By(fmt.Sprintf("applying test data: %s", path)) + + content, err := os.ReadFile(filepath.Join(f.TestDataPath, path)) + Expect(err).NotTo(HaveOccurred(), "Failed to load test data from %s", path) + + // Replace namespace in YAML if present + yamlContent := replaceNamespace(string(content), f.namespace) + + err = f.kubectl.Apply(yamlContent) + Expect(err).NotTo(HaveOccurred(), "Failed to apply test data %s in namespace %s", path, f.namespace) +} + +// ApplyTestDataWithoutNamespaceReplacement loads and applies a test manifest WITHOUT namespace replacement +// Use this when you need to apply resources to specific namespaces +func (f *Framework) ApplyTestDataWithoutNamespaceReplacement(path string) { + By(fmt.Sprintf("applying test data without namespace replacement: %s", path)) + + content, err := os.ReadFile(filepath.Join(f.TestDataPath, path)) + Expect(err).NotTo(HaveOccurred(), "Failed to load test data from %s", path) + + // Apply without forcing namespace (YAML contains the correct namespace) + err = f.kubectl.ApplyWithoutNamespaceOverride(string(content)) + Expect(err).NotTo(HaveOccurred(), "Failed to apply test data %s", path) +} + +// replaceNamespace replaces hardcoded namespaces in YAML content +func replaceNamespace(yaml, namespace string) string { + // This is a simple replacement - for production use, proper YAML parsing might be better + // But for tests this is sufficient + lines := []string{} + for _, line := range splitLines(yaml) { + // Replace namespace: with namespace: + if len(line) > 12 && line[:12] == " namespace:" { + lines = append(lines, fmt.Sprintf(" namespace: %s", namespace)) + } else { + lines = append(lines, line) + } + } + return joinLines(lines) +} + +// splitLines splits string by newlines +func splitLines(s string) []string { + return strings.Split(s, "\n") +} + +// joinLines joins lines with newlines +func joinLines(lines []string) string { + return strings.Join(lines, "\n") +} + +// ApplyYAML applies raw YAML content +func (f *Framework) ApplyYAML(yamlContent string) { + err := f.kubectl.Apply(yamlContent) + Expect(err).NotTo(HaveOccurred(), "Failed to apply YAML in namespace %s", f.namespace) +} + +// WaitForDeploymentReady waits for a deployment to be ready +func (f *Framework) WaitForDeploymentReady(name string) { + By(fmt.Sprintf("waiting for deployment %s to be ready", name)) + start := time.Now() + defer func() { + duration := time.Since(start) + f.metrics.DeploymentWaitTime += duration + GinkgoWriter.Printf("⏱️ Deployment %s ready in %v\n", name, duration) + }() + + f.kubectl.WaitForDeploymentReady(name) +} + +// WaitForPipelineValid waits for a pipeline to become valid +func (f *Framework) WaitForPipelineValid(name string) { + By(fmt.Sprintf("waiting for pipeline %s to become valid", name)) + start := time.Now() + defer func() { + duration := time.Since(start) + f.metrics.PipelineValidationTime += duration + GinkgoWriter.Printf("⏱️ Pipeline %s validated in %v\n", name, duration) + }() + + f.kubectl.WaitForPipelineValid(name) +} + +// WaitForPipelineInvalid waits for a pipeline to become invalid (for negative tests) +func (f *Framework) WaitForPipelineInvalid(name string) { + By(fmt.Sprintf("waiting for pipeline %s to become invalid", name)) + f.kubectl.WaitForPipelineInvalid(name) +} + +// GetPipelineStatus retrieves a specific status field from a pipeline +func (f *Framework) GetPipelineStatus(name string, field string) string { + result, err := f.kubectl.GetWithJsonPath("vectorpipeline", name, fmt.Sprintf(".status.%s", field)) + Expect(err).NotTo(HaveOccurred(), + "Failed to get pipeline %s status field %s in namespace %s", name, field, f.namespace) + return result +} + +// GetServicePort retrieves the port of a service +func (f *Framework) GetServicePort(name string) string { + result, err := f.kubectl.GetWithJsonPath("service", name, ".spec.ports[0].port") + Expect(err).NotTo(HaveOccurred(), + "Failed to get service %s port in namespace %s", name, f.namespace) + return result +} + +// TryGetServicePort retrieves the port of a service without failing if not found +func (f *Framework) TryGetServicePort(name string) (string, error) { + return f.kubectl.GetWithJsonPath("service", name, ".spec.ports[0].port") +} + +// CreateMultiplePipelinesFromTemplate creates N pipelines from a template by replacing a placeholder +func (f *Framework) CreateMultiplePipelinesFromTemplate(templatePath, placeholder string, count int) time.Duration { + start := time.Now() + + content, err := os.ReadFile(filepath.Join(f.TestDataPath, templatePath)) + Expect(err).NotTo(HaveOccurred(), "Failed to load template from %s", templatePath) + + template := string(content) + + for i := 1; i <= count; i++ { + pipelineName := fmt.Sprintf("pipeline-%03d", i) + yaml := replaceNamespace(template, f.namespace) + yaml = replacePlaceholder(yaml, placeholder, pipelineName) + + err = f.kubectl.Apply(yaml) + Expect(err).NotTo(HaveOccurred(), + "Failed to apply pipeline %s from template %s in namespace %s", pipelineName, templatePath, f.namespace) + } + + return time.Since(start) +} + +// replacePlaceholder replaces a placeholder in YAML content +func replacePlaceholder(yaml, placeholder, value string) string { + return strings.ReplaceAll(yaml, placeholder, value) +} + +// CountValidPipelines counts how many pipelines are valid in the namespace +func (f *Framework) CountValidPipelines() (int, error) { + result, err := f.kubectl.GetWithJsonPath("vectorpipeline", "", ".items[*].status.configCheckResult") + if err != nil { + return 0, err + } + + if result == "" { + return 0, nil + } + + validCount := 0 + for _, status := range splitFields(result) { + if status == "true" { + validCount++ + } + } + + return validCount, nil +} + +// CountPipelines returns the total number of pipelines in the namespace +func (f *Framework) CountPipelines() (int, error) { + result, err := f.kubectl.GetAll("vectorpipeline", "") + if err != nil { + return 0, err + } + + if result == "" { + return 0, nil + } + + return len(splitFields(result)), nil +} + +// CountServicesContaining counts services whose name contains the given substring +func (f *Framework) CountServicesContaining(substring string) (int, error) { + result, err := f.kubectl.GetAll("service", "") + if err != nil { + return 0, err + } + + if result == "" { + return 0, nil + } + + count := 0 + for _, svc := range splitFields(result) { + if svc != "" && containsSubstring(svc, substring) { + count++ + } + } + + return count, nil +} + +// containsSubstring checks if a string contains a substring +func containsSubstring(s, substr string) bool { + return strings.Contains(s, substr) +} + +// ExpectServiceExists verifies that a service exists +func (f *Framework) ExpectServiceExists(name string) { + By(fmt.Sprintf("verifying service %s exists", name)) + _, err := f.kubectl.Get("service", name) + Expect(err).NotTo(HaveOccurred(), + "Expected service %s to exist in namespace %s", name, f.namespace) +} + +// CountServicesWithLabel counts services matching a label selector +func (f *Framework) CountServicesWithLabel(labelSelector string) int { + result, err := f.kubectl.GetAll("service", labelSelector) + Expect(err).NotTo(HaveOccurred(), + "Failed to get services with label %s in namespace %s", labelSelector, f.namespace) + + if result == "" { + return 0 + } + + count := 0 + for _, svc := range splitFields(result) { + if svc != "" { + count++ + } + } + return count +} + +// WaitForServiceCount waits for a specific number of services +func (f *Framework) WaitForServiceCount(labelSelector string, expectedCount int, timeout time.Duration) { + By(fmt.Sprintf("waiting for %d services with label %s", expectedCount, labelSelector)) + f.kubectl.WaitForServiceCount(labelSelector, expectedCount, timeout) +} + +// PrintMetrics prints timing metrics for the test +func (f *Framework) PrintMetrics() { + GinkgoWriter.Println("\n📊 Test Metrics:") + GinkgoWriter.Printf(" Setup: %v\n", f.metrics.SetupTime) + GinkgoWriter.Printf(" Deployment Wait: %v\n", f.metrics.DeploymentWaitTime) + GinkgoWriter.Printf(" Pipeline Validation: %v\n", f.metrics.PipelineValidationTime) + GinkgoWriter.Printf(" Cleanup: %v\n", f.metrics.CleanupTime) + GinkgoWriter.Printf(" Total: %v\n", f.metrics.SetupTime+f.metrics.DeploymentWaitTime+f.metrics.PipelineValidationTime+f.metrics.CleanupTime) +} + +// splitFields splits space-separated fields +func splitFields(s string) []string { + return strings.Fields(s) +} + +// GetPodLogs retrieves logs from a pod +func (f *Framework) GetPodLogs(podName string) (string, error) { + return f.kubectl.GetPodLogs(podName) +} + +// GetPodLogsTail retrieves the last N lines of logs from a pod +func (f *Framework) GetPodLogsTail(podName string, lines int) (string, error) { + return f.kubectl.GetPodLogsTail(podName, lines) +} + +// GetPodsByLabel retrieves pod names matching a label selector +func (f *Framework) GetPodsByLabel(labelSelector string) ([]string, error) { + return f.kubectl.GetPodsByLabel(labelSelector) +} + +// WaitForPodReady waits for a pod to become ready +func (f *Framework) WaitForPodReady(podName string) { + By(fmt.Sprintf("waiting for pod %s to be ready", podName)) + err := f.kubectl.WaitForPodReady(podName, "2m") + Expect(err).NotTo(HaveOccurred(), "Pod %s did not become ready in namespace %s", podName, f.namespace) +} + +// GetAggregatorPods retrieves aggregator pod names for a given aggregator +func (f *Framework) GetAggregatorPods(aggregatorName string) ([]string, error) { + // Aggregator pods use instance label to identify which aggregator they belong to + labelSelector := fmt.Sprintf("app.kubernetes.io/instance=%s,app.kubernetes.io/component=Aggregator", aggregatorName) + return f.kubectl.GetPodsByLabel(labelSelector) +} + +// GetAgentPods retrieves agent pod names +func (f *Framework) GetAgentPods(vectorName string) ([]string, error) { + // Agent pods use instance label and component=Agent + labelSelector := fmt.Sprintf("app.kubernetes.io/instance=%s,app.kubernetes.io/component=Agent", vectorName) + return f.kubectl.GetPodsByLabel(labelSelector) +} + +// GetPipelineAnnotation retrieves a specific annotation from a pipeline +func (f *Framework) GetPipelineAnnotation(name string, annotationKey string) string { + jsonPath := fmt.Sprintf(".metadata.annotations['%s']", annotationKey) + result, err := f.kubectl.GetWithJsonPath("vectorpipeline", name, jsonPath) + if err != nil { + // Annotation might not exist, which is expected in some cases + return "" + } + return result +} + +// VerifyAgentHasPipeline verifies that the agent Secret contains the specified pipeline +func (f *Framework) VerifyAgentHasPipeline(vectorName, pipelineName string) error { + return f.VerifyAgentHasPipelineInNamespace(vectorName, pipelineName, f.namespace) +} + +// VerifyAgentHasPipelineInNamespace verifies that an agent Secret contains the specified pipeline from a specific namespace +func (f *Framework) VerifyAgentHasPipelineInNamespace(vectorName, pipelineName, namespace string) error { + // Get the agent's vector config from the Secret + // The config is stored in a Secret with name pattern: {vectorName}-agent + secretName := fmt.Sprintf("%s-agent", vectorName) + + // Get base64-encoded config from Secret + encodedConfig, err := f.kubectl.GetWithJsonPath("secret", secretName, ".data['agent\\.json']") + if err != nil { + return fmt.Errorf("failed to get agent secret %s: %w", secretName, err) + } + + if encodedConfig == "" { + return fmt.Errorf("agent secret %s has no agent.json data", secretName) + } + + // Check size before decoding to prevent DoS via large payloads + maxEncodedSize := MaxConfigSize * 4 / 3 + if len(encodedConfig) > maxEncodedSize { + return fmt.Errorf("config too large: %d bytes (max %d bytes)", len(encodedConfig), maxEncodedSize) + } + + // Decode base64 + configBytes, err := base64.StdEncoding.DecodeString(encodedConfig) + if err != nil { + return fmt.Errorf("failed to decode base64 config from secret %s: %w", secretName, err) + } + config := string(configBytes) + + if config == "" { + return fmt.Errorf("agent config is empty after decoding") + } + + // Check if the pipeline name appears in the config + // In normal mode, pipeline components are prefixed with namespace-pipelinename- + expectedPrefix := fmt.Sprintf("%s-%s-", namespace, pipelineName) + if !strings.Contains(config, expectedPrefix) { + return fmt.Errorf("pipeline %s not found in agent config (expected prefix: %s)", pipelineName, expectedPrefix) + } + + return nil +} + +// VerifyAgentHasClusterPipeline verifies that an agent Secret contains the specified ClusterVectorPipeline +func (f *Framework) VerifyAgentHasClusterPipeline(vectorName, pipelineName string) error { + // Get the agent's vector config from the Secret + secretName := fmt.Sprintf("%s-agent", vectorName) + + // Get base64-encoded config from Secret + encodedConfig, err := f.kubectl.GetWithJsonPath("secret", secretName, ".data['agent\\.json']") + if err != nil { + return fmt.Errorf("failed to get agent secret %s: %w", secretName, err) + } + + if encodedConfig == "" { + return fmt.Errorf("agent secret %s has no agent.json data", secretName) + } + + // Check size before decoding to prevent DoS via large payloads + maxEncodedSize := MaxConfigSize * 4 / 3 + if len(encodedConfig) > maxEncodedSize { + return fmt.Errorf("config too large: %d bytes (max %d bytes)", len(encodedConfig), maxEncodedSize) + } + + // Decode base64 + configBytes, err := base64.StdEncoding.DecodeString(encodedConfig) + if err != nil { + return fmt.Errorf("failed to decode base64 config from secret %s: %w", secretName, err) + } + config := string(configBytes) + + if config == "" { + return fmt.Errorf("agent config is empty after decoding") + } + + // Check if the cluster pipeline name appears in the config + // ClusterVectorPipeline components are prefixed with only pipelinename- (no namespace prefix) + expectedPrefix := fmt.Sprintf("%s-", pipelineName) + if !strings.Contains(config, expectedPrefix) { + return fmt.Errorf("cluster pipeline %s not found in agent config (expected prefix: %s)", pipelineName, expectedPrefix) + } + + return nil +} + +// VerifyAggregatorHasPipeline verifies that an aggregator Secret contains the specified pipeline +func (f *Framework) VerifyAggregatorHasPipeline(aggregatorName, pipelineName string) error { + // Get the aggregator's vector config from the Secret + // The config is stored in a Secret with name pattern: {aggregatorName}-aggregator + secretName := fmt.Sprintf("%s-aggregator", aggregatorName) + + // Get base64-encoded config from Secret + encodedConfig, err := f.kubectl.GetWithJsonPath("secret", secretName, ".data['config\\.json']") + if err != nil { + return fmt.Errorf("failed to get aggregator secret %s: %w", secretName, err) + } + + if encodedConfig == "" { + return fmt.Errorf("aggregator secret %s has no config.json data", secretName) + } + + // Check size before decoding to prevent DoS via large payloads + maxEncodedSize := MaxConfigSize * 4 / 3 + if len(encodedConfig) > maxEncodedSize { + return fmt.Errorf("config too large: %d bytes (max %d bytes)", len(encodedConfig), maxEncodedSize) + } + + // Decode base64 + configBytes, err := base64.StdEncoding.DecodeString(encodedConfig) + if err != nil { + return fmt.Errorf("failed to decode base64 config from secret %s: %w", secretName, err) + } + config := string(configBytes) + + if config == "" { + return fmt.Errorf("aggregator %s config is empty after decoding", aggregatorName) + } + + // Check if the pipeline name appears in the config + expectedPrefix := fmt.Sprintf("%s-%s-", f.namespace, pipelineName) + if !strings.Contains(config, expectedPrefix) { + return fmt.Errorf("pipeline %s not found in aggregator %s config (expected prefix: %s)", + pipelineName, aggregatorName, expectedPrefix) + } + + return nil +} + +// ApplyTestDataWithVars loads and applies a test manifest with variable substitution +func (f *Framework) ApplyTestDataWithVars(path string, vars map[string]string) { + By(fmt.Sprintf("applying test data with vars: %s", path)) + + content, err := os.ReadFile(filepath.Join(f.TestDataPath, path)) + Expect(err).NotTo(HaveOccurred(), "Failed to load test data from %s", path) + + // Replace namespace in YAML + yamlContent := replaceNamespace(string(content), f.namespace) + + // Replace variables + for placeholder, value := range vars { + yamlContent = strings.ReplaceAll(yamlContent, placeholder, value) + } + + err = f.kubectl.Apply(yamlContent) + Expect(err).NotTo(HaveOccurred(), "Failed to apply test data %s in namespace %s", path, f.namespace) +} + +// DeleteResource deletes a Kubernetes resource +func (f *Framework) DeleteResource(kind, name string) { + By(fmt.Sprintf("deleting %s %s", kind, name)) + err := f.kubectl.Delete(kind, name) + Expect(err).NotTo(HaveOccurred(), "Failed to delete %s %s in namespace %s", kind, name, f.namespace) +} + +// WaitForPodReadyInNamespace waits for a pod to become ready in a specific namespace +func (f *Framework) WaitForPodReadyInNamespace(podName, namespace string) { + By(fmt.Sprintf("waiting for pod %s to be ready in namespace %s", podName, namespace)) + client := kubectl.NewClient(namespace) + err := client.WaitForPodReady(podName, "2m") + Expect(err).NotTo(HaveOccurred(), "Pod %s did not become ready in namespace %s", podName, namespace) +} + +// WaitForPipelineValidInNamespace waits for a pipeline to become valid in a specific namespace +func (f *Framework) WaitForPipelineValidInNamespace(name, namespace string) { + By(fmt.Sprintf("waiting for pipeline %s to become valid in namespace %s", name, namespace)) + start := time.Now() + defer func() { + duration := time.Since(start) + GinkgoWriter.Printf("⏱️ Pipeline %s validated in %v (namespace: %s)\n", name, duration, namespace) + }() + + client := kubectl.NewClient(namespace) + client.WaitForPipelineValid(name) +} + +// GetPipelineAnnotationInNamespace retrieves a specific annotation from a pipeline in a specific namespace +func (f *Framework) GetPipelineAnnotationInNamespace(name, namespace, annotationKey string) string { + jsonPath := fmt.Sprintf(".metadata.annotations['%s']", annotationKey) + client := kubectl.NewClient(namespace) + result, err := client.GetWithJsonPath("vectorpipeline", name, jsonPath) + if err != nil { + // Annotation might not exist, which is expected in some cases + return "" + } + return result +} + +// WaitForClusterPipelineValid waits for a ClusterVectorPipeline to become valid +func (f *Framework) WaitForClusterPipelineValid(name string) { + By(fmt.Sprintf("waiting for ClusterVectorPipeline %s to become valid", name)) + start := time.Now() + defer func() { + duration := time.Since(start) + GinkgoWriter.Printf("⏱️ ClusterVectorPipeline %s validated in %v\n", name, duration) + }() + + // ClusterVectorPipeline is cluster-scoped, so we use a client without namespace + client := kubectl.NewClient("") + Eventually(func() string { + result, _ := client.GetWithJsonPath("clustervectorpipeline", name, ".status.configCheckResult") + return result + }, config.PipelineValidTimeout, config.DefaultPollInterval).Should(Equal("true"), + "ClusterVectorPipeline %s did not become valid", name) +} + +// GetClusterPipelineAnnotation retrieves a specific annotation from a ClusterVectorPipeline +func (f *Framework) GetClusterPipelineAnnotation(name, annotationKey string) string { + jsonPath := fmt.Sprintf(".metadata.annotations['%s']", annotationKey) + client := kubectl.NewClient("") + result, err := client.GetWithJsonPath("clustervectorpipeline", name, jsonPath) + if err != nil { + // Annotation might not exist, which is expected in some cases + return "" + } + return result +} + +// GetClusterPipelineStatus retrieves a specific status field from a ClusterVectorPipeline +func (f *Framework) GetClusterPipelineStatus(name, field string) string { + client := kubectl.NewClient("") + result, err := client.GetWithJsonPath("clustervectorpipeline", name, fmt.Sprintf(".status.%s", field)) + Expect(err).NotTo(HaveOccurred(), + "Failed to get ClusterVectorPipeline %s status field %s", name, field) + return result +} + +// Kubectl returns the kubectl client +func (f *Framework) Kubectl() *kubectl.Client { + return f.kubectl +} + +// GetRegisteredFramework retrieves a framework by namespace +// Used by artifact collector to access kubectl client and namespace +func GetRegisteredFramework(namespace string) (*Framework, bool) { + value, ok := frameworkRegistry.Load(namespace) + if !ok { + return nil, false + } + return value.(*Framework), true +} + +// GetFrameworkRegistry returns the framework registry for iteration +// Used by ReportAfterEach to find frameworks when namespace is not known +func GetFrameworkRegistry() *sync.Map { + return &frameworkRegistry +} + +// GetSecret retrieves a Secret by name in the framework's namespace +func (f *Framework) GetSecret(name string) (map[string][]byte, error) { + cmd := fmt.Sprintf("kubectl get secret %s -n %s -o json", name, f.namespace) + output, err := exec.Command("sh", "-c", cmd).CombinedOutput() + if err != nil { + return nil, fmt.Errorf("failed to get secret %s: %w, output: %s", name, err, string(output)) + } + + var secret struct { + Data map[string]string `json:"data"` + } + if err := json.Unmarshal(output, &secret); err != nil { + return nil, fmt.Errorf("failed to unmarshal secret: %w", err) + } + + // Decode base64 data + decodedData := make(map[string][]byte) + maxEncodedSize := MaxConfigSize * 4 / 3 + for k, v := range secret.Data { + // Check size before decoding to prevent DoS via large payloads + if len(v) > maxEncodedSize { + return nil, fmt.Errorf("secret data for key %s too large: %d bytes (max %d bytes)", k, len(v), maxEncodedSize) + } + + decoded, err := base64.StdEncoding.DecodeString(v) + if err != nil { + return nil, fmt.Errorf("failed to decode secret data for key %s: %w", k, err) + } + decodedData[k] = decoded + } + + return decodedData, nil +} + +// GetDeployment retrieves a Deployment by name in the framework's namespace +func (f *Framework) GetDeployment(name string) (*DeploymentInfo, error) { + cmd := fmt.Sprintf("kubectl get deployment %s -n %s -o json", name, f.namespace) + output, err := exec.Command("sh", "-c", cmd).CombinedOutput() + if err != nil { + return nil, fmt.Errorf("failed to get deployment %s: %w, output: %s", name, err, string(output)) + } + + var deployment struct { + Spec struct { + Template struct { + Spec struct { + InitContainers []struct { + Name string `json:"name"` + } `json:"initContainers"` + Containers []struct { + Name string `json:"name"` + } `json:"containers"` + } `json:"spec"` + } `json:"template"` + } `json:"spec"` + } + if err := json.Unmarshal(output, &deployment); err != nil { + return nil, fmt.Errorf("failed to unmarshal deployment: %w", err) + } + + info := &DeploymentInfo{ + InitContainers: make([]string, 0), + Containers: make([]string, 0), + } + + for _, c := range deployment.Spec.Template.Spec.InitContainers { + info.InitContainers = append(info.InitContainers, c.Name) + } + for _, c := range deployment.Spec.Template.Spec.Containers { + info.Containers = append(info.Containers, c.Name) + } + + return info, nil +} + +// DeploymentInfo contains simplified deployment information +type DeploymentInfo struct { + InitContainers []string + Containers []string +} + +// RecordStep records a test step for reproducibility +func (f *Framework) RecordStep(step recorder.TestStep) { + if f.recorder != nil { + f.recorder.RecordStep(step) + } +} + +// SetTestName sets the current test name in the recorder +func (f *Framework) SetTestName(name string) { + if f.recorder != nil { + f.recorder.SetTestName(name) + } +} + +// ExportTestPlan exports the recorded test plan to files +func (f *Framework) ExportTestPlan() { + if f.recorder == nil { + return + } + + // Get current test spec info + spec := CurrentSpecReport() + testName := buildTestName(spec) + + if testName == "" { + testName = "unknown-test" + } + + f.recorder.SetTestName(testName) + + // In dry-run mode, print to stdout + if f.dryRun { + fmt.Println("\n" + strings.Repeat("=", 80)) + fmt.Printf("Test Plan: %s\n", testName) + fmt.Println(strings.Repeat("=", 80)) + fmt.Println(f.recorder.ExportAsShellScript()) + return + } + + // Otherwise, save to artifact directory if it exists + artifactDir := os.Getenv("ARTIFACT_DIR") + if artifactDir == "" { + artifactDir = "test/e2e/results/test-plans" + } + + // Create directory if it doesn't exist + if err := os.MkdirAll(artifactDir, 0755); err != nil { + fmt.Printf("Warning: failed to create artifact directory: %v\n", err) + return + } + + // Sanitize test name for filename + safeTestName := strings.ReplaceAll(testName, " ", "-") + safeTestName = strings.ReplaceAll(safeTestName, "/", "-") + + // Save as shell script + scriptPath := filepath.Join(artifactDir, fmt.Sprintf("%s.sh", safeTestName)) + scriptContent := f.recorder.ExportAsShellScript() + if err := os.WriteFile(scriptPath, []byte(scriptContent), 0755); err != nil { + fmt.Printf("Warning: failed to write test plan script: %v\n", err) + } else { + fmt.Printf("✓ Test plan saved to: %s\n", scriptPath) + } + + // Save as markdown + mdPath := filepath.Join(artifactDir, fmt.Sprintf("%s.md", safeTestName)) + mdContent := f.recorder.ExportAsMarkdown() + if err := os.WriteFile(mdPath, []byte(mdContent), 0644); err != nil { + fmt.Printf("Warning: failed to write test plan markdown: %v\n", err) + } else { + fmt.Printf("✓ Test plan documentation saved to: %s\n", mdPath) + } +} + +// buildTestName constructs a test name from the spec report +func buildTestName(spec types.SpecReport) string { + hierarchy := spec.ContainerHierarchyTexts + leaf := spec.LeafNodeText + + if len(hierarchy) > 0 { + return strings.Join(append(hierarchy, leaf), " ") + } + return leaf +} + +// ToContext stores the framework in the given context +// This allows framework to be passed through context chains if needed +func (f *Framework) ToContext(ctx context.Context) context.Context { + return context.WithValue(ctx, FrameworkContextKey{}, f) +} + +// FromContext retrieves a framework from the given context +// Returns nil if no framework is stored in the context +func FromContext(ctx context.Context) *Framework { + if f, ok := ctx.Value(FrameworkContextKey{}).(*Framework); ok { + return f + } + return nil +} + +// FromReportEntries retrieves a framework from Ginkgo report entries +// This is the preferred way to access framework in ReportAfterEach +// Returns nil if no framework entry is found +func FromReportEntries(entries []types.ReportEntry) *Framework { + for _, entry := range entries { + if entry.Name == frameworkReportEntryName { + // GetRawValue() returns the underlying interface{} value + if f, ok := entry.Value.GetRawValue().(*Framework); ok { + return f + } + } + } + return nil +} + +// LogOptions contains options for retrieving pod logs +type LogOptions struct { + // Container name to get logs from (empty for default container) + Container string + // TailLines limits the number of lines from the end of the logs + TailLines int + // SinceSeconds returns logs newer than a relative duration (in seconds) + SinceSeconds int +} + +// WaitForLogsContaining waits for a substring to appear in pod logs +// Returns nil if found, error if timeout occurs +func (f *Framework) WaitForLogsContaining(podName, substring string, timeout time.Duration) error { + fmt.Fprintf(GinkgoWriter, "⏳ Waiting for logs in pod %s to contain: %s\n", podName, substring) + + var lastLogs string + startTime := time.Now() + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + err := wait.PollUntilContextTimeout(ctx, time.Second, timeout, true, func(ctx context.Context) (bool, error) { + logs, err := f.GetPodLogs(podName) + if err != nil { + // Not a critical error, pod might not exist yet or be starting + return false, nil + } + lastLogs = logs + return strings.Contains(logs, substring), nil + }) + + if err != nil { + elapsed := time.Since(startTime) + // Truncate logs if too long + truncatedLogs := lastLogs + if len(lastLogs) > 500 { + truncatedLogs = lastLogs[len(lastLogs)-500:] + "\n... (truncated)" + } + return fmt.Errorf("timeout waiting for logs to contain '%s' in pod %s after %v. Last logs:\n%s", + substring, podName, elapsed, truncatedLogs) + } + + elapsed := time.Since(startTime) + fmt.Fprintf(GinkgoWriter, "✓ Found expected substring in pod %s logs (took %v)\n", podName, elapsed) + return nil +} + +// WaitForLogsMatching waits for a regex pattern to match in pod logs +// Returns nil if match found, error if timeout occurs or pattern is invalid +func (f *Framework) WaitForLogsMatching(podName, pattern string, timeout time.Duration) error { + fmt.Fprintf(GinkgoWriter, "⏳ Waiting for logs in pod %s to match pattern: %s\n", podName, pattern) + + // Compile regex pattern + re, err := regexp.Compile(pattern) + if err != nil { + return fmt.Errorf("invalid regex pattern '%s': %w", pattern, err) + } + + var lastLogs string + startTime := time.Now() + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + err = wait.PollUntilContextTimeout(ctx, time.Second, timeout, true, func(ctx context.Context) (bool, error) { + logs, err := f.GetPodLogs(podName) + if err != nil { + // Not a critical error, pod might not exist yet or be starting + return false, nil + } + lastLogs = logs + return re.MatchString(logs), nil + }) + + if err != nil { + elapsed := time.Since(startTime) + // Truncate logs if too long + truncatedLogs := lastLogs + if len(lastLogs) > 500 { + truncatedLogs = lastLogs[len(lastLogs)-500:] + "\n... (truncated)" + } + return fmt.Errorf("timeout waiting for logs to match pattern '%s' in pod %s after %v. Last logs:\n%s", + pattern, podName, elapsed, truncatedLogs) + } + + elapsed := time.Since(startTime) + fmt.Fprintf(GinkgoWriter, "✓ Found pattern match in pod %s logs (took %v)\n", podName, elapsed) + return nil +} + +// AssertNoLogsContaining verifies that a substring does NOT appear in pod logs +// Returns nil if substring is absent for the entire check duration, error otherwise +func (f *Framework) AssertNoLogsContaining(podName, substring string, checkDuration time.Duration) error { + fmt.Fprintf(GinkgoWriter, "⏳ Verifying logs in pod %s do NOT contain: %s (checking for %v)\n", + podName, substring, checkDuration) + + var foundLogs string + startTime := time.Now() + ctx, cancel := context.WithTimeout(context.Background(), checkDuration) + defer cancel() + + err := wait.PollUntilContextTimeout(ctx, time.Second, checkDuration, true, func(ctx context.Context) (bool, error) { + logs, err := f.GetPodLogs(podName) + if err != nil { + // Pod might not exist yet, which is acceptable for negative checks + return false, nil + } + + if strings.Contains(logs, substring) { + foundLogs = logs + // Found the substring - this is a failure for negative assertion + return true, nil + } + + // Continue checking + return false, nil + }) + + // For Consistently-style checks, we want to ensure the substring was NEVER found + if wait.Interrupted(err) { + // Timeout means we successfully verified absence for the entire duration + elapsed := time.Since(startTime) + fmt.Fprintf(GinkgoWriter, "✓ Verified substring absent in pod %s logs for %v\n", podName, elapsed) + return nil + } + + if foundLogs != "" { + // We found the substring - this is an error + truncatedLogs := foundLogs + if len(foundLogs) > 500 { + truncatedLogs = foundLogs[len(foundLogs)-500:] + "\n... (truncated)" + } + return fmt.Errorf("found unexpected substring '%s' in pod %s logs. Last logs:\n%s", + substring, podName, truncatedLogs) + } + + // Other error occurred + if err != nil { + return fmt.Errorf("error while checking logs for pod %s: %w", podName, err) + } + + return nil +} + +// GetPodLogsWithOptions retrieves logs from a pod with custom options +func (f *Framework) GetPodLogsWithOptions(podName string, opts LogOptions) (string, error) { + if opts.Container != "" || opts.TailLines > 0 || opts.SinceSeconds > 0 { + // Use kubectl client methods if options are specified + if opts.TailLines > 0 { + return f.kubectl.GetPodLogsTail(podName, opts.TailLines) + } + // For other options, we'd need to add more kubectl methods + // For now, fall back to basic GetPodLogs + return f.kubectl.GetPodLogs(podName) + } + + return f.kubectl.GetPodLogs(podName) +} diff --git a/test/e2e/framework/kubectl/client.go b/test/e2e/framework/kubectl/client.go new file mode 100644 index 00000000..6d13ca7f --- /dev/null +++ b/test/e2e/framework/kubectl/client.go @@ -0,0 +1,485 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kubectl + +import ( + "encoding/json" + "fmt" + "log" + "os/exec" + "strings" + "time" + + corev1 "k8s.io/api/core/v1" + + "github.com/kaasops/vector-operator/test/utils" +) + +// Client provides convenient kubectl operations +type Client struct { + namespace string +} + +// NewClient creates a new kubectl client for the given namespace +func NewClient(namespace string) *Client { + return &Client{namespace: namespace} +} + +// Apply applies YAML content to the cluster with explicit namespace override +// This ensures resources are created in the correct test namespace +func (c *Client) Apply(yamlContent string) error { + // Validate namespace to prevent command injection + if err := ValidateNamespace(c.namespace); err != nil { + return fmt.Errorf("namespace validation failed: %w", err) + } + + // Log command for audit and reproducibility + log.Printf("KUBECTL_CMD: kubectl apply -f - -n %s", c.namespace) + + cmd := exec.Command("kubectl", "apply", "-f", "-", "-n", c.namespace) + cmd.Stdin = strings.NewReader(yamlContent) + output, err := utils.Run(cmd) + + // Log kubectl output for debugging (helps catch namespace mismatches) + if len(output) > 0 { + fmt.Printf("kubectl apply: %s\n", string(output)) + } + + return err +} + +// ApplyWithoutNamespaceOverride applies YAML content without forcing namespace +// Use this when the YAML already contains the correct namespace field +func (c *Client) ApplyWithoutNamespaceOverride(yamlContent string) error { + // Log command for audit and reproducibility + log.Printf("KUBECTL_CMD: kubectl apply -f -") + + cmd := exec.Command("kubectl", "apply", "-f", "-") + cmd.Stdin = strings.NewReader(yamlContent) + output, err := utils.Run(cmd) + + // Log kubectl output for debugging + if len(output) > 0 { + fmt.Printf("kubectl apply: %s\n", string(output)) + } + + return err +} + +// Get retrieves a resource by name and type +func (c *Client) Get(resourceType, name string) ([]byte, error) { + // Validate parameters to prevent command injection + if err := ValidateNamespace(c.namespace); err != nil { + return nil, fmt.Errorf("namespace validation failed: %w", err) + } + if err := ValidateResourceType(resourceType); err != nil { + return nil, fmt.Errorf("resource type validation failed: %w", err) + } + if err := ValidateResourceName(name); err != nil { + return nil, fmt.Errorf("resource name validation failed: %w", err) + } + + // Log command for audit and reproducibility + log.Printf("KUBECTL_CMD: kubectl get %s %s -n %s", resourceType, name, c.namespace) + + cmd := exec.Command("kubectl", "get", resourceType, name, "-n", c.namespace) + return utils.Run(cmd) +} + +// GetWithJsonPath retrieves a specific field from a resource +// If name is empty, retrieves from all resources of the given type +func (c *Client) GetWithJsonPath(resourceType, name, jsonPath string) (string, error) { + // Validate parameters to prevent command injection + if err := ValidateNamespace(c.namespace); err != nil { + return "", fmt.Errorf("namespace validation failed: %w", err) + } + if err := ValidateResourceType(resourceType); err != nil { + return "", fmt.Errorf("resource type validation failed: %w", err) + } + if name != "" { + if err := ValidateResourceName(name); err != nil { + return "", fmt.Errorf("resource name validation failed: %w", err) + } + } + if err := ValidateJSONPath(jsonPath); err != nil { + return "", fmt.Errorf("jsonPath validation failed: %w", err) + } + + // Build command args based on whether name is specified + args := []string{"get", resourceType} + + // Only include name if it's not empty (empty name means get all resources) + if name != "" { + args = append(args, name) + } + + args = append(args, "-n", c.namespace, "-o", fmt.Sprintf("jsonpath={%s}", jsonPath)) + + // Log command for audit and reproducibility + log.Printf("KUBECTL_CMD: kubectl %s", strings.Join(args, " ")) + + cmd := exec.Command("kubectl", args...) + output, err := utils.Run(cmd) + return string(output), err +} + +// GetAll retrieves all resources of a type with optional label selector +func (c *Client) GetAll(resourceType string, labelSelector string) (string, error) { + // Validate parameters to prevent command injection + if err := ValidateNamespace(c.namespace); err != nil { + return "", fmt.Errorf("namespace validation failed: %w", err) + } + if err := ValidateResourceType(resourceType); err != nil { + return "", fmt.Errorf("resource type validation failed: %w", err) + } + if labelSelector != "" { + if err := ValidateLabelSelector(labelSelector); err != nil { + return "", fmt.Errorf("label selector validation failed: %w", err) + } + } + + args := []string{"get", resourceType, "-n", c.namespace} + if labelSelector != "" { + args = append(args, "-l", labelSelector) + } + args = append(args, "-o", "jsonpath={.items[*].metadata.name}") + + // Log command for audit and reproducibility + log.Printf("KUBECTL_CMD: kubectl %s", strings.Join(args, " ")) + + cmd := exec.Command("kubectl", args...) + output, err := utils.Run(cmd) + return string(output), err +} + +// Wait waits for a resource condition +func (c *Client) Wait(resourceType, name, condition string, timeout string) error { + // Validate parameters to prevent command injection + if err := ValidateNamespace(c.namespace); err != nil { + return fmt.Errorf("namespace validation failed: %w", err) + } + if err := ValidateResourceType(resourceType); err != nil { + return fmt.Errorf("resource type validation failed: %w", err) + } + if err := ValidateResourceName(name); err != nil { + return fmt.Errorf("resource name validation failed: %w", err) + } + if err := ValidateTimeout(timeout); err != nil { + return fmt.Errorf("timeout validation failed: %w", err) + } + + // Log command for audit and reproducibility + log.Printf("KUBECTL_CMD: kubectl wait --for=%s --timeout=%s %s/%s -n %s", condition, timeout, resourceType, name, c.namespace) + + cmd := exec.Command("kubectl", "wait", + fmt.Sprintf("--for=%s", condition), + fmt.Sprintf("--timeout=%s", timeout), + fmt.Sprintf("%s/%s", resourceType, name), + "-n", c.namespace) + _, err := utils.Run(cmd) + return err +} + +// Delete deletes a resource +func (c *Client) Delete(resourceType, name string) error { + // Validate parameters to prevent command injection + if err := ValidateNamespace(c.namespace); err != nil { + return fmt.Errorf("namespace validation failed: %w", err) + } + if err := ValidateResourceType(resourceType); err != nil { + return fmt.Errorf("resource type validation failed: %w", err) + } + if err := ValidateResourceName(name); err != nil { + return fmt.Errorf("resource name validation failed: %w", err) + } + + // Log command for audit and reproducibility + log.Printf("KUBECTL_CMD: kubectl delete %s %s -n %s", resourceType, name, c.namespace) + + cmd := exec.Command("kubectl", "delete", resourceType, name, "-n", c.namespace) + _, err := utils.Run(cmd) + return err +} + +// CreateNamespace creates a namespace +func CreateNamespace(name string) error { + // Validate namespace to prevent command injection + if err := ValidateNamespace(name); err != nil { + return fmt.Errorf("namespace validation failed: %w", err) + } + + // Log command for audit and reproducibility + log.Printf("KUBECTL_CMD: kubectl create ns %s", name) + + cmd := exec.Command("kubectl", "create", "ns", name) + _, err := utils.Run(cmd) + return err +} + +// GetNamespace retrieves namespace information +func GetNamespace(name string) (*corev1.Namespace, error) { + // Validate namespace to prevent command injection + if err := ValidateNamespace(name); err != nil { + return nil, fmt.Errorf("namespace validation failed: %w", err) + } + + // Log command for audit and reproducibility + log.Printf("KUBECTL_CMD: kubectl get ns %s -o json", name) + + cmd := exec.Command("kubectl", "get", "ns", name, "-o", "json") + output, err := cmd.Output() + if err != nil { + return nil, err + } + + var ns corev1.Namespace + if err := json.Unmarshal(output, &ns); err != nil { + return nil, fmt.Errorf("failed to parse namespace JSON: %w", err) + } + + return &ns, nil +} + +// DeleteNamespace deletes a namespace with retry and force delete fallback +// Handles CRD resources with finalizers to prevent stuck namespaces +func DeleteNamespace(name string, timeout string) error { + // Validate parameters to prevent command injection + if err := ValidateNamespace(name); err != nil { + return fmt.Errorf("namespace validation failed: %w", err) + } + if err := ValidateTimeout(timeout); err != nil { + return fmt.Errorf("timeout validation failed: %w", err) + } + + // Log command for audit and reproducibility + log.Printf("KUBECTL_CMD: kubectl delete ns %s --timeout=%s", name, timeout) + + // Parse timeout duration for wait logic + timeoutDuration, err := parseDuration(timeout) + if err != nil { + return fmt.Errorf("invalid timeout format: %w", err) + } + + // First try: normal delete + cmd := exec.Command("kubectl", "delete", "ns", name, fmt.Sprintf("--timeout=%s", timeout)) + _, err = utils.Run(cmd) + if err == nil { + // Wait for namespace to actually disappear + return waitForNamespaceDeletion(name, timeoutDuration) + } + + // If normal delete fails or times out, force cleanup CRD resources first + fmt.Printf("⚠️ Namespace %s deletion failed, attempting force cleanup\n", name) + + // Clean up operator CRD resources that might have finalizers + crdTypes := []string{ + "vectorpipeline", + "vectoraggregator", + "vector", + "clustervectorpipeline", + "clustervectoraggregator", + } + + for _, crdType := range crdTypes { + // Get all resources of this type + cmd := exec.Command("kubectl", "get", crdType, "-n", name, "-o", "name") + output, err := cmd.Output() + if err != nil { + continue // Resource type doesn't exist or no resources, skip + } + + resources := strings.Fields(string(output)) + for _, resource := range resources { + // Remove finalizers + patchCmd := exec.Command("kubectl", "patch", resource, "-n", name, + "-p", `{"metadata":{"finalizers":[]}}`, + "--type=merge") + _ = patchCmd.Run() // Ignore errors + + // Force delete + deleteCmd := exec.Command("kubectl", "delete", resource, "-n", name, + "--grace-period=0", "--force") + _ = deleteCmd.Run() // Ignore errors + } + } + + // Remove namespace finalizers + _ = exec.Command("kubectl", "patch", "ns", name, + "-p", `{"metadata":{"finalizers":[]}}`, + "--type=merge").Run() + + // Then force delete namespace with shorter timeout + log.Printf("KUBECTL_CMD: kubectl delete ns %s --grace-period=0 --force --timeout=10s", name) + cmd = exec.Command("kubectl", "delete", "ns", name, + "--grace-period=0", "--force", "--timeout=10s") + _, _ = utils.Run(cmd) + + // Wait for namespace to actually disappear, even after force delete + waitErr := waitForNamespaceDeletion(name, 30*time.Second) + if waitErr != nil { + fmt.Printf("⚠️ Namespace %s still exists after cleanup, continuing anyway\n", name) + return nil // Don't fail the test - namespace will be cleaned up eventually + } + + return nil +} + +// waitForNamespaceDeletion waits for a namespace to be fully deleted +func waitForNamespaceDeletion(name string, timeout time.Duration) error { + deadline := time.Now().Add(timeout) + pollInterval := 2 * time.Second + + for time.Now().Before(deadline) { + // Try to get the namespace + cmd := exec.Command("kubectl", "get", "ns", name) + err := cmd.Run() + if err != nil { + // Namespace not found - deletion successful + log.Printf("KUBECTL_CMD: namespace %s successfully deleted", name) + return nil + } + + // Namespace still exists, wait and retry + time.Sleep(pollInterval) + } + + return fmt.Errorf("namespace %s still exists after %v", name, timeout) +} + +// parseDuration parses timeout strings like "30s", "5m", "1h" +func parseDuration(timeout string) (time.Duration, error) { + // Extract numeric part and unit + if len(timeout) < 2 { + return 0, fmt.Errorf("invalid timeout: %s", timeout) + } + + unit := timeout[len(timeout)-1:] + valueStr := timeout[:len(timeout)-1] + + var value int + _, err := fmt.Sscanf(valueStr, "%d", &value) + if err != nil { + return 0, fmt.Errorf("invalid timeout value: %s", timeout) + } + + switch unit { + case "s": + return time.Duration(value) * time.Second, nil + case "m": + return time.Duration(value) * time.Minute, nil + case "h": + return time.Duration(value) * time.Hour, nil + default: + return 0, fmt.Errorf("invalid timeout unit: %s (must be s, m, or h)", unit) + } +} + +// GetPodLogs retrieves logs from a pod +func (c *Client) GetPodLogs(podName string) (string, error) { + // Validate parameters to prevent command injection + if err := ValidateNamespace(c.namespace); err != nil { + return "", fmt.Errorf("namespace validation failed: %w", err) + } + if err := ValidateResourceName(podName); err != nil { + return "", fmt.Errorf("pod name validation failed: %w", err) + } + + // Log command for audit and reproducibility + log.Printf("KUBECTL_CMD: kubectl logs %s -n %s", podName, c.namespace) + + cmd := exec.Command("kubectl", "logs", podName, "-n", c.namespace) + output, err := utils.Run(cmd) + return string(output), err +} + +// GetPodLogsSince retrieves logs from a pod since a specific time +func (c *Client) GetPodLogsSince(podName string, since string) (string, error) { + cmd := exec.Command("kubectl", "logs", podName, "-n", c.namespace, "--since", since) + output, err := utils.Run(cmd) + return string(output), err +} + +// GetPodLogsTail retrieves the last N lines of logs from a pod +func (c *Client) GetPodLogsTail(podName string, lines int) (string, error) { + cmd := exec.Command("kubectl", "logs", podName, "-n", c.namespace, "--tail", fmt.Sprintf("%d", lines)) + output, err := utils.Run(cmd) + return string(output), err +} + +// GetPodLogsSinceTime retrieves logs from a pod since a specific time with line limit +// Uses --since-time for temporal filtering and --tail as a safety limit +func (c *Client) GetPodLogsSinceTime(podName string, since time.Time, tailLines int) (string, error) { + // Format time as RFC3339 for Kubernetes + sinceTime := since.Format(time.RFC3339) + + // Use both --since-time and --tail: + // --since-time filters logs by timestamp + // --tail provides safety limit if too many logs match + cmd := exec.Command("kubectl", "logs", podName, "-n", c.namespace, + "--since-time", sinceTime, + "--tail", fmt.Sprintf("%d", tailLines)) + output, err := utils.Run(cmd) + return string(output), err +} + +// GetPodsByLabel retrieves pod names matching a label selector +func (c *Client) GetPodsByLabel(labelSelector string) ([]string, error) { + // Validate parameters to prevent command injection + if err := ValidateNamespace(c.namespace); err != nil { + return nil, fmt.Errorf("namespace validation failed: %w", err) + } + if err := ValidateLabelSelector(labelSelector); err != nil { + return nil, fmt.Errorf("label selector validation failed: %w", err) + } + + // Log command for audit and reproducibility + log.Printf("KUBECTL_CMD: kubectl get pods -n %s -l %s -o jsonpath={.items[*].metadata.name}", c.namespace, labelSelector) + + cmd := exec.Command("kubectl", "get", "pods", "-n", c.namespace, "-l", labelSelector, "-o", "jsonpath={.items[*].metadata.name}") + output, err := utils.Run(cmd) + if err != nil { + return nil, err + } + + podNames := strings.Fields(string(output)) + return podNames, nil +} + +// WaitForPodReady waits for a pod to become ready +func (c *Client) WaitForPodReady(podName string, timeout string) error { + // Validate parameters to prevent command injection + if err := ValidateNamespace(c.namespace); err != nil { + return fmt.Errorf("namespace validation failed: %w", err) + } + if err := ValidateResourceName(podName); err != nil { + return fmt.Errorf("pod name validation failed: %w", err) + } + if err := ValidateTimeout(timeout); err != nil { + return fmt.Errorf("timeout validation failed: %w", err) + } + + // Log command for audit and reproducibility + log.Printf("KUBECTL_CMD: kubectl wait --for=condition=Ready --timeout=%s pod/%s -n %s", timeout, podName, c.namespace) + + cmd := exec.Command("kubectl", "wait", + "--for=condition=Ready", + fmt.Sprintf("--timeout=%s", timeout), + fmt.Sprintf("pod/%s", podName), + "-n", c.namespace) + _, err := utils.Run(cmd) + return err +} diff --git a/test/e2e/framework/kubectl/validation.go b/test/e2e/framework/kubectl/validation.go new file mode 100644 index 00000000..40986df7 --- /dev/null +++ b/test/e2e/framework/kubectl/validation.go @@ -0,0 +1,143 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kubectl + +import ( + "fmt" + "regexp" +) + +// ValidateNamespace validates namespace against RFC 1123 DNS Label requirements. +// A valid namespace must: +// - Be 1-63 characters long +// - Contain only lowercase alphanumeric characters or '-' +// - Start with an alphanumeric character +// - End with an alphanumeric character +// Empty namespace is allowed for cluster-scoped resources +func ValidateNamespace(namespace string) error { + // Allow empty namespace for cluster-scoped resources + if len(namespace) == 0 { + return nil + } + + if len(namespace) > 63 { + return fmt.Errorf("namespace length must be 1-63 characters, got %d", len(namespace)) + } + + // RFC 1123 DNS Label regex: lowercase alphanumeric and hyphens only + // Must start and end with alphanumeric + if !regexp.MustCompile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?$`).MatchString(namespace) { + return fmt.Errorf("invalid namespace format: %s (must match RFC 1123 DNS Label)", namespace) + } + + return nil +} + +// ValidateResourceName validates Kubernetes resource names against RFC 1123 DNS Subdomain requirements. +// A valid resource name must: +// - Be 1-253 characters long +// - Contain only lowercase alphanumeric characters, '-', or '.' +// - Start with an alphanumeric character +// - End with an alphanumeric character +func ValidateResourceName(name string) error { + if len(name) == 0 { + return fmt.Errorf("resource name cannot be empty") + } + + if len(name) > 253 { + return fmt.Errorf("resource name length must be 1-253 characters, got %d", len(name)) + } + + // RFC 1123 DNS Subdomain regex + if !regexp.MustCompile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`).MatchString(name) { + return fmt.Errorf("invalid resource name format: %s (must match RFC 1123 DNS Subdomain)", name) + } + + return nil +} + +// ValidateResourceType validates Kubernetes resource type names. +// These are typically lowercase and may contain '.' for API groups. +func ValidateResourceType(resourceType string) error { + if len(resourceType) == 0 { + return fmt.Errorf("resource type cannot be empty") + } + + // Allow alphanumeric, dots for API groups (e.g., "apps.deployment") + if !regexp.MustCompile(`^[a-z0-9]([a-z0-9\.\-]*[a-z0-9])?$`).MatchString(resourceType) { + return fmt.Errorf("invalid resource type format: %s", resourceType) + } + + return nil +} + +// ValidateLabelSelector validates Kubernetes label selectors. +// Label selectors have specific syntax requirements for keys and values. +func ValidateLabelSelector(selector string) error { + if selector == "" { + // Empty selector is valid (means no filter) + return nil + } + + // Basic validation: check for suspicious characters that could be used for injection + // Allow alphanumeric, dots, hyphens, underscores, slashes (for label keys), equals, commas + if !regexp.MustCompile(`^[a-zA-Z0-9\.\_\-/=,]+$`).MatchString(selector) { + return fmt.Errorf("invalid label selector format: %s", selector) + } + + return nil +} + +// ValidateTimeout validates timeout strings used with kubectl commands. +// Valid formats: "30s", "5m", "1h", "2m0s", "1h30m", "1h30m45s" +// Accepts both simple format (5m) and Go duration format (5m0s) +func ValidateTimeout(timeout string) error { + if timeout == "" { + return fmt.Errorf("timeout cannot be empty") + } + + // Allow Go duration format: combinations of hours, minutes, seconds + // Examples: 30s, 5m, 1h, 2m0s, 1h30m, 1h30m45s + // Pattern: optional hours (Nh), optional minutes (Nm), optional seconds (Ns) + if !regexp.MustCompile(`^([0-9]+h)?([0-9]+m)?([0-9]+(\.[0-9]+)?[sµμn]s?)?$`).MatchString(timeout) { + return fmt.Errorf("invalid timeout format: %s (must be Go duration like '30s', '5m', '2m0s', or '1h30m')", timeout) + } + + // Ensure at least one component is present + if !regexp.MustCompile(`[0-9]`).MatchString(timeout) { + return fmt.Errorf("invalid timeout format: %s (must contain at least one time component)", timeout) + } + + return nil +} + +// ValidateJSONPath validates JSONPath expressions used with kubectl. +// This is a basic validation to prevent obvious injection attempts. +func ValidateJSONPath(jsonPath string) error { + if jsonPath == "" { + return fmt.Errorf("jsonPath cannot be empty") + } + + // Basic validation: JSONPath should not contain shell metacharacters + // Allow alphanumeric, dots, brackets, quotes, underscores, hyphens, asterisks, colons, backslashes + // Backslash is needed for escaping dots in keys like .data['agent\.json'] + if !regexp.MustCompile(`^[\w\.\[\]\{\}'":\*\-\s,@\?\\]+$`).MatchString(jsonPath) { + return fmt.Errorf("invalid jsonPath format: %s", jsonPath) + } + + return nil +} diff --git a/test/e2e/framework/kubectl/wait.go b/test/e2e/framework/kubectl/wait.go new file mode 100644 index 00000000..cfaed942 --- /dev/null +++ b/test/e2e/framework/kubectl/wait.go @@ -0,0 +1,111 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kubectl + +import ( + "fmt" + "os/exec" + "strings" + "time" + + . "github.com/onsi/gomega" + + "github.com/kaasops/vector-operator/test/e2e/framework/config" + "github.com/kaasops/vector-operator/test/utils" +) + +// WaitForDeploymentReady waits for a deployment to be created and ready +func (c *Client) WaitForDeploymentReady(name string) { + // First wait for deployment to exist (with reduced timeout) + Eventually(func() error { + cmd := exec.Command("kubectl", "get", "deployment", name, "-n", c.namespace) + _, err := utils.Run(cmd) + return err + }, config.DeploymentCreateTimeout, config.DefaultPollInterval).Should(Succeed(), + "Deployment %s should be created in namespace %s", name, c.namespace) + + // Then wait for it to be available + err := c.Wait("deployment", name, "condition=available", config.DeploymentReadyTimeout.String()) + Expect(err).NotTo(HaveOccurred(), + "Deployment %s should become ready in namespace %s", name, c.namespace) +} + +// WaitForPipelineValid waits for a VectorPipeline to become valid +func (c *Client) WaitForPipelineValid(name string) { + Eventually(func() error { + result, err := c.GetWithJsonPath("vectorpipeline", name, ".status.configCheckResult") + if err != nil { + return err + } + if result != "true" { + return fmt.Errorf("pipeline not valid yet: %s", result) + } + return nil + }, config.PipelineValidTimeout, config.SlowPollInterval).Should(Succeed(), + "Pipeline %s should become valid in namespace %s", name, c.namespace) +} + +// WaitForPipelineInvalid waits for a VectorPipeline to become invalid (for negative tests) +func (c *Client) WaitForPipelineInvalid(name string) { + Eventually(func() error { + result, err := c.GetWithJsonPath("vectorpipeline", name, ".status.configCheckResult") + if err != nil { + return err + } + if result != "false" { + return fmt.Errorf("expected pipeline to be invalid, got: %s", result) + } + return nil + }, config.PipelineValidTimeout, config.SlowPollInterval).Should(Succeed(), + "Pipeline %s should become invalid in namespace %s", name, c.namespace) +} + +// WaitForServiceExists waits for a service to be created +func (c *Client) WaitForServiceExists(name string) { + Eventually(func() error { + _, err := c.Get("service", name) + return err + }, config.ServiceCreateTimeout, config.SlowPollInterval).Should(Succeed(), + "Service %s should be created in namespace %s", name, c.namespace) +} + +// WaitForServiceCount waits for a specific number of services matching filter +func (c *Client) WaitForServiceCount(labelSelector string, expectedCount int, timeout time.Duration) { + Eventually(func() (int, error) { + result, err := c.GetAll("service", labelSelector) + if err != nil { + return 0, err + } + if result == "" { + return 0, nil + } + + services := 0 + for _, svc := range splitFields(result) { + if svc != "" { + services++ + } + } + return services, nil + }, timeout, config.SlowPollInterval).Should(Equal(expectedCount), + "Expected %d services with label %s in namespace %s", expectedCount, labelSelector, c.namespace) +} + +// splitFields splits space-separated fields and filters empty strings +func splitFields(s string) []string { + return strings.Fields(s) +} diff --git a/test/e2e/framework/lifecycle.go b/test/e2e/framework/lifecycle.go new file mode 100644 index 00000000..d7af9475 --- /dev/null +++ b/test/e2e/framework/lifecycle.go @@ -0,0 +1,91 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package framework + +import ( + "time" + + . "github.com/onsi/ginkgo/v2" + + "github.com/kaasops/vector-operator/test/utils" +) + +// SharedDependencies manages dependencies that are shared across all tests +type SharedDependencies struct { + prometheusInstalled bool + certManagerInstalled bool + installTime time.Duration +} + +var globalDeps *SharedDependencies + +// InstallSharedDependencies installs Prometheus and cert-manager once for all tests +// This should be called in BeforeSuite +func InstallSharedDependencies() { + if globalDeps != nil { + GinkgoWriter.Println("⚠️ Shared dependencies already installed, skipping...") + return + } + + start := time.Now() + globalDeps = &SharedDependencies{} + + By("installing Prometheus Operator (shared)") + err := utils.InstallPrometheusOperator() + if err != nil { + // Ignore AlreadyExists errors - dependencies might be already installed + GinkgoWriter.Printf("⚠️ Prometheus Operator installation returned error (might already exist): %v\n", err) + } + globalDeps.prometheusInstalled = true + + By("installing cert-manager (shared)") + err = utils.InstallCertManager() + if err != nil { + // Ignore AlreadyExists errors - dependencies might be already installed + GinkgoWriter.Printf("⚠️ cert-manager installation returned error (might already exist): %v\n", err) + } + globalDeps.certManagerInstalled = true + + globalDeps.installTime = time.Since(start) + GinkgoWriter.Printf("✅ Shared dependencies installed in %v\n", globalDeps.installTime) +} + +// UninstallSharedDependencies removes Prometheus and cert-manager +// This should be called in AfterSuite +func UninstallSharedDependencies() { + if globalDeps == nil { + return + } + + By("uninstalling Prometheus Operator (shared)") + if globalDeps.prometheusInstalled { + utils.UninstallPrometheusOperator() + } + + By("uninstalling cert-manager (shared)") + if globalDeps.certManagerInstalled { + utils.UninstallCertManager() + } + + GinkgoWriter.Println("✅ Shared dependencies uninstalled") + globalDeps = nil +} + +// AreSharedDependenciesInstalled checks if shared dependencies are available +func AreSharedDependenciesInstalled() bool { + return globalDeps != nil && globalDeps.prometheusInstalled && globalDeps.certManagerInstalled +} diff --git a/test/e2e/framework/recorder/recorder.go b/test/e2e/framework/recorder/recorder.go new file mode 100644 index 00000000..72de48b1 --- /dev/null +++ b/test/e2e/framework/recorder/recorder.go @@ -0,0 +1,258 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package recorder + +import ( + "fmt" + "strings" + "time" +) + +// TestRecorder records test operations for reproducibility and documentation +type TestRecorder struct { + testName string + namespace string + steps []TestStep + startTime time.Time + stepOrder int +} + +// TestStep represents a single operation in a test +type TestStep struct { + Order int + Command string // Exact kubectl or shell command + Description string // Human-readable description + Input string // YAML or other input data + Expected string // Expected result + WaitFor string // Wait condition (e.g., "condition=available") + Timeout string // Timeout for the operation +} + +// NewTestRecorder creates a new test recorder +func NewTestRecorder(namespace string) *TestRecorder { + return &TestRecorder{ + namespace: namespace, + steps: make([]TestStep, 0), + startTime: time.Now(), + stepOrder: 0, + } +} + +// SetTestName sets the test name for this recording +func (r *TestRecorder) SetTestName(name string) { + r.testName = name +} + +// RecordStep records a test step +func (r *TestRecorder) RecordStep(step TestStep) { + r.stepOrder++ + step.Order = r.stepOrder + r.steps = append(r.steps, step) +} + +// GetSteps returns all recorded steps +func (r *TestRecorder) GetSteps() []TestStep { + return r.steps +} + +// ExportAsShellScript exports the recorded steps as an executable shell script +func (r *TestRecorder) ExportAsShellScript() string { + var sb strings.Builder + + // Script header + sb.WriteString("#!/bin/bash\n") + sb.WriteString("# E2E Test Playbook\n") + sb.WriteString(fmt.Sprintf("# Test: %s\n", r.testName)) + sb.WriteString(fmt.Sprintf("# Namespace: %s\n", r.namespace)) + sb.WriteString(fmt.Sprintf("# Generated: %s\n\n", time.Now().Format(time.RFC3339))) + + // Shell settings for safety + sb.WriteString("set -e # Exit on error\n") + sb.WriteString("set -u # Exit on undefined variable\n") + sb.WriteString("set -o pipefail # Catch errors in pipes\n\n") + + // Variables + sb.WriteString(fmt.Sprintf("NAMESPACE='%s'\n", r.namespace)) + sb.WriteString("KUBECTL='kubectl'\n") + sb.WriteString("TMPDIR=$(mktemp -d)\n") + sb.WriteString("trap 'rm -rf $TMPDIR' EXIT\n\n") + + // Helper functions + sb.WriteString(r.generateHelperFunctions()) + + // Main steps + sb.WriteString("# Test Steps\n") + sb.WriteString("echo '═══════════════════════════════════════════════════════════'\n") + sb.WriteString(fmt.Sprintf("echo 'Test: %s'\n", r.testName)) + sb.WriteString("echo '═══════════════════════════════════════════════════════════'\n\n") + + for _, step := range r.steps { + sb.WriteString(fmt.Sprintf("# Step %d: %s\n", step.Order, step.Description)) + sb.WriteString("echo '───────────────────────────────────────────────────────────'\n") + sb.WriteString(fmt.Sprintf("log_info 'Step %d: %s'\n", step.Order, step.Description)) + sb.WriteString("echo '───────────────────────────────────────────────────────────'\n") + + // If there's input data, save it to a temporary file + if step.Input != "" { + tmpFile := fmt.Sprintf("$TMPDIR/step-%d.yaml", step.Order) + sb.WriteString(fmt.Sprintf("cat <<'EOF' > %s\n", tmpFile)) + sb.WriteString(step.Input) + sb.WriteString("\nEOF\n") + + // Modify command to use the temp file + if strings.Contains(step.Command, "kubectl apply -f -") { + modifiedCmd := strings.Replace(step.Command, "kubectl apply -f -", fmt.Sprintf("kubectl apply -f %s", tmpFile), 1) + sb.WriteString(modifiedCmd + "\n") + } else { + sb.WriteString(step.Command + "\n") + } + } else { + sb.WriteString(step.Command + "\n") + } + + // Add expected result as comment + if step.Expected != "" { + sb.WriteString(fmt.Sprintf("# Expected: %s\n", step.Expected)) + } + + // Add wait condition if specified + if step.WaitFor != "" { + sb.WriteString(fmt.Sprintf("# Wait for: %s (timeout: %s)\n", step.WaitFor, step.Timeout)) + } + + sb.WriteString("\n") + } + + // Success message + sb.WriteString("echo '═══════════════════════════════════════════════════════════'\n") + sb.WriteString("log_success 'Test completed successfully!'\n") + sb.WriteString("echo '═══════════════════════════════════════════════════════════'\n") + + return sb.String() +} + +// ExportAsMarkdown exports the recorded steps as Markdown documentation +func (r *TestRecorder) ExportAsMarkdown() string { + var sb strings.Builder + + // Document header + sb.WriteString(fmt.Sprintf("# Test Plan: %s\n\n", r.testName)) + sb.WriteString(fmt.Sprintf("**Generated**: %s\n\n", time.Now().Format(time.RFC3339))) + sb.WriteString(fmt.Sprintf("**Namespace**: `%s`\n\n", r.namespace)) + + // Prerequisites + sb.WriteString("## Prerequisites\n\n") + sb.WriteString("- Kubernetes cluster with Vector Operator installed\n") + sb.WriteString("- kubectl configured with cluster access\n") + sb.WriteString("- Appropriate RBAC permissions\n\n") + + // Test steps + sb.WriteString("## Test Steps\n\n") + + for _, step := range r.steps { + sb.WriteString(fmt.Sprintf("### Step %d: %s\n\n", step.Order, step.Description)) + + // Command + sb.WriteString("**Command**:\n") + sb.WriteString("```bash\n") + sb.WriteString(step.Command + "\n") + sb.WriteString("```\n\n") + + // Input YAML if present + if step.Input != "" { + sb.WriteString("**Input YAML**:\n") + sb.WriteString("```yaml\n") + sb.WriteString(step.Input + "\n") + sb.WriteString("```\n\n") + } + + // Wait condition if present + if step.WaitFor != "" { + sb.WriteString(fmt.Sprintf("**Wait Condition**: `%s`\n\n", step.WaitFor)) + if step.Timeout != "" { + sb.WriteString(fmt.Sprintf("**Timeout**: %s\n\n", step.Timeout)) + } + } + + // Expected result + if step.Expected != "" { + sb.WriteString(fmt.Sprintf("**Expected Result**: %s\n\n", step.Expected)) + } + + sb.WriteString("---\n\n") + } + + return sb.String() +} + +// generateHelperFunctions generates helper shell functions for the script +func (r *TestRecorder) generateHelperFunctions() string { + return `# Helper Functions +log_info() { + echo "[INFO] $(date '+%Y-%m-%d %H:%M:%S') - $1" +} + +log_error() { + echo "[ERROR] $(date '+%Y-%m-%d %H:%M:%S') - $1" >&2 +} + +log_success() { + echo "[SUCCESS] $(date '+%Y-%m-%d %H:%M:%S') - $1" +} + +check_deployment() { + local name=$1 + local namespace=${2:-$NAMESPACE} + log_info "Checking deployment $name in namespace $namespace..." + kubectl get deployment "$name" -n "$namespace" &>/dev/null || { + log_error "Deployment $name not found!" + return 1 + } + log_info "Deployment $name exists" +} + +check_service() { + local name=$1 + local namespace=${2:-$NAMESPACE} + log_info "Checking service $name in namespace $namespace..." + kubectl get service "$name" -n "$namespace" &>/dev/null || { + log_error "Service $name not found!" + return 1 + } + log_info "Service $name exists" +} + +wait_for_pods() { + local label=$1 + local namespace=${2:-$NAMESPACE} + local timeout=${3:-120s} + log_info "Waiting for pods with label $label in namespace $namespace..." + kubectl wait --for=condition=Ready pods -l "$label" -n "$namespace" --timeout="$timeout" || { + log_error "Pods with label $label did not become ready within $timeout" + return 1 + } + log_info "Pods are ready" +} + +` +} + +// Clear clears all recorded steps +func (r *TestRecorder) Clear() { + r.steps = make([]TestStep, 0) + r.stepOrder = 0 +} diff --git a/test/e2e/framework/resources.go b/test/e2e/framework/resources.go new file mode 100644 index 00000000..9e02fe13 --- /dev/null +++ b/test/e2e/framework/resources.go @@ -0,0 +1,36 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package framework + +import ( + "github.com/kaasops/vector-operator/test/e2e/framework/assertions" +) + +// Pipeline returns a pipeline resource wrapper for custom matchers +func (f *Framework) Pipeline(name string) *assertions.PipelineResource { + return assertions.NewPipelineResource(f.namespace, name) +} + +// ClusterPipeline returns a cluster-scoped pipeline resource wrapper for custom matchers +func (f *Framework) ClusterPipeline(name string) *assertions.PipelineResource { + return assertions.NewPipelineResource("", name) +} + +// Service returns a service resource wrapper for custom matchers +func (f *Framework) Service(name string) *assertions.ServiceResource { + return assertions.NewServiceResource(f.namespace, name) +} diff --git a/test/e2e/normal_mode_e2e_test.go b/test/e2e/normal_mode_e2e_test.go new file mode 100644 index 00000000..1f250574 --- /dev/null +++ b/test/e2e/normal_mode_e2e_test.go @@ -0,0 +1,222 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package e2e + +import ( + "fmt" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/kaasops/vector-operator/test/e2e/framework" + "github.com/kaasops/vector-operator/test/e2e/framework/config" +) + +// Normal Mode tests verify that the operator works correctly for standard pipelines. +// These tests cover basic Vector Agent and Aggregator functionality. +var _ = Describe("Normal Mode", Label(config.LabelSmoke, config.LabelFast), Ordered, func() { + f := framework.NewUniqueFramework("test-normal-mode") + + BeforeAll(func() { + f.Setup() + }) + + AfterAll(func() { + f.Teardown() + f.PrintMetrics() + }) + + Context("VectorPipeline basics", func() { + It("should create and validate a basic pipeline with agent", func() { + By("deploying Vector Agent") + f.ApplyTestData("normal-mode/agent.yaml") + + // Give controller time to process Vector CR Create event and create daemonset + // Normal mode requires slightly more time as it involves more resources + time.Sleep(5 * time.Second) + + By("creating a VectorPipeline") + f.ApplyTestData("normal-mode/pipeline-basic.yaml") + + By("waiting for pipeline to become valid") + f.WaitForPipelineValid("basic-pipeline") + + By("verifying agent processes the pipeline configuration") + Eventually(func() error { + // Check that agent config contains the pipeline components + return f.VerifyAgentHasPipeline("normal-agent", "basic-pipeline") + }, config.ServiceCreateTimeout, config.DefaultPollInterval).Should(Succeed()) + }) + + It("should handle pipeline with transforms and multiple sinks", func() { + By("creating a complex pipeline with transforms") + f.ApplyTestData("normal-mode/pipeline-complex.yaml") + + By("waiting for pipeline to become valid") + f.WaitForPipelineValid("complex-pipeline") + + By("verifying pipeline has expected components") + // Pipeline should have sources, transforms, and sinks all in agent + Eventually(func() error { + return f.VerifyAgentHasPipeline("normal-agent", "complex-pipeline") + }, config.ServiceCreateTimeout, config.DefaultPollInterval).Should(Succeed()) + }) + }) + + Context("VectorAggregator basics", func() { + It("should deploy aggregator and process pipelines", func() { + By("deploying VectorAggregator") + f.ApplyTestData("normal-mode/aggregator.yaml") + f.WaitForDeploymentReady("normal-aggregator-aggregator") + + By("creating a pipeline with aggregator role") + f.ApplyTestData("normal-mode/pipeline-aggregator-role.yaml") + + By("waiting for pipeline to become valid") + f.WaitForPipelineValid("aggregator-pipeline") + + By("verifying pipeline has aggregator role") + role := f.GetPipelineStatus("aggregator-pipeline", "role") + Expect(role).To(Equal("aggregator")) + + By("verifying aggregator processes the pipeline") + Eventually(func() error { + return f.VerifyAggregatorHasPipeline("normal-aggregator", "aggregator-pipeline") + }, config.ServiceCreateTimeout, config.DefaultPollInterval).Should(Succeed()) + }) + }) + + Context("Multiple pipelines in normal mode", func() { + It("should handle multiple pipelines without conflicts", func() { + By("creating 3 pipelines in normal mode") + for i := 1; i <= 3; i++ { + f.ApplyTestDataWithVars("normal-mode/pipeline-template.yaml", + map[string]string{"{{INDEX}}": fmt.Sprintf("pipeline-%d", i)}) + } + + By("waiting for all pipelines to become valid") + f.WaitForPipelineValid("pipeline-1") + f.WaitForPipelineValid("pipeline-2") + f.WaitForPipelineValid("pipeline-3") + + By("verifying all pipelines are in agent configuration") + Eventually(func() error { + if err := f.VerifyAgentHasPipeline("normal-agent", "pipeline-1"); err != nil { + return err + } + if err := f.VerifyAgentHasPipeline("normal-agent", "pipeline-2"); err != nil { + return err + } + return f.VerifyAgentHasPipeline("normal-agent", "pipeline-3") + }, config.ServiceCreateTimeout, config.DefaultPollInterval).Should(Succeed()) + }) + }) + + Context("Pipeline deletion in normal mode", func() { + It("should clean up pipeline from agent config when deleted", func() { + By("creating a pipeline") + f.ApplyTestData("normal-mode/pipeline-deletable.yaml") + f.WaitForPipelineValid("deletable-pipeline") + + By("verifying pipeline is in agent config") + Eventually(func() error { + return f.VerifyAgentHasPipeline("normal-agent", "deletable-pipeline") + }, config.ServiceCreateTimeout, config.DefaultPollInterval).Should(Succeed()) + + By("deleting the pipeline") + f.DeleteResource("vectorpipeline", "deletable-pipeline") + + By("verifying pipeline is removed from agent config") + Eventually(func() bool { + err := f.VerifyAgentHasPipeline("normal-agent", "deletable-pipeline") + return err != nil // Should return error when pipeline not found + }, config.ServiceCreateTimeout, config.DefaultPollInterval).Should(BeTrue()) + }) + }) + + Context("Kubernetes logs source with label selectors", func() { + It("should collect logs from pods matching label selector", func() { + By("deploying a test pod with specific labels") + f.ApplyTestData("normal-mode/test-app-pod.yaml") + f.WaitForPodReady("test-app") + + By("creating pipeline with kubernetes_logs source and label selector") + f.ApplyTestData("normal-mode/pipeline-kubernetes-logs.yaml") + f.WaitForPipelineValid("k8s-logs-pipeline") + + By("verifying agent has kubernetes_logs source") + Eventually(func() error { + return f.VerifyAgentHasPipeline("normal-agent", "k8s-logs-pipeline") + }, config.ServiceCreateTimeout, config.DefaultPollInterval).Should(Succeed()) + + By("verifying pipeline role is Agent") + role := f.GetPipelineStatus("k8s-logs-pipeline", "role") + Expect(role).To(Equal("agent"), "kubernetes_logs pipeline should have agent role") + }) + }) + + Context("Namespace isolation", func() { + It("should only collect logs from the pipeline's namespace", func() { + By("creating a separate namespace") + f.ApplyTestDataWithoutNamespaceReplacement("normal-mode/namespace-isolation-ns.yaml") + + By("deploying Vector agent in isolated namespace") + // Note: In real scenario, the same Vector DaemonSet serves all namespaces + // But pipelines are namespace-scoped + + By("deploying pods in both namespaces") + f.ApplyTestData("normal-mode/namespace-isolation-pod-main.yaml") + f.ApplyTestDataWithoutNamespaceReplacement("normal-mode/namespace-isolation-pod-isolated.yaml") + f.WaitForPodReady("main-namespace-pod") + f.WaitForPodReadyInNamespace("isolated-pod", "test-normal-mode-isolated") + + By("creating pipeline in isolated namespace") + f.ApplyTestDataWithoutNamespaceReplacement("normal-mode/namespace-isolation-pipeline.yaml") + f.WaitForPipelineValidInNamespace("isolated-pipeline", "test-normal-mode-isolated") + + By("verifying namespace isolation in configuration") + // The agent config should have extra_namespace_label_selector set to the pipeline's namespace + Eventually(func() error { + return f.VerifyAgentHasPipelineInNamespace("normal-agent", "isolated-pipeline", "test-normal-mode-isolated") + }, config.ServiceCreateTimeout, config.DefaultPollInterval).Should(Succeed()) + }) + }) + + Context("ClusterVectorPipeline", func() { + It("should collect logs from multiple namespaces", func() { + By("creating ClusterVectorPipeline") + f.ApplyTestDataWithoutNamespaceReplacement("normal-mode/cluster-pipeline.yaml") + f.WaitForClusterPipelineValid("cluster-wide-pipeline") + + By("deploying test pods in different namespaces with matching labels") + f.ApplyTestData("normal-mode/cluster-pipeline-pod-ns1.yaml") + f.ApplyTestDataWithoutNamespaceReplacement("normal-mode/cluster-pipeline-pod-ns2.yaml") + f.WaitForPodReady("cluster-monitored-pod-1") + f.WaitForPodReadyInNamespace("cluster-monitored-pod-2", "test-normal-mode-isolated") + + By("verifying agent processes the ClusterVectorPipeline") + Eventually(func() error { + return f.VerifyAgentHasClusterPipeline("normal-agent", "cluster-wide-pipeline") + }, config.ServiceCreateTimeout, config.DefaultPollInterval).Should(Succeed()) + + By("verifying pipeline role is Agent") + role := f.GetClusterPipelineStatus("cluster-wide-pipeline", "role") + Expect(role).To(Equal("agent"), "ClusterVectorPipeline with kubernetes_logs should have agent role") + }) + }) +}) diff --git a/test/e2e/scripts/README.md b/test/e2e/scripts/README.md new file mode 100644 index 00000000..1669ae19 --- /dev/null +++ b/test/e2e/scripts/README.md @@ -0,0 +1,40 @@ +# E2E Test Scripts + +Utilities for working with e2e test results and test environment. + +## Available Scripts + +### generate_report.py + +Generates an interactive HTML pivot grid report from e2e test results. + +**Usage:** +```bash +# From project root +make test-report + +# Or directly +cd test/e2e/results +python3 ../scripts/generate_report.py +``` + +**What it does:** +- Scans all `run-*` directories in `test/e2e/results/` +- Parses test metadata and results from each run +- Generates `test_results_report.html` with interactive pivot grid +- Shows test stability across multiple runs (flaky tests, always-failing tests, etc.) + +**Requirements:** +- Python 3.6+ +- Test results in `test/e2e/results/run-YYYY-MM-DD-HHMMSS/` format + +**Output:** +- `test/e2e/results/test_results_report.html` - Interactive HTML report + +## Adding New Scripts + +When adding new test utilities: +1. Place the script in this directory +2. Update this README with usage instructions +3. Add a Makefile target if appropriate (see `make help`) +4. Ensure the script has proper error handling and help text diff --git a/test/e2e/scripts/generate_report.py b/test/e2e/scripts/generate_report.py new file mode 100644 index 00000000..d02c1bce --- /dev/null +++ b/test/e2e/scripts/generate_report.py @@ -0,0 +1,4554 @@ +#!/usr/bin/env python3 +""" +Generate HTML Pivot Grid Report for E2E Test Results (Enhanced V2) + +Features: +- Interactive pivot grid showing test results across multiple runs +- Trend analysis charts (Pass Rate, Duration) +- Advanced Log Viewer with ANSI support and filtering +- Deep Flakiness Analysis (Score, Patterns) +- Run Comparison (New Failures, Fixed Tests) +- Smart artifact matching +- Filtering and Search +""" + +import json +import sys +import html +import re +from datetime import datetime +from pathlib import Path +from typing import Dict, List, Any, Optional, Set +from dataclasses import dataclass, field + +# --- SVG Icons --- + +def svg_icon(name: str, size: int = 16, color: str = 'currentColor') -> str: + """Generate inline SVG icons""" + icons = { + 'search': f'', + 'copy': f'', + 'download': f'', + 'error': f'', + 'warning': f'', + 'info': f'', + 'bug': f'', + 'chevron-up': f'', + 'chevron-down': f'', + 'arrow-up': f'', + 'arrow-down': f'', + 'wrap': f'', + 'sun': f'', + 'moon': f'', + } + return icons.get(name, '') + +def format_duration(seconds: float) -> str: + """Format duration in human-readable format""" + if not seconds or seconds < 0: + return 'N/A' + + hours = int(seconds // 3600) + minutes = int((seconds % 3600) // 60) + secs = int(seconds % 60) + + if hours > 0: + return f"{hours}h {minutes}m {secs}s" + elif minutes > 0: + return f"{minutes}m {secs}s" + else: + return f"{secs}s" + +# --- Data Structures --- + +@dataclass +class TestResult: + name: str + full_name: str + leaf_text: str + state: str + runtime: float + failure_message: str = "" + labels: List[str] = field(default_factory=list) + container_hierarchy: List[str] = field(default_factory=list) + start_time: str = "" + end_time: str = "" + artifact_metadata: Optional[Dict[str, Any]] = None + +@dataclass +class TestRun: + run_id: str + start_time: str + total_tests: int + passed_tests: int + failed_tests: int + environment: Dict[str, Any] + total_runtime: float + test_output_log: str + tests: List[TestResult] + git_commit: str = "" + git_branch: str = "" + git_dirty: str = "" + description: str = "" + + @property + def date_str(self) -> str: + return datetime.fromisoformat(self.start_time).strftime('%Y-%m-%d %H:%M') + +@dataclass +class PivotRow: + test_name: str + full_test_name: str + leaf_text: str + container_hierarchy: List[str] + runs: Dict[str, Any] = field(default_factory=dict) # run_id -> result dict + + # Stats + total_runs: int = 0 + pass_count: int = 0 + fail_count: int = 0 + skip_count: int = 0 + pass_rate: float = 0.0 + total_runtime: float = 0.0 + avg_runtime: float = 0.0 + min_runtime: float = float('inf') + max_runtime: float = 0.0 + + # Flakiness + is_flaky: bool = False + flakiness_score: float = 0.0 + flakiness_pattern: str = 'stable' + +# --- Templates --- + +class ReportTemplates: + """Holds HTML, CSS, and JS templates.""" + + CSS = """ + :root { + /* Light theme colors */ + --bg-primary: #ffffff; + --bg-secondary: #f8fafc; + --bg-tertiary: #fafbfc; + --bg-hover: #e0e7ff; + --cell-hover-bg: #eff6ff; + --cell-active-bg: #dbeafe; + --text-primary: #0f172a; + --text-secondary: #64748b; + --text-tertiary: #94a3b8; + --border-color: #e2e8f0; + --border-secondary: #cbd5e1; + --modal-backdrop: rgba(0,0,0,0.4); + --accent-color: #3b82f6; + + /* Status colors */ + --success-bg: #d1fae5; + --success-text: #065f46; + --error-bg: #fee2e2; + --error-text: #991b1b; + --warning-bg: #fef3c7; + --warning-text: #92400e; + --info-bg: #dbeafe; + --info-text: #1e40af; + + /* Log viewer colors */ + --log-bg: #1e293b; + --log-text: #e2e8f0; + --log-border: #334155; + --log-controls-bg: #334155; + --log-input-bg: #1e293b; + --log-input-border: #475569; + } + + [data-theme="dark"] { + /* Dark theme colors */ + --bg-primary: #1e293b; + --bg-secondary: #0f172a; + --bg-tertiary: #1e293b; + --bg-hover: #334155; + --cell-hover-bg: rgba(59, 130, 246, 0.2); + --cell-active-bg: rgba(59, 130, 246, 0.3); + --text-primary: #f1f5f9; + --text-secondary: #cbd5e1; + --text-tertiary: #94a3b8; + --border-color: #334155; + --border-secondary: #475569; + --modal-backdrop: rgba(0,0,0,0.7); + --accent-color: #60a5fa; + + /* Status colors */ + --success-bg: #064e3b; + --success-text: #a7f3d0; + --error-bg: #7f1d1d; + --error-text: #fca5a5; + --warning-bg: #78350f; + --warning-text: #fcd34d; + --info-bg: #1e3a8a; + --info-text: #93c5fd; + + /* Log viewer colors */ + --log-bg: #0f172a; + --log-text: #e2e8f0; + --log-border: #1e293b; + } + + /* Row hover effect */ + tbody tr { + transition: background-color 0.15s ease; + } + tbody tr:hover { + background-color: var(--bg-hover); + } + tbody tr:hover .test-name-cell, + tbody tr:hover .stats-cell { + background-color: var(--bg-hover); + } + + * { margin: 0; padding: 0; box-sizing: border-box; } + body { + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + background: var(--bg-secondary); + color: var(--text-primary); + min-height: 100vh; + } + + /* SVG Icons */ + svg { + display: inline-block; + vertical-align: middle; + flex-shrink: 0; + } + button svg, .filter-label svg, .log-actions-item svg { + margin-right: 6px; + } + + .container { background: var(--bg-primary); min-height: 100vh; display: flex; flex-direction: column; } + + /* Header & Tabs */ + .header { + padding: 20px 40px; + border-bottom: 1px solid var(--border-color); + display: flex; + justify-content: space-between; + align-items: center; + background: var(--bg-primary); + } + .header h1 { font-size: 22px; font-weight: 600; margin-bottom: 4px; } + + .tabs { + display: flex; + padding: 0 40px; + background: var(--bg-primary); + border-bottom: 1px solid var(--border-color); + gap: 24px; + } + .tab-btn { + padding: 16px 4px; + background: none; + border: none; + border-bottom: 2px solid transparent; + color: var(--text-secondary); + font-weight: 500; + cursor: pointer; + transition: all 0.2s; + } + .tab-btn:hover { color: var(--text-primary); } + .tab-btn.active { + color: var(--accent-color); + border-bottom-color: var(--accent-color); + } + + .tab-content { display: none; padding: 24px 40px; } + .tab-content.active { display: block; } + + /* Summary Cards */ + .summary-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); + gap: 16px; + margin-bottom: 24px; + } + .card { + background: var(--bg-primary); + padding: 20px; + border-radius: 8px; + border: 1px solid var(--border-color); + text-align: center; + display: flex; + flex-direction: column; + justify-content: center; + min-height: 100px; + } + .card .label { font-size: 13px; color: var(--text-secondary); text-transform: uppercase; font-weight: 600; margin-bottom: 8px; } + .card .value { font-size: 32px; font-weight: 700; color: var(--text-primary); text-align: center; display: block; } + .card.passed .value { color: #10b981; } + .card.failed .value { color: #ef4444; } + + /* Tooltips */ + .tooltip { + position: relative; + cursor: help; + } + .card.tooltip { + display: flex; + flex-direction: column; + justify-content: center; + min-height: 100px; + } + .pass-rate.tooltip { + display: inline-block; + } + .badge.tooltip { + position: relative; + display: inline-block; + } + .tooltip .tooltiptext { + visibility: hidden; + width: 250px; + background-color: #1f2937; + color: #fff; + text-align: left; + border-radius: 6px; + padding: 8px 12px; + position: absolute; + z-index: 1000; + top: 100%; + margin-top: 8px; + left: 50%; + margin-left: -125px; + opacity: 0; + transition: opacity 0.3s; + font-size: 12px; + line-height: 1.4; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3); + pointer-events: none; + } + .tooltip .tooltiptext::after { + content: ""; + position: absolute; + bottom: 100%; + left: 50%; + margin-left: -5px; + border-width: 5px; + border-style: solid; + border-color: transparent transparent #1f2937 transparent; + } + .tooltip:hover .tooltiptext { + visibility: visible; + opacity: 1; + } + + /* Charts */ + .charts-container { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 24px; + margin-bottom: 32px; + } + .chart-wrapper { + background: var(--bg-primary); + padding: 20px; + border-radius: 12px; + border: 1px solid var(--border-color); + height: 350px; + } + + /* Flaky Section */ + .flaky-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 16px; + margin-top: 16px; + } + .flaky-card { + text-align: left; + cursor: pointer; + transition: transform 0.2s; + } + .flaky-card:hover { transform: translateY(-2px); border-color: var(--accent-color); } + .flaky-header { display: flex; justify-content: space-between; margin-bottom: 8px; } + .flaky-name { font-weight: 500; margin-bottom: 8px; font-size: 14px; overflow: hidden; text-overflow: ellipsis; } + .flaky-stats { font-size: 12px; color: var(--text-secondary); } + + /* Table Styles */ + .table-wrapper { + overflow-x: auto; + border: 1px solid var(--border-color); + border-radius: 8px; + } + table { width: 100%; border-collapse: collapse; font-size: 14px; } + th, td { + padding: 12px 16px; + border-bottom: 1px solid var(--border-color); + text-align: left; + white-space: nowrap; + } + th { + background: var(--bg-secondary); + font-weight: 600; + color: var(--text-secondary); + position: sticky; + top: 0; + z-index: 10; + } + th.run-header { + cursor: pointer; + transition: all 0.2s ease; + } + th.run-header:hover { + background: var(--bg-hover); + color: var(--text-primary); + transform: scale(1.02); + } + th:first-child, td:first-child { + position: sticky; + left: 0; + background: var(--bg-primary); + z-index: 11; + border-right: 1px solid var(--border-color); + min-width: 500px; + max-width: 700px; + white-space: normal; + } + th:first-child { background: var(--bg-secondary); z-index: 12; } + + /* Breadcrumb Test Names */ + .test-name-cell { + padding: 10px 12px !important; + line-height: 1.5; + position: relative; + } + .test-name-wrapper { + display: flex; + align-items: center; + gap: 8px; + } + .copy-test-name-btn { + opacity: 0; + transition: opacity 0.2s ease; + background: var(--bg-primary); + border: 1px solid var(--border-color); + border-radius: 4px; + padding: 4px 8px; + cursor: pointer; + font-size: 11px; + color: var(--text-secondary); + display: flex; + align-items: center; + gap: 4px; + white-space: nowrap; + } + .copy-test-name-btn:hover { + background: var(--bg-hover); + color: var(--text-primary); + border-color: var(--text-tertiary); + } + .copy-test-name-btn:active { + transform: scale(0.95); + } + tbody tr:hover .copy-test-name-btn { + opacity: 1; + } + .test-breadcrumb { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 6px; + font-size: 13px; + } + .breadcrumb-item { + display: inline-flex; + align-items: center; + } + .breadcrumb-container { + color: var(--text-secondary); + font-weight: 500; + } + .breadcrumb-container.level-0 { + color: var(--text-primary); + font-weight: 600; + } + .breadcrumb-separator { + color: var(--text-tertiary); + margin: 0 6px; + font-weight: 300; + user-select: none; + } + .breadcrumb-leaf { + color: var(--text-primary); + font-weight: 400; + } + + .result-cell { + text-align: center; + cursor: pointer !important; + transition: all 0.2s ease; + position: relative; + } + .result-cell:hover { + background: var(--cell-hover-bg) !important; + transform: translateY(-1px); + box-shadow: 0 2px 4px rgba(0,0,0,0.1); + } + .result-cell:active { + transform: translateY(0); + } + .result-cell * { + cursor: pointer !important; + pointer-events: none; + } + .badge { + padding: 4px 8px; + border-radius: 4px; + font-size: 12px; + font-weight: 600; + text-transform: uppercase; + pointer-events: none; + } + .badge.passed { background: var(--success-bg); color: var(--success-text); } + .badge.failed { background: var(--error-bg); color: var(--error-text); } + .badge.skipped { background: var(--warning-bg); color: var(--warning-text); } + + /* Runtime Badges */ + .runtime { margin-left: 6px; font-size: 11px; pointer-events: none; } + .runtime-fast { color: #10b981; } + .runtime-medium { color: #f59e0b; } + .runtime-slow { color: #ef4444; } + + /* Stats Column */ + .stats-col, .stats-cell { + position: sticky; + left: 500px; /* after test name */ + background: var(--bg-primary); + border-right: 1px solid var(--border-color); + min-width: 120px; + text-align: center; + font-size: 12px; + z-index: 11; + } + .stats-col { z-index: 12; background: var(--bg-secondary); } + .pass-rate { padding: 4px 8px; border-radius: 4px; margin-bottom: 4px; display: inline-block; font-weight: bold; } + .rate-high { background: var(--success-bg); color: var(--success-text); } + .rate-medium { background: var(--warning-bg); color: var(--warning-text); } + .rate-low { background: var(--error-bg); color: var(--error-text); } + .counts { color: var(--text-secondary); margin-bottom: 2px; } + .avg-time { color: var(--text-tertiary); font-size: 10px; } + + /* Filters */ + .filters { + display: flex; + gap: 16px; + margin-bottom: 20px; + flex-wrap: wrap; + } + .filter-input { + padding: 8px 12px; + border: 1px solid var(--border-secondary); + border-radius: 6px; + background: var(--bg-primary); + color: var(--text-primary); + min-width: 200px; + } + + /* Modal */ + .modal { + display: none; + position: fixed; + top: 0; left: 0; width: 100%; height: 100%; + background: var(--modal-backdrop); + z-index: 1000; + backdrop-filter: blur(2px); + } + .modal-content { + background: var(--bg-primary); + width: 90%; max-width: 1200px; + margin: 20px auto 30px; + border-radius: 12px; + max-height: calc(100vh - 50px); + display: flex; + flex-direction: column; + box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25); + } + .modal-header { + padding: 20px 30px; + border-bottom: 1px solid var(--border-color); + display: flex; + justify-content: space-between; + align-items: center; + flex-shrink: 0; + } + .modal-header h2 { + font-size: 18px; + margin: 0; + } + .modal-body { + padding: 0; + overflow-y: auto; + background: var(--bg-secondary); + flex: 1; + } + .close-btn { + font-size: 28px; + cursor: pointer; + color: var(--text-secondary); + transition: color 0.2s; + } + .close-btn:hover { + color: var(--text-primary); + } + + /* Modal Tabs */ + .modal-tabs { + display: flex; + background: var(--bg-primary); + border-bottom: 1px solid var(--border-color); + padding: 0 30px; + gap: 24px; + } + .modal-tab { + padding: 14px 4px; + background: none; + border: none; + border-bottom: 2px solid transparent; + color: var(--text-secondary); + font-weight: 500; + font-size: 14px; + cursor: pointer; + transition: all 0.2s; + } + .modal-tab:hover { + color: var(--text-primary); + } + .modal-tab.active { + color: var(--accent-color); + border-bottom-color: var(--accent-color); + } + .modal-tab-content { + display: none; + padding: 30px; + } + .modal-tab-content.active { + display: block; + } + + /* Test Detail Sections */ + .test-detail-section { + background: var(--bg-primary); + border-radius: 8px; + padding: 20px; + margin-bottom: 16px; + border: 1px solid var(--border-color); + } + .test-detail-section h4 { + margin: 0 0 16px 0; + font-size: 14px; + font-weight: 600; + color: var(--text-primary); + text-transform: uppercase; + letter-spacing: 0.5px; + } + .detail-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 16px; + } + .detail-item { + display: flex; + flex-direction: column; + } + .detail-label { + font-size: 11px; + color: var(--text-tertiary); + text-transform: uppercase; + letter-spacing: 0.5px; + margin-bottom: 4px; + } + .detail-value { + font-size: 14px; + color: var(--text-primary); + font-weight: 500; + } + .detail-value code { + background: var(--bg-secondary); + padding: 2px 6px; + border-radius: 4px; + font-size: 12px; + } + + /* Artifact List */ + .artifact-list { + display: flex; + flex-direction: column; + gap: 12px; + } + .artifact-item { + background: var(--bg-secondary); + border: 1px solid var(--border-color); + border-radius: 6px; + padding: 16px; + } + .artifact-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 12px; + } + .artifact-name { + font-weight: 600; + font-size: 14px; + color: var(--text-primary); + } + .artifact-meta { + font-size: 11px; + color: var(--text-tertiary); + } + + /* Error Box */ + .error-box { + background: var(--error-bg); + border: 1px solid var(--error-text); + border-radius: 8px; + padding: 16px; + margin-top: 16px; + } + .error-box h4 { + margin: 0 0 12px 0; + color: var(--error-text); + font-size: 14px; + } + .error-box pre { + margin: 0; + color: var(--error-text); + font-size: 12px; + white-space: pre-wrap; + word-break: break-word; + } + + /* Log Viewer */ + .log-viewer { + background: var(--log-bg); + color: var(--log-text); + padding: 0; + border-radius: 8px; + font-family: 'JetBrains Mono', monospace; + font-size: 13px; + max-height: 600px; + overflow-y: auto; + line-height: 1.6; + counter-reset: line-number; + } + .log-viewer.with-line-numbers { + padding-left: 0; + } + .log-line { + display: flex; + padding: 2px 0; + position: relative; + } + .log-line:hover { + background: rgba(59, 130, 246, 0.15); + } + .log-line-number { + counter-increment: line-number; + flex-shrink: 0; + width: 50px; + padding: 0 12px; + text-align: right; + color: var(--text-tertiary); + user-select: none; + border-right: 1px solid var(--border-color); + font-size: 11px; + line-height: 1.6; + } + .log-line-number::before { + content: counter(line-number); + } + .log-line-number:hover { + color: var(--text-secondary); + cursor: pointer; + } + .log-line-content { + flex: 1; + padding: 0 12px; + white-space: pre-wrap; + word-break: break-word; + } + + /* Log syntax highlighting */ + .log-viewer .log-passed { + color: #10b981; + font-weight: 600; + } + .log-viewer .log-failed { + color: #ef4444; + font-weight: 600; + background: rgba(239, 68, 68, 0.1); + padding: 2px 4px; + border-radius: 2px; + } + .log-viewer .log-step { + color: #3b82f6; + font-weight: 600; + } + .log-viewer .log-error { + color: #f97316; + font-weight: 600; + background: rgba(249, 115, 22, 0.1); + padding: 2px 4px; + border-radius: 2px; + } + .log-viewer .log-timestamp { + color: #6366f1; + opacity: 0.8; + font-size: 0.95em; + } + .log-viewer .log-duration { + color: #8b5cf6; + font-weight: 500; + } + .log-viewer .log-command { + color: #06b6d4; + font-style: italic; + } + .log-viewer .log-test-name { + color: #fbbf24; + font-weight: 500; + } + .log-viewer .log-warning { + color: #f59e0b; + font-weight: 500; + } + + /* Test separators in logs */ + .log-test-separator { + border-top: 2px solid var(--border-color); + margin: 20px 0 16px 0; + padding-top: 16px; + position: relative; + } + .log-test-separator::before { + content: '━━━━'; + position: absolute; + top: -13px; + left: 0; + background: var(--log-bg); + padding-right: 10px; + color: var(--border-color); + font-size: 14px; + letter-spacing: 2px; + } + .log-test-header { + font-weight: 600; + color: var(--accent-color); + font-size: 14px; + margin-bottom: 8px; + padding: 8px 12px; + background: var(--bg-hover); + border-left: 3px solid var(--accent-color); + border-radius: 4px; + } + .log-test-header .test-status { + float: right; + font-size: 12px; + padding: 2px 8px; + border-radius: 3px; + font-weight: 600; + } + .log-test-header .test-status.passed { + background: #d1fae5; + color: #065f46; + } + .log-test-header .test-status.failed { + background: #fee2e2; + color: #991b1b; + } + + .log-controls { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 12px; + padding: 10px 12px; + background: linear-gradient(to bottom, var(--bg-primary), var(--bg-hover)); + border: 1px solid var(--border-color); + border-radius: 8px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); + flex-wrap: wrap; + } + .log-controls > strong { + margin-right: 8px; + font-size: 13px; + color: var(--text-primary); + } + .toolbar-separator { + width: 1px; + height: 24px; + background: var(--border-color); + margin: 0 4px; + } + .log-btn { + padding: 4px 12px; + background: var(--log-controls-bg); + border: 1px solid var(--log-border); + color: var(--log-text); + border-radius: 4px; + cursor: pointer; + } + .log-btn.active { background: var(--accent-color); color: white; } + + /* Control groups */ + .log-actions-menu, + .log-filters-menu { + position: relative; + display: inline-flex; + margin-right: 6px; + } + .log-search-controls { + display: flex; + align-items: center; + gap: 4px; + padding: 4px 8px; + background: var(--bg-hover); + border-radius: 6px; + } + .log-nav-buttons { + display: flex; + gap: 4px; + padding: 4px 8px; + background: var(--bg-hover); + border-radius: 6px; + } + .log-display-controls { + display: flex; + gap: 6px; + align-items: center; + padding: 4px 8px; + background: var(--bg-hover); + border-radius: 6px; + } + .log-search-input { + padding: 4px 8px; + border: 1px solid var(--border-color); + border-radius: 4px; + font-size: 11px; + width: 180px; + outline: none; + transition: all 0.2s ease; + } + .log-search-input:focus { + border-color: var(--accent-color); + box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1); + } + .log-search-btn { + padding: 4px 8px; + background: var(--bg-primary); + border: 1px solid var(--border-color); + color: var(--text-secondary); + border-radius: 4px; + cursor: pointer; + font-size: 13px; + transition: all 0.2s ease; + min-width: 28px; + display: flex; + align-items: center; + justify-content: center; + } + .log-search-btn:hover { + background: var(--bg-hover); + color: var(--text-primary); + border-color: var(--accent-color); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + transform: translateY(-1px); + } + .log-search-btn:active { + transform: translateY(0); + box-shadow: none; + } + .log-search-btn:disabled { + opacity: 0.4; + cursor: not-allowed; + } + .search-counter { + font-size: 11px; + color: var(--text-secondary); + min-width: 40px; + text-align: center; + } + /* Search result highlighting */ + .search-highlight { + background: #bfdbfe; + color: #1e3a8a; + border-radius: 2px; + padding: 0 2px; + } + .theme-dark .search-highlight { + background: #1e3a8a; + color: #bfdbfe; + } + .search-highlight-current { + background: #3b82f6; + color: white; + border-radius: 2px; + padding: 0 2px; + font-weight: 600; + box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.3); + } + .theme-dark .search-highlight-current { + background: #60a5fa; + color: #0f172a; + } + + /* Actions and Filters menus */ + .log-actions-btn { + padding: 4px 8px; + background: var(--bg-primary); + border: 1px solid var(--border-color); + color: var(--text-secondary); + border-radius: 4px; + cursor: pointer; + font-size: 16px; + transition: all 0.2s ease; + min-width: 32px; + display: flex; + align-items: center; + justify-content: center; + } + .log-actions-btn:hover { + background: var(--bg-hover); + color: var(--text-primary); + border-color: var(--accent-color); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + transform: translateY(-1px); + } + .log-actions-btn:active { + transform: translateY(0); + box-shadow: none; + } + .log-actions-dropdown { + display: none; + position: absolute; + top: 100%; + left: 0; + margin-top: 4px; + background: var(--bg-primary); + border: 1px solid var(--border-color); + border-radius: 6px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + z-index: 1000; + min-width: 160px; + overflow: hidden; + } + .log-actions-dropdown.show { + display: block; + } + .log-actions-item { + padding: 8px 12px; + cursor: pointer; + font-size: 12px; + color: var(--text-primary); + transition: background 0.15s ease; + display: flex; + align-items: center; + gap: 8px; + } + .log-actions-item:hover { + background: var(--bg-hover); + } + .log-actions-divider { + height: 1px; + background: var(--border-color); + margin: 4px 0; + } + + /* Filters menu */ + .log-filters-btn { + padding: 4px 10px; + background: var(--bg-primary); + border: 1px solid var(--border-color); + color: var(--text-secondary); + border-radius: 4px; + cursor: pointer; + font-size: 11px; + transition: all 0.2s ease; + display: flex; + align-items: center; + gap: 4px; + } + .log-filters-btn:hover { + background: var(--bg-hover); + color: var(--text-primary); + border-color: var(--accent-color); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + transform: translateY(-1px); + } + .log-filters-btn:active { + transform: translateY(0); + box-shadow: none; + } + .log-filters-dropdown { + display: none; + position: absolute; + top: 100%; + left: 0; + margin-top: 4px; + background: var(--bg-primary); + border: 1px solid var(--border-color); + border-radius: 6px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + z-index: 1000; + min-width: 180px; + padding: 8px 0; + overflow: hidden; + } + .log-filters-dropdown.show { + display: block; + } + .log-filters-header { + padding: 4px 12px 8px; + font-size: 11px; + font-weight: 600; + color: var(--text-secondary); + text-transform: uppercase; + border-bottom: 1px solid var(--border-color); + margin-bottom: 4px; + } + .log-filter-item { + padding: 6px 12px; + cursor: pointer; + font-size: 12px; + color: var(--text-primary); + transition: background 0.15s ease; + display: flex; + align-items: center; + gap: 8px; + } + .log-filter-item:hover { + background: var(--bg-hover); + } + .log-filter-item input[type="checkbox"] { + margin: 0; + cursor: pointer; + } + .filter-label { + display: flex; + align-items: center; + gap: 6px; + flex: 1; + } + .filter-count { + margin-left: auto; + font-size: 11px; + color: var(--text-secondary); + background: var(--bg-hover); + padding: 2px 6px; + border-radius: 10px; + } + .log-filters-actions { + display: flex; + gap: 6px; + padding: 8px 12px 4px; + border-top: 1px solid var(--border-color); + margin-top: 4px; + } + .log-filter-action-btn { + flex: 1; + padding: 4px 8px; + background: var(--bg-primary); + border: 1px solid var(--border-color); + color: var(--text-secondary); + border-radius: 4px; + cursor: pointer; + font-size: 11px; + transition: all 0.2s ease; + } + .log-filter-action-btn:hover { + background: var(--bg-hover); + border-color: var(--accent-color); + } + .log-filter-action-btn.primary { + background: var(--accent-color); + color: white; + border-color: var(--accent-color); + } + .log-filter-action-btn.primary:hover { + background: #2563eb; + } + + /* Navigation buttons */ + .log-nav-btn { + padding: 4px 10px; + background: var(--bg-primary); + border: 1px solid var(--border-color); + color: var(--text-secondary); + border-radius: 4px; + cursor: pointer; + font-size: 11px; + transition: all 0.2s ease; + display: flex; + align-items: center; + gap: 4px; + } + .log-nav-btn:hover { + background: var(--bg-hover); + color: var(--text-primary); + border-color: var(--accent-color); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + transform: translateY(-1px); + } + .log-nav-btn:active { + transform: translateY(0); + box-shadow: none; + } + .log-nav-btn:disabled { + opacity: 0.4; + cursor: not-allowed; + } + .log-nav-btn:disabled:hover { + background: var(--bg-primary); + color: var(--text-secondary); + border-color: var(--border-color); + } + .log-nav-btn .nav-icon { + font-size: 12px; + } + .log-nav-btn.error-nav { + border-color: #ef4444; + color: #ef4444; + } + .log-nav-btn.error-nav:hover { + background: #fef2f2; + box-shadow: 0 1px 3px rgba(239, 68, 68, 0.2); + transform: translateY(-1px); + } + + /* Display controls */ + .log-zoom-control { + display: flex; + gap: 2px; + align-items: center; + } + .log-zoom-btn { + padding: 2px 6px; + background: var(--bg-primary); + border: 1px solid var(--border-color); + color: var(--text-secondary); + border-radius: 3px; + cursor: pointer; + font-size: 12px; + font-weight: 600; + transition: all 0.2s ease; + } + .log-zoom-btn:hover { + background: var(--bg-hover); + color: var(--text-primary); + border-color: var(--accent-color); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + transform: scale(1.05); + } + .log-zoom-btn:active { + transform: scale(0.95); + box-shadow: none; + } + .log-zoom-value { + font-size: 11px; + color: var(--text-tertiary); + min-width: 35px; + text-align: center; + } + .log-viewer.font-sm { font-size: 11px; } + .log-viewer.font-md { font-size: 13px; } + .log-viewer.font-lg { font-size: 15px; } + .log-viewer.font-xl { font-size: 17px; } + .log-viewer.wrap-enabled .log-line-content { + white-space: pre-wrap; + word-break: break-word; + } + .log-viewer.wrap-disabled .log-line-content { + white-space: pre; + overflow-x: auto; + } + + /* Floating Controls Panel (Google Maps style) */ + .log-floating-controls { + position: absolute; + bottom: 16px; + right: 16px; + display: flex; + flex-direction: column; + gap: 10px; + z-index: 100; + pointer-events: none; + opacity: 0.3; + transition: opacity 0.2s ease; + } + .log-floating-controls:hover { + opacity: 1; + } + .log-floating-controls > * { + pointer-events: auto; + } + .floating-control-group { + background: rgba(255, 255, 255, 0.95); + border: none; + border-radius: 2px; + padding: 4px; + box-shadow: rgba(0, 0, 0, 0.3) 0px 1px 4px -1px; + display: flex; + flex-direction: column; + gap: 0; + align-items: center; + } + .theme-dark .floating-control-group { + background: rgba(30, 41, 59, 0.75); + box-shadow: rgba(0, 0, 0, 0.4) 0px 2px 6px 0px; + backdrop-filter: blur(8px); + } + .floating-control-divider { + width: 20px; + height: 1px; + background: #e5e7eb; + margin: 4px 0; + } + .theme-dark .floating-control-divider { + background: #4a5568; + } + .floating-zoom-control { + display: flex; + flex-direction: column; + gap: 0; + align-items: center; + } + .floating-zoom-btn { + width: 28px; + height: 28px; + padding: 0; + background: transparent; + border: none; + color: #5f6368; + border-radius: 2px; + cursor: pointer; + font-size: 18px; + font-weight: 400; + line-height: 1; + transition: background 0.1s ease; + display: flex; + align-items: center; + justify-content: center; + } + .floating-zoom-btn:hover { + background: #f1f3f4; + } + .theme-dark .floating-zoom-btn { + color: #e2e8f0; + } + .theme-dark .floating-zoom-btn:hover { + background: #4a5568; + } + .floating-zoom-btn:active { + background: #e8eaed; + } + .theme-dark .floating-zoom-btn:active { + background: #2d3748; + } + .floating-zoom-value { + font-size: 10px; + color: #5f6368; + font-weight: 400; + text-align: center; + min-width: 28px; + padding: 2px 0; + } + .theme-dark .floating-zoom-value { + color: #cbd5e0; + } + .floating-wrap-btn { + width: 28px; + height: 28px; + padding: 0; + background: transparent; + border: none; + color: #5f6368; + border-radius: 2px; + cursor: pointer; + font-size: 14px; + line-height: 1; + transition: background 0.1s ease; + display: flex; + align-items: center; + justify-content: center; + } + .floating-wrap-btn:hover { + background: #f1f3f4; + } + .theme-dark .floating-wrap-btn { + color: #e2e8f0; + } + .theme-dark .floating-wrap-btn:hover { + background: #4a5568; + } + .floating-wrap-btn:active { + background: #e8eaed; + } + .theme-dark .floating-wrap-btn:active { + background: #2d3748; + } + .floating-wrap-btn.active { + background: #e8f0fe; + color: #1a73e8; + } + .theme-dark .floating-wrap-btn.active { + background: #3b82f6; + color: white; + } + .log-line-number-clickable { + position: relative; + cursor: pointer; + } + .log-line-number-clickable:hover { + background: rgba(59, 130, 246, 0.1); + } + + /* pprof Visualization - Compact */ + .pprof-compact-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 12px; + padding: 8px 12px; + background: var(--bg-secondary); + border-radius: 6px; + flex-wrap: wrap; + gap: 8px; + } + .pprof-stats { + display: flex; + align-items: center; + gap: 6px; + font-size: 13px; + color: var(--text-primary); + flex-wrap: wrap; + } + .pprof-stat strong { + color: #3b82f6; + } + .pprof-stat-sep { + color: var(--text-secondary); + font-size: 10px; + } + .pprof-help-btn { + display: flex; + align-items: center; + gap: 4px; + font-size: 11px; + color: var(--text-secondary); + background: transparent; + border: 1px solid var(--border-color); + padding: 4px 8px; + border-radius: 4px; + cursor: pointer; + transition: all 0.2s; + } + .pprof-help-btn:hover { + background: var(--bg-hover); + color: var(--text-primary); + } + .pprof-help-popup { + display: none; + position: absolute; + right: 0; + top: 100%; + margin-top: 4px; + width: 320px; + padding: 12px; + background: #1f2937; + color: #fff; + border-radius: 8px; + font-size: 12px; + line-height: 1.5; + box-shadow: 0 4px 12px rgba(0,0,0,0.3); + z-index: 100; + } + .pprof-help-popup.visible { + display: block; + } + .pprof-help-popup code { + background: rgba(255,255,255,0.1); + padding: 2px 6px; + border-radius: 3px; + font-size: 11px; + } + .pprof-compact-header { + position: relative; + } + .pprof-bars { + display: flex; + flex-direction: column; + gap: 4px; + } + .pprof-bar-item { + display: grid; + grid-template-columns: 150px 1fr; + gap: 8px; + align-items: center; + } + .pprof-bar-label { + font-size: 11px; + color: var(--text-secondary); + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + font-family: monospace; + } + .pprof-bar-container { + position: relative; + height: 18px; + background: var(--bg-secondary); + border-radius: 3px; + overflow: hidden; + } + .pprof-bar { + height: 100%; + border-radius: 3px; + background: linear-gradient(90deg, #3b82f6, #8b5cf6); + } + .pprof-bar-value { + position: absolute; + right: 6px; + top: 50%; + transform: translateY(-50%); + font-size: 10px; + font-weight: 600; + color: var(--text-primary); + } + .pprof-stacks { + display: flex; + flex-direction: column; + gap: 2px; + } + .pprof-stack-item { + background: var(--bg-secondary); + border-radius: 4px; + overflow: hidden; + } + .pprof-stack-header { + display: flex; + align-items: center; + padding: 6px 10px; + cursor: pointer; + gap: 8px; + transition: background 0.2s; + } + .pprof-stack-header:hover { + background: var(--bg-hover); + } + .pprof-stack-count { + background: #10b981; + color: white; + padding: 1px 6px; + border-radius: 8px; + font-size: 10px; + font-weight: 600; + min-width: 32px; + text-align: center; + } + .pprof-stack-name { + flex: 1; + font-size: 11px; + font-family: monospace; + color: var(--text-secondary); + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } + .pprof-stack-toggle { + color: var(--text-secondary); + font-size: 9px; + transition: transform 0.2s; + } + .pprof-stack-item.expanded .pprof-stack-toggle { + transform: rotate(90deg); + } + .pprof-stack-frames { + display: none; + padding: 0 10px 8px 48px; + font-size: 10px; + font-family: monospace; + color: var(--text-secondary); + } + .pprof-stack-item.expanded .pprof-stack-frames { + display: block; + } + .pprof-frame { + padding: 2px 0; + border-left: 2px solid var(--border-color); + padding-left: 10px; + margin-left: 2px; + } + .pprof-section { + margin-bottom: 24px; + } + .pprof-section-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 12px; + } + .pprof-section-title { + display: flex; + align-items: center; + gap: 8px; + font-size: 14px; + font-weight: 600; + color: var(--text-primary); + } + .pprof-help-link { + font-size: 11px; + color: #3b82f6; + text-decoration: none; + display: flex; + align-items: center; + gap: 4px; + } + .pprof-help-link:hover { + text-decoration: underline; + } + .pprof-raw-toggle { + font-size: 11px; + color: var(--text-secondary); + background: var(--bg-secondary); + border: 1px solid var(--border-color); + padding: 4px 10px; + border-radius: 4px; + cursor: pointer; + margin-top: 12px; + } + .pprof-raw-toggle:hover { + background: var(--bg-hover); + } + .pprof-raw-content { + display: none; + margin-top: 12px; + } + .pprof-raw-content.visible { + display: block; + } + + /* Comparison */ + .comparison-run-info { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 20px; + margin-top: 20px; + } + .run-info-card { + background: var(--bg-primary); + padding: 15px; + border-radius: 8px; + border: 1px solid var(--border-color); + } + .run-info-card h4 { + margin: 0 0 10px 0; + color: var(--text-primary); + font-size: 14px; + } + .run-info-details { + font-size: 12px; + color: var(--text-secondary); + } + .run-info-details > div { + margin: 5px 0; + } + .comparison-grid { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + gap: 20px; + margin-top: 20px; + } + .comparison-col { + background: var(--bg-primary); + padding: 20px; + border-radius: 8px; + border: 1px solid var(--border-color); + } + + /* Utility */ + .hidden { display: none !important; } + .theme-toggle { + background: var(--bg-hover); + border: 1px solid var(--border-color); + padding: 8px 12px; + border-radius: 6px; + cursor: pointer; + color: var(--text-primary); + } + + @media print { + .theme-toggle, .tabs, .filters, .log-controls, .close-btn { display: none !important; } + .container { display: block; } + .tab-content { display: block !important; padding: 0; } + .card, .chart-wrapper, .table-wrapper { border: 1px solid #ddd; break-inside: avoid; } + body { background: white; color: black; } + * { box-shadow: none !important; } + } + """ + + JS = """ + // ============================================================ + // SECTION: Icons & Utilities + // ============================================================ + + function svgIcon(name, size = 16, color = 'currentColor') { + const icons = { + 'search': ``, + 'copy': ``, + 'download': ``, + 'error': ``, + 'warning': ``, + 'info': ``, + 'bug': ``, + 'chevron-up': ``, + 'chevron-down': ``, + 'arrow-up': ``, + 'arrow-down': ``, + 'wrap': ``, + 'sun': ``, + 'moon': ``, + }; + return icons[name] || ''; + } + + // ============================================================ + // SECTION: Global State & Navigation + // ============================================================ + + let currentTab = 'dashboard'; + + function switchTab(tabId) { + document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active')); + document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active')); + + document.querySelector(`[onclick="switchTab('${tabId}')"]`).classList.add('active'); + document.getElementById(tabId).classList.add('active'); + currentTab = tabId; + } + + // Theme + function toggleTheme() { + const html = document.documentElement; + const current = html.getAttribute('data-theme'); + const next = current === 'dark' ? 'light' : 'dark'; + html.setAttribute('data-theme', next); + localStorage.setItem('theme', next); + updateThemeButton(); + } + + function updateThemeButton() { + const theme = document.documentElement.getAttribute('data-theme'); + const btn = document.getElementById('themeToggle'); + if (btn) { + btn.innerHTML = theme === 'dark' ? svgIcon('sun', 18) : svgIcon('moon', 18); + btn.title = theme === 'dark' ? 'Switch to light mode' : 'Switch to dark mode'; + } + } + + // Init + document.addEventListener('DOMContentLoaded', () => { + const savedTheme = localStorage.getItem('theme') || + (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'); + document.documentElement.setAttribute('data-theme', savedTheme); + updateThemeButton(); + + renderCharts(); + }); + + // Filtering + function filterTable() { + const search = document.getElementById('searchInput').value.toLowerCase(); + const status = document.getElementById('statusFilter').value; + const stability = document.getElementById('stabilityFilter').value; + const label = document.getElementById('labelFilter').value; + + const rows = document.querySelectorAll('#resultsTable tbody tr'); + + rows.forEach(row => { + const rowData = JSON.parse(row.dataset.json); + const name = rowData.test_name.toLowerCase(); + + // Search + const matchesSearch = name.includes(search); + + // Status (check if ANY run matches) + let matchesStatus = status === 'all'; + if (!matchesStatus) { + matchesStatus = Object.values(rowData.runs).some(r => r && r.state === status); + } + + // Stability + let matchesStability = true; + if (stability === 'flaky') matchesStability = rowData.is_flaky; + if (stability === 'stable') matchesStability = !rowData.is_flaky && rowData.fail_count === 0; + if (stability === 'always-failing') matchesStability = rowData.pass_count === 0; + + // Label + let matchesLabel = label === 'all'; + if (!matchesLabel) { + // Check if any run has this label (simplified) + matchesLabel = Object.values(rowData.runs).some(r => r && r.labels && r.labels.includes(label)); + } + + if (matchesSearch && matchesStatus && matchesStability && matchesLabel) { + row.style.display = ''; + } else { + row.style.display = 'none'; + } + }); + } + + // Modal Tabs + function switchModalTab(tabId) { + // Update tab buttons + document.querySelectorAll('.modal-tab').forEach(btn => btn.classList.remove('active')); + document.querySelector(`[onclick="switchModalTab('${tabId}')"]`).classList.add('active'); + + // Update tab content + document.querySelectorAll('.modal-tab-content').forEach(content => content.classList.remove('active')); + document.getElementById(tabId).classList.add('active'); + } + + // ============================================================ + // SECTION: Modal & Test Details + // ============================================================ + + function showTestDetails(data) { + const modal = document.getElementById('testModal'); + const modalBody = document.getElementById('modalContent'); + + // Check if this is pivot data (flaky tests) or single test data + const isPivotData = data.state === undefined; + + // Build tabs - only Summary tab for pivot data + let html = ` +

agentagent image Image for Vector agent. timberio/vector:0.47.0-distroless-libc by default
hostAliases HostAliases provides mapping between ip and hostnames, that would be propagated to pod.
podSecurityPolicyNamePodSecurityPolicyName - defines name for podSecurityPolicy in case of empty value, prefixedName will be used.
readinessProbe Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. By default - not set
+ + + + + + + + + `; + + Object.entries(data.runs).forEach(([runId, result]) => { + html += ` + + + + + + `; + }); + + html += ` + +
Run IDStatusRuntime
#${runId}${result.state}${result.runtime ? result.runtime.toFixed(2) + 's' : 'N/A'}
+ + + `; + } + + if (data.artifact_metadata) { + const meta = data.artifact_metadata; + html += ` +

+ `; + + if (meta.artifacts) { + html += ` +
+

Artifact Summary

+
+
+
Log Files
+
${meta.artifacts.log_files?.length || 0}
+
+
+
Resource Files
+
${meta.artifacts.resource_files?.length || 0}
+
+
+
Event Files
+
${meta.artifacts.event_files?.length || 0}
+
+
+
Collection Time
+
${meta.artifacts.collection_time || 'N/A'}
+
+
+
+ `; + } + } + + if (data.failure_message) { + html += ` +
+

Failure Message

+
${escapeHtml(data.failure_message)}
+
+ `; + } + + html += ``; // Close summary tab + + // Queue for pprof rendering - declared at function scope for access after DOM update + let pprofRenderQueue = []; + + // Only show these tabs for single test data (not pivot data) + if (!isPivotData) { + // Artifacts Tab + html += ``; // Close artifacts tab + + // Logs Tab + html += ``; // Close logs tab + + // Resources Tab + html += ``; // Close resources tab + } // End if (!isPivotData) + + modalBody.innerHTML = html; + + // Render pprof visualizations after DOM is updated + if (pprofRenderQueue && pprofRenderQueue.length > 0) { + pprofRenderQueue.forEach(item => { + renderPprofFile(item.filename, item.content, item.containerId); + }); + } + + // Apply syntax highlighting to all log viewers + modalBody.querySelectorAll('.log-viewer').forEach(viewer => { + viewer.innerHTML = highlightLogSyntax(viewer.innerHTML); + viewer.classList.add('with-line-numbers'); + // Initialize navigation and display controls for each log viewer + if (viewer.id) { + initLogNavigation(viewer); + initDisplayControls(viewer.id); + } + }); + + // Update modal title + document.getElementById('testModalTitle').textContent = 'Test Run Details'; + + modal.style.display = 'block'; + } + + function formatTimestamp(ts) { + if (!ts) return 'N/A'; + try { + return new Date(ts).toLocaleString(); + } catch { + return ts; + } + } + + // Format duration in human-readable format + function formatDuration(seconds) { + if (!seconds || seconds < 0) return 'N/A'; + + const hours = Math.floor(seconds / 3600); + const minutes = Math.floor((seconds % 3600) / 60); + const secs = Math.floor(seconds % 60); + + if (hours > 0) { + return `${hours}h ${minutes}m ${secs}s`; + } else if (minutes > 0) { + return `${minutes}m ${secs}s`; + } else { + return `${secs}s`; + } + } + + // Copy test name to clipboard + function copyTestName(btn) { + const testName = btn.getAttribute('data-test-name'); + + // Use Clipboard API + if (navigator.clipboard && navigator.clipboard.writeText) { + navigator.clipboard.writeText(testName).then(() => { + // Visual feedback + const originalText = btn.innerHTML; + btn.innerHTML = '✓ Copied!'; + btn.style.background = '#10b981'; + btn.style.color = 'white'; + btn.style.borderColor = '#10b981'; + + setTimeout(() => { + btn.innerHTML = originalText; + btn.style.background = ''; + btn.style.color = ''; + btn.style.borderColor = ''; + }, 1500); + }).catch(err => { + console.error('Failed to copy:', err); + btn.innerHTML = '✗ Failed'; + setTimeout(() => { + btn.innerHTML = originalText; + }, 1500); + }); + } else { + // Fallback for older browsers + const originalText = btn.innerHTML; + const textarea = document.createElement('textarea'); + textarea.value = testName; + textarea.style.position = 'fixed'; + textarea.style.opacity = '0'; + document.body.appendChild(textarea); + textarea.select(); + try { + document.execCommand('copy'); + btn.innerHTML = '✓ Copied!'; + btn.style.background = '#10b981'; + btn.style.color = 'white'; + setTimeout(() => { + btn.innerHTML = originalText; + btn.style.background = ''; + btn.style.color = ''; + }, 1500); + } catch (err) { + console.error('Fallback copy failed:', err); + } + document.body.removeChild(textarea); + } + } + + // Copy to clipboard with visual feedback on the clicked element + function copyWithElementFeedback(text, element) { + const showSuccess = () => { + const originalHTML = element.innerHTML; + element.innerHTML = svgIcon('copy', 12) + ' Copied!'; + element.style.background = '#10b981'; + element.style.color = 'white'; + setTimeout(() => { + element.innerHTML = originalHTML; + element.style.background = ''; + element.style.color = ''; + }, 1500); + }; + + if (navigator.clipboard && navigator.clipboard.writeText) { + navigator.clipboard.writeText(text).then(showSuccess).catch(err => { + console.error('Failed to copy:', err); + fallbackCopyToClipboard(text, 'Copied!'); + }); + } else { + fallbackCopyToClipboard(text, 'Copied!'); + showSuccess(); + } + } + + // Add visual separators between tests + function addTestSeparators(text) { + if (!text) return ''; + + // Split by Ginkgo test separators (lines with multiple dashes) + const lines = text.split('\\n'); + const result = []; + let inTest = false; + let testName = ''; + let testStatus = ''; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + + // Detect test separator line + if (line.match(/^-{30,}$/)) { + // This is a Ginkgo test separator + result.push(line); + inTest = false; + continue; + } + + // Detect test name (lines that start with test description - typically after separator) + // Pattern: "Test Description" followed by optional labels like [smoke, slow] + const testNameMatch = line.match(/^([A-Z][^\\n]+?(?:should|must|can|will|does)[^\\n]*?)(?:\\s+\\[.*?\\])?$/); + if (testNameMatch && !inTest) { + testName = testNameMatch[1].trim(); + // Add separator and header + result.push('
'); + result.push(`
${testName}
`); + result.push(line); + inTest = true; + continue; + } + + // Detect test completion with status + const passedMatch = line.match(/\\[38;5;10m\\[.*?PASSED.*?\\[(\\d+\\.\\d+)\\s*seconds\\].*?\\[0m/); + const failedMatch = line.match(/\\[38;5;9m\\[.*?FAILED.*?\\[(\\d+\\.\\d+)\\s*seconds\\].*?\\[0m/); + + if (passedMatch || failedMatch) { + testStatus = passedMatch ? 'passed' : 'failed'; + const duration = passedMatch ? passedMatch[1] : (failedMatch ? failedMatch[1] : ''); + + // Update header if we have a test name + if (testName && inTest) { + const headerIndex = result.lastIndexOf(`
${testName}
`); + if (headerIndex !== -1) { + result[headerIndex] = `
${testName}${testStatus.toUpperCase()} ${duration}s
`; + } + } + inTest = false; + testName = ''; + } + + result.push(line); + } + + return result.join('\\n'); + } + + // Add line numbers to log output + function addLineNumbers(text) { + if (!text) return ''; + + const lines = text.split('\\n'); + const numberedLines = lines.map(line => { + // Skip empty lines at the end + if (line.trim() === '' && lines[lines.length - 1] === line) { + return ''; + } + return `
${line}
`; + }).filter(line => line !== ''); + + return numberedLines.join(''); + } + + // Highlight log syntax for better readability + function highlightLogSyntax(text) { + if (!text) return ''; + + // First add test separators + text = addTestSeparators(text); + + // Then apply syntax highlighting + text = text + // PASSED/passed with checkmark + .replace(/(\\[38;5;10m\\[.*?PASSED.*?\\[0m|\\bPASSED\\b|✅|passed)/gi, '$1') + // FAILED/failed with cross + .replace(/(\\[38;5;9m\\[.*?FAILED.*?\\[0m|\\bFAILED\\b|❌|✗|failed)/gi, '$1') + // STEP markers + .replace(/(\\[1mSTEP:\\[0m|STEP:)/g, '$1') + // ERROR/error + .replace(/(\\bERROR\\b|\\berror\\b|\\bError\\b|🔥)/gi, '$1') + // Warnings + .replace(/(⚠️|WARNING|warning)/gi, '$1') + // Timestamps like @ 11/19/25 15:35:34.904 + .replace(/(@ \\d{2}\\/\\d{2}\\/\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d+)/g, '$1') + // Duration like [35.588 seconds] or 2.5s + .replace(/(\\[\\d+\\.\\d+ seconds\\]|\\d+\\.\\d+s)/g, '$1') + // kubectl/running commands + .replace(/(running:|kubectl|KUBECTL_CMD:)([^\\n]*)/gi, '$1$2') + // Test names (lines starting with test description) + .replace(/^([▸●] .*$)/gm, '$1'); + + // Finally add line numbers + return addLineNumbers(text); + } + + // ============================================================ + // SECTION: Log Viewer State & Navigation + // ============================================================ + + const logViewerStates = new WeakMap(); + + function getLogViewerState(viewer) { + if (!logViewerStates.has(viewer)) { + logViewerStates.set(viewer, { + // Error navigation + currentErrorIndex: -1, + errorPositions: [], + // Display settings + currentFontIndex: 1, + wrapEnabled: true, + // Search state + searchIndex: -1, + searchMatches: [], + originalContent: null, + // Filter state + filters: { error: true, warning: true, info: true, debug: true } + }); + } + return logViewerStates.get(viewer); + } + + // Legacy accessors for compatibility during refactoring + function getViewerState(viewer) { return getLogViewerState(viewer); } + + function findErrorsInLog(viewer) { + const state = getViewerState(viewer); + state.errorPositions = []; + const uniqueLines = new Set(); + const errorElements = viewer.querySelectorAll('.log-failed, .log-error'); + errorElements.forEach(elem => { + const line = elem.closest('.log-line'); + if (line && !uniqueLines.has(line)) { + uniqueLines.add(line); + state.errorPositions.push(line); + } + }); + return state.errorPositions.length; + } + + function navigateLog(viewer, direction) { + if (direction === 'top') { + viewer.scrollTop = 0; + return; + } + if (direction === 'bottom') { + viewer.scrollTop = viewer.scrollHeight; + return; + } + + // Error navigation + const state = getViewerState(viewer); + if (state.errorPositions.length === 0) { + findErrorsInLog(viewer); + } + if (state.errorPositions.length === 0) { + showNotification('No errors found in logs', 'info'); + return; + } + + if (direction === 'next') { + state.currentErrorIndex = (state.currentErrorIndex + 1) % state.errorPositions.length; + } else if (direction === 'prev') { + state.currentErrorIndex = state.currentErrorIndex <= 0 ? state.errorPositions.length - 1 : state.currentErrorIndex - 1; + } + + scrollToElement(state.errorPositions[state.currentErrorIndex], viewer); + updateNavButtons(viewer); + } + + // Legacy wrappers for compatibility + function jumpToNextError(viewer) { navigateLog(viewer, 'next'); } + function jumpToPrevError(viewer) { navigateLog(viewer, 'prev'); } + function jumpToTop(viewer) { navigateLog(viewer, 'top'); } + function jumpToBottom(viewer) { navigateLog(viewer, 'bottom'); } + + function scrollToElement(element, container) { + if (!element || !container) return; + + // Highlight the line temporarily + const isDark = document.documentElement.classList.contains('theme-dark'); + element.style.transition = 'background 0.3s ease'; + element.style.background = isDark ? 'rgba(59, 130, 246, 0.2)' : 'rgba(147, 197, 253, 0.4)'; + + // Scroll to element + const containerRect = container.getBoundingClientRect(); + const elementRect = element.getBoundingClientRect(); + const scrollTop = container.scrollTop; + const offset = elementRect.top - containerRect.top + scrollTop - 100; + + container.scrollTo({ + top: offset, + behavior: 'smooth' + }); + + // Remove highlight after delay + setTimeout(() => { + element.style.background = ''; + }, 1500); + } + + function updateNavButtons(viewer) { + const state = getViewerState(viewer); + const errorCount = state.errorPositions.length; + + // Derive button IDs from viewer ID + const viewerId = viewer.id; + let prevBtnId, nextBtnId; + + if (viewerId.startsWith('testLogViewer')) { + // Extract index from testLogViewer0, testLogViewer1, etc. + const index = viewerId.replace('testLogViewer', ''); + prevBtnId = `testNavPrevError${index}`; + nextBtnId = `testNavNextError${index}`; + } else { + // Default for run log viewer + prevBtnId = 'navPrevError'; + nextBtnId = 'navNextError'; + } + + const prevBtn = document.getElementById(prevBtnId); + const nextBtn = document.getElementById(nextBtnId); + + if (prevBtn && nextBtn) { + const countText = errorCount > 0 ? ` (${state.currentErrorIndex + 1}/${errorCount})` : ' (0)'; + prevBtn.disabled = errorCount === 0; + nextBtn.disabled = errorCount === 0; + + // Update button text with current position + if (errorCount > 0) { + nextBtn.innerHTML = ` Next Error ${countText}`; + prevBtn.innerHTML = ` Prev Error`; + } + } + } + + function initLogNavigation(viewer) { + const errorCount = findErrorsInLog(viewer); + const state = getViewerState(viewer); + state.currentErrorIndex = -1; + updateNavButtons(viewer); + return errorCount; + } + + // Display control functions + const fontSizes = ['font-sm', 'font-md', 'font-lg', 'font-xl']; + const fontLabels = ['85%', '100%', '115%', '130%']; + + function getDisplayState(viewer) { return getLogViewerState(viewer); } + + function zoom(viewerId, delta) { + const viewer = document.getElementById(viewerId); + if (!viewer) return; + + const state = getDisplayState(viewer); + const newIndex = state.currentFontIndex + delta; + if (newIndex < 0 || newIndex >= fontSizes.length) return; + + viewer.classList.remove(fontSizes[state.currentFontIndex]); + state.currentFontIndex = newIndex; + viewer.classList.add(fontSizes[state.currentFontIndex]); + updateZoomDisplay(viewerId); + } + + function updateZoomDisplay(viewerId) { + const viewer = document.getElementById(viewerId); + if (!viewer) return; + + const state = getDisplayState(viewer); + + // Derive zoom display ID from viewer ID + let displayId; + if (viewerId.startsWith('testLogViewer')) { + const index = viewerId.replace('testLogViewer', ''); + displayId = `testZoomDisplay${index}`; + } else { + displayId = 'zoomDisplay'; + } + + const display = document.getElementById(displayId); + if (display) { + display.textContent = fontLabels[state.currentFontIndex]; + } + } + + function toggleWrap(viewerId) { + const viewer = document.getElementById(viewerId); + if (!viewer) return; + + const state = getDisplayState(viewer); + + // Derive wrap button ID from viewer ID + let btnId; + if (viewerId.startsWith('testLogViewer')) { + const index = viewerId.replace('testLogViewer', ''); + btnId = `testWrapToggleBtn${index}`; + } else { + btnId = 'wrapToggleBtn'; + } + + const btn = document.getElementById(btnId); + if (!btn) return; + + state.wrapEnabled = !state.wrapEnabled; + + if (state.wrapEnabled) { + viewer.classList.remove('wrap-disabled'); + viewer.classList.add('wrap-enabled'); + btn.classList.add('active'); + btn.title = 'Line wrapping: ON (click to turn off)'; + // Update text for non-floating buttons + if (!btn.classList.contains('floating-wrap-btn')) { + btn.textContent = '↩ Wrap: On'; + } + } else { + viewer.classList.remove('wrap-enabled'); + viewer.classList.add('wrap-disabled'); + btn.classList.remove('active'); + btn.title = 'Line wrapping: OFF (click to turn on)'; + // Update text for non-floating buttons + if (!btn.classList.contains('floating-wrap-btn')) { + btn.textContent = '→ Wrap: Off'; + } + } + } + + function initDisplayControls(viewerId) { + const viewer = document.getElementById(viewerId); + if (!viewer) return; + + // Initialize state + const state = getDisplayState(viewer); + + // Set initial font size + viewer.classList.add(fontSizes[state.currentFontIndex]); + + // Set initial wrap state + if (state.wrapEnabled) { + viewer.classList.add('wrap-enabled'); + + // Set active class for wrap button + let btnId; + if (viewerId.startsWith('testLogViewer')) { + const index = viewerId.replace('testLogViewer', ''); + btnId = `testWrapToggleBtn${index}`; + } else { + btnId = 'wrapToggleBtn'; + } + const btn = document.getElementById(btnId); + if (btn) { + btn.classList.add('active'); + btn.title = 'Line wrapping: ON (click to turn off)'; + } + } + + // Add click handlers to line numbers + viewer.addEventListener('click', (e) => { + const lineNumber = e.target.closest('.log-line-number'); + if (lineNumber) { + const line = lineNumber.closest('.log-line'); + if (line) { + const lineContent = line.querySelector('.log-line-content'); + if (lineContent) { + const lineNum = Array.from(viewer.querySelectorAll('.log-line')).indexOf(line) + 1; + copyLineContent(lineNum, lineContent.textContent); + } + } + } + }); + + // Make line numbers clickable + viewer.querySelectorAll('.log-line-number').forEach(num => { + num.classList.add('log-line-number-clickable'); + }); + + // Update display + updateZoomDisplay(viewerId); + } + + function copyLineContent(lineNumber, lineContent) { + const textToCopy = `Line ${lineNumber}: ${lineContent}`; + + if (navigator.clipboard && navigator.clipboard.writeText) { + navigator.clipboard.writeText(textToCopy).then(() => { + showCopyNotification('Line copied!'); + }).catch(err => { + console.error('Failed to copy:', err); + }); + } else { + // Fallback + const textarea = document.createElement('textarea'); + textarea.value = textToCopy; + textarea.style.position = 'fixed'; + textarea.style.opacity = '0'; + document.body.appendChild(textarea); + textarea.select(); + try { + document.execCommand('copy'); + showCopyNotification('Line copied!'); + } catch (err) { + console.error('Fallback copy failed:', err); + } + document.body.removeChild(textarea); + } + } + + function showNotification(message, type = 'success') { + const colors = { + success: '#10b981', + warning: '#f59e0b', + error: '#ef4444', + info: '#3b82f6' + }; + const notification = document.createElement('div'); + notification.textContent = message; + notification.style.cssText = `position:fixed;top:20px;right:20px;background:${colors[type] || colors.info};color:white;padding:10px 18px;border-radius:6px;font-size:13px;z-index:10000;box-shadow:0 4px 12px rgba(0,0,0,0.2);`; + document.body.appendChild(notification); + + setTimeout(() => { + notification.style.transition = 'opacity 0.3s ease'; + notification.style.opacity = '0'; + setTimeout(() => document.body.removeChild(notification), 300); + }, 2000); + } + + function showCopyNotification(message) { showNotification(message, 'success'); } + + // Search functionality + function getSearchState(viewer) { + const state = getLogViewerState(viewer); + // Map unified state fields to search-specific names for compatibility + return { + get currentIndex() { return state.searchIndex; }, + set currentIndex(v) { state.searchIndex = v; }, + get matches() { return state.searchMatches; }, + set matches(v) { state.searchMatches = v; }, + get originalContent() { return state.originalContent; }, + set originalContent(v) { state.originalContent = v; } + }; + } + + function performSearch(viewerId) { + const viewer = document.getElementById(viewerId); + if (!viewer) return; + + const state = getSearchState(viewer); + + // Get search input ID + let searchInputId, searchCounterId; + if (viewerId === 'runLogViewer') { + searchInputId = 'runLogSearchInput'; + searchCounterId = 'runLogSearchCounter'; + } else { + const index = viewerId.replace('testLogViewer', ''); + searchInputId = `testLogSearchInput${index}`; + searchCounterId = `testLogSearchCounter${index}`; + } + + const searchInput = document.getElementById(searchInputId); + const searchCounter = document.getElementById(searchCounterId); + + if (!searchInput || !searchCounter) return; + + const searchText = searchInput.value.trim(); + + // Save original content on first search + if (!state.originalContent) { + state.originalContent = viewer.innerHTML; + } + + // Clear previous search + viewer.innerHTML = state.originalContent; + state.matches = []; + state.currentIndex = -1; + + // Reset error navigation after DOM is rebuilt + const viewerState = getViewerState(viewer); + if (viewerState) { + viewerState.errorPositions = []; + viewerState.currentErrorIndex = -1; + } + + if (!searchText) { + searchCounter.textContent = '0/0'; + return; + } + + // Find and highlight all matches + const lines = viewer.querySelectorAll('.log-line'); + let matchCount = 0; + + lines.forEach(line => { + const contentDiv = line.querySelector('.log-line-content'); + if (!contentDiv) return; + + const text = contentDiv.textContent; + const lowerText = text.toLowerCase(); + const lowerSearch = searchText.toLowerCase(); + let index = 0; + let html = ''; + let lastIndex = 0; + + while ((index = lowerText.indexOf(lowerSearch, lastIndex)) !== -1) { + // Add text before match + html += escapeHtml(text.substring(lastIndex, index)); + // Add highlighted match + html += `${escapeHtml(text.substring(index, index + searchText.length))}`; + state.matches.push({ line, index: matchCount }); + matchCount++; + lastIndex = index + searchText.length; + } + + if (lastIndex > 0) { + html += escapeHtml(text.substring(lastIndex)); + contentDiv.innerHTML = html; + } + }); + + // Update counter + searchCounter.textContent = matchCount > 0 ? `1/${matchCount}` : '0/0'; + + // Highlight first match + if (matchCount > 0) { + state.currentIndex = 0; + highlightCurrentMatch(viewer, 0); + } + } + + function escapeHtml(text) { + const div = document.createElement('div'); + div.textContent = text; + return div.innerHTML; + } + + function highlightCurrentMatch(viewer, index) { + // Remove previous current highlight + const prevCurrent = viewer.querySelector('.search-highlight-current'); + if (prevCurrent) { + prevCurrent.classList.remove('search-highlight-current'); + prevCurrent.classList.add('search-highlight'); + } + + // Highlight new current match + const allHighlights = viewer.querySelectorAll('.search-highlight'); + if (index >= 0 && index < allHighlights.length) { + const currentHighlight = allHighlights[index]; + currentHighlight.classList.remove('search-highlight'); + currentHighlight.classList.add('search-highlight-current'); + + // Scroll to match + const line = currentHighlight.closest('.log-line'); + if (line) { + scrollToElement(line, viewer); + } + } + } + + function nextSearchResult(viewerId) { + const viewer = document.getElementById(viewerId); + if (!viewer) return; + + const state = getSearchState(viewer); + if (state.matches.length === 0) return; + + state.currentIndex = (state.currentIndex + 1) % state.matches.length; + highlightCurrentMatch(viewer, state.currentIndex); + updateSearchCounter(viewerId, state.currentIndex + 1, state.matches.length); + } + + function prevSearchResult(viewerId) { + const viewer = document.getElementById(viewerId); + if (!viewer) return; + + const state = getSearchState(viewer); + if (state.matches.length === 0) return; + + state.currentIndex = state.currentIndex - 1; + if (state.currentIndex < 0) { + state.currentIndex = state.matches.length - 1; + } + highlightCurrentMatch(viewer, state.currentIndex); + updateSearchCounter(viewerId, state.currentIndex + 1, state.matches.length); + } + + function updateSearchCounter(viewerId, current, total) { + let searchCounterId; + if (viewerId === 'runLogViewer') { + searchCounterId = 'runLogSearchCounter'; + } else { + const index = viewerId.replace('testLogViewer', ''); + searchCounterId = `testLogSearchCounter${index}`; + } + + const searchCounter = document.getElementById(searchCounterId); + if (searchCounter) { + searchCounter.textContent = `${current}/${total}`; + } + } + + // Actions menu functionality + function toggleActionsMenu(viewerId) { + const menuId = `actionsMenu_${viewerId}`; + const menu = document.getElementById(menuId); + if (!menu) return; + + // Close all other menus first + document.querySelectorAll('.log-actions-dropdown').forEach(m => { + if (m.id !== menuId) { + m.classList.remove('show'); + } + }); + + // Toggle current menu + menu.classList.toggle('show'); + } + + // Close menus when clicking outside + document.addEventListener('click', (e) => { + if (!e.target.closest('.log-actions-menu')) { + document.querySelectorAll('.log-actions-dropdown').forEach(m => { + m.classList.remove('show'); + }); + } + if (!e.target.closest('.log-filters-menu')) { + document.querySelectorAll('.log-filters-dropdown').forEach(m => { + m.classList.remove('show'); + }); + } + }); + + function copyAllLog(viewerId) { + const viewer = document.getElementById(viewerId); + if (!viewer) return; + + // Get all text content from log lines + const lines = viewer.querySelectorAll('.log-line-content'); + const text = Array.from(lines).map(line => line.textContent).join('\\n'); + + copyToClipboard(text, 'All log content copied!'); + toggleActionsMenu(viewerId); // Close menu after action + } + + function copyVisibleLog(viewerId) { + const viewer = document.getElementById(viewerId); + if (!viewer) return; + + // Get visible text content (respecting any filters/search) + const text = viewer.textContent; + + copyToClipboard(text, 'Visible log content copied!'); + toggleActionsMenu(viewerId); + } + + function copyToClipboard(text, message) { + if (navigator.clipboard && navigator.clipboard.writeText) { + navigator.clipboard.writeText(text).then(() => { + showCopyNotification(message); + }).catch(err => { + console.error('Failed to copy:', err); + fallbackCopyToClipboard(text, message); + }); + } else { + fallbackCopyToClipboard(text, message); + } + } + + function fallbackCopyToClipboard(text, message) { + const textarea = document.createElement('textarea'); + textarea.value = text; + textarea.style.position = 'fixed'; + textarea.style.opacity = '0'; + document.body.appendChild(textarea); + textarea.select(); + try { + document.execCommand('copy'); + showCopyNotification(message); + } catch (err) { + console.error('Fallback copy failed:', err); + } + document.body.removeChild(textarea); + } + + function downloadLog(viewerId, extension, filename) { + const viewer = document.getElementById(viewerId); + if (!viewer) return; + + // Get all text content from log lines + const lines = viewer.querySelectorAll('.log-line-content'); + const text = Array.from(lines).map(line => line.textContent).join('\\n'); + + // Create blob and download + const blob = new Blob([text], { type: 'text/plain' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = `${filename}.${extension}`; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + + showCopyNotification(`Downloaded ${filename}.${extension}`); + toggleActionsMenu(viewerId); + } + + // Filters functionality + function getFilterState(viewer) { + return getLogViewerState(viewer).filters; + } + + function toggleFiltersMenu(viewerId) { + const menuId = `filtersMenu_${viewerId}`; + const menu = document.getElementById(menuId); + if (!menu) return; + + // Close all other menus + document.querySelectorAll('.log-filters-dropdown, .log-actions-dropdown').forEach(m => { + if (m.id !== menuId) { + m.classList.remove('show'); + } + }); + + // Toggle current menu + const wasOpen = menu.classList.contains('show'); + menu.classList.toggle('show'); + + // Count logs when opening + if (!wasOpen) { + const viewer = document.getElementById(viewerId); + if (viewer) { + countLogLevels(viewer, menuId); + } + } + } + + function countLogLevels(viewer, menuId) { + const menu = document.getElementById(menuId); + if (!menu) return; + + const counts = { + error: 0, + warning: 0, + info: 0, + debug: 0 + }; + + const lines = viewer.querySelectorAll('.log-line'); + lines.forEach(line => { + const text = line.textContent.toLowerCase(); + if (text.includes('error') || text.includes('failed') || text.includes('✗') || text.includes('❌')) { + counts.error++; + } else if (text.includes('warn')) { + counts.warning++; + } else if (text.includes('info') || text.includes('✓') || text.includes('✅')) { + counts.info++; + } else if (text.includes('debug')) { + counts.debug++; + } else { + counts.info++; // Default to info + } + }); + + // Update counts in UI + Object.keys(counts).forEach(level => { + const countSpan = menu.querySelector(`.filter-count[data-level="${level}"]`); + if (countSpan) { + countSpan.textContent = counts[level]; + } + }); + } + + function updateFilterState(viewerId) { + // This is called on checkbox change, but we apply filters on "Apply" click + // Just keep it for future real-time filtering if needed + } + + function applyFilters(viewerId) { + const viewer = document.getElementById(viewerId); + const menuId = `filtersMenu_${viewerId}`; + const menu = document.getElementById(menuId); + if (!viewer || !menu) return; + + // Get checked states + const checkboxes = menu.querySelectorAll('input[type="checkbox"]'); + const enabledLevels = { + error: false, + warning: false, + info: false, + debug: false + }; + + checkboxes.forEach(cb => { + const level = cb.getAttribute('data-level'); + if (level) { + enabledLevels[level] = cb.checked; + } + }); + + // Save state + const state = getFilterState(viewer); + Object.assign(state, enabledLevels); + + // Apply filters to log lines + const lines = viewer.querySelectorAll('.log-line'); + lines.forEach(line => { + const text = line.textContent.toLowerCase(); + let shouldShow = false; + + if (enabledLevels.error && (text.includes('error') || text.includes('failed') || text.includes('✗') || text.includes('❌'))) { + shouldShow = true; + } else if (enabledLevels.warning && text.includes('warn')) { + shouldShow = true; + } else if (enabledLevels.debug && text.includes('debug')) { + shouldShow = true; + } else if (enabledLevels.info) { + // Show everything else if info is enabled + shouldShow = true; + } + + line.style.display = shouldShow ? '' : 'none'; + }); + + // Close menu + toggleFiltersMenu(viewerId); + } + + function resetFilters(viewerId) { + const viewer = document.getElementById(viewerId); + const menuId = `filtersMenu_${viewerId}`; + const menu = document.getElementById(menuId); + if (!viewer || !menu) return; + + // Check all checkboxes + const checkboxes = menu.querySelectorAll('input[type="checkbox"]'); + checkboxes.forEach(cb => { + cb.checked = true; + }); + + // Show all lines + const lines = viewer.querySelectorAll('.log-line'); + lines.forEach(line => { + line.style.display = ''; + }); + + // Reset state + const state = getFilterState(viewer); + state.error = true; + state.warning = true; + state.info = true; + state.debug = true; + } + + function showRunDetails(runData) { + const modal = document.getElementById('testModal'); + const modalBody = document.getElementById('modalContent'); + + const passRate = runData.total_tests > 0 ? ((runData.passed_tests / runData.total_tests) * 100).toFixed(1) : '0.0'; + + let html = ` + + + + + + `; + + modalBody.innerHTML = html; + + // Apply syntax highlighting to run log viewer + const runLogViewer = document.getElementById('runLogViewer'); + if (runLogViewer) { + runLogViewer.innerHTML = highlightLogSyntax(runLogViewer.innerHTML); + runLogViewer.classList.add('with-line-numbers'); + // Initialize navigation and display controls after syntax highlighting + initLogNavigation(runLogViewer); + initDisplayControls('runLogViewer'); + } + + document.getElementById('testModalTitle').textContent = 'Run #' + runData.run_id + ' Details'; + modal.style.display = 'block'; + } + + function closeModal(id) { + if (!id) id = 'testModal'; + document.getElementById(id).style.display = 'none'; + } + + // ============================================================ + // SECTION: pprof Visualization + // ============================================================ + + function parsePprofHeap(content) { + const lines = content.split('\\n'); + const result = { totalSize: 0, totalObjects: 0, entries: [] }; + + // Parse header: heap profile: 12: 323920 [125: 1251160] @ heap/1048576 + const headerMatch = lines[0]?.match(/heap profile: (\\d+): (\\d+) \\[(\\d+): (\\d+)\\]/); + if (headerMatch) { + result.liveObjects = parseInt(headerMatch[1]); + result.liveSize = parseInt(headerMatch[2]); + result.totalObjects = parseInt(headerMatch[3]); + result.totalSize = parseInt(headerMatch[4]); + } + + // Parse entries + let currentEntry = null; + for (let i = 1; i < lines.length; i++) { + const line = lines[i]; + // Entry line: 1: 278528 [1: 278528] @ 0x... + const entryMatch = line.match(/^(\\d+): (\\d+) \\[(\\d+): (\\d+)\\] @/); + if (entryMatch) { + if (currentEntry) result.entries.push(currentEntry); + currentEntry = { + liveObjects: parseInt(entryMatch[1]), + liveSize: parseInt(entryMatch[2]), + totalObjects: parseInt(entryMatch[3]), + totalSize: parseInt(entryMatch[4]), + stack: [] + }; + } else if (line.startsWith('#') && currentEntry) { + // Stack frame: # 0x6dfa4b k8s.io/api/core/v1.(*Secret).Unmarshal+0x100b + const frameMatch = line.match(/#\\s+0x[0-9a-f]+\\s+(.+?)(?:\\s+\\/|$)/); + if (frameMatch) { + currentEntry.stack.push(frameMatch[1].trim()); + } + } + } + if (currentEntry) result.entries.push(currentEntry); + + // Sort by live size descending + result.entries.sort((a, b) => b.liveSize - a.liveSize); + return result; + } + + function parsePprofGoroutine(content) { + const lines = content.split('\\n'); + const result = { totalGoroutines: 0, stacks: [] }; + + // Parse header: goroutine profile: total 15 + const headerMatch = lines[0]?.match(/goroutine profile: total (\\d+)/); + if (headerMatch) { + result.totalGoroutines = parseInt(headerMatch[1]); + } + + // Parse entries + let currentStack = null; + for (let i = 1; i < lines.length; i++) { + const line = lines[i]; + // Entry line: 1 @ 0x... or count @ 0x... + const entryMatch = line.match(/^(\\d+) @/); + if (entryMatch) { + if (currentStack) result.stacks.push(currentStack); + currentStack = { + count: parseInt(entryMatch[1]), + frames: [] + }; + } else if (line.startsWith('#') && currentStack) { + const frameMatch = line.match(/#\\s+0x[0-9a-f]+\\s+(.+?)(?:\\s+\\/|$)/); + if (frameMatch) { + currentStack.frames.push(frameMatch[1].trim()); + } + } + } + if (currentStack) result.stacks.push(currentStack); + + // Sort by count descending + result.stacks.sort((a, b) => b.count - a.count); + return result; + } + + function formatBytes(bytes) { + if (bytes === 0) return '0 B'; + const k = 1024; + const sizes = ['B', 'KB', 'MB', 'GB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; + } + + function renderPprofHeap(parsed, containerId) { + const container = document.getElementById(containerId); + if (!container || !parsed) return; + + const topEntries = parsed.entries.slice(0, 8); + const maxSize = topEntries[0]?.liveSize || 1; + + let html = ` +
+
+ ${formatBytes(parsed.liveSize)} live + + ${parsed.liveObjects?.toLocaleString() || 0} objects + + ${formatBytes(parsed.totalSize)} total allocated +
+ +
+ How to interpret heap profile:
+ • Live memory — currently allocated and in use
+ • Top allocators — functions allocating most memory
+ • Look for unexpected large allocations or memory leaks

+ Deep analysis:
+ go tool pprof heap.pb.gz
+ Commands: top, web, list funcName +
+
+
+ `; + + topEntries.forEach((entry, idx) => { + const pct = (entry.liveSize / maxSize * 100).toFixed(0); + const funcName = entry.stack[0] || 'unknown'; + const shortName = funcName.split('.').pop() || funcName; + html += ` +
+
${escapeHtml(shortName)}
+
+
+ ${formatBytes(entry.liveSize)} +
+
+ `; + }); + + html += '
'; + container.innerHTML = html; + } + + function renderPprofGoroutine(parsed, containerId) { + const container = document.getElementById(containerId); + if (!container || !parsed) return; + + let html = ` +
+
+ ${parsed.totalGoroutines} goroutines + + ${parsed.stacks.length} unique stacks +
+ +
+ How to interpret goroutine profile:
+ • Count (Nx) — number of goroutines with same stack
+ • High counts may indicate goroutine leaks
+ • Click stack to see full call trace

+ What to look for:
+ • Blocked goroutines (waiting on channels/mutexes)
+ • Unexpected goroutine accumulation over time
+ • Goroutines stuck in infinite loops +
+
+
+ `; + + parsed.stacks.slice(0, 12).forEach((stack, idx) => { + const topFrame = stack.frames[0] || 'unknown'; + const shortName = topFrame.split('.').pop() || topFrame; + html += ` +
+
+ ${stack.count}x + ${escapeHtml(shortName)} + +
+
+ ${stack.frames.map(f => `
${escapeHtml(f)}
`).join('')} +
+
+ `; + }); + + html += '
'; + container.innerHTML = html; + } + + function isPprofFile(filename) { + return filename.includes('pprof-heap') || filename.includes('pprof-goroutine'); + } + + function renderPprofFile(filename, content, containerId) { + if (filename.includes('pprof-heap')) { + renderPprofHeap(parsePprofHeap(content), containerId); + } else if (filename.includes('pprof-goroutine')) { + renderPprofGoroutine(parsePprofGoroutine(content), containerId); + } + } + + function copyAllTestNames() { + const rows = document.querySelectorAll('#resultsTable tbody tr'); + let names = []; + rows.forEach(row => { + if (row.style.display !== 'none') { + const data = JSON.parse(row.dataset.json); + names.push(data.test_name); + } + }); + + if (names.length > 0) { + navigator.clipboard.writeText(names.join('\\n')).then(() => { + const btn = document.querySelector('button[onclick="copyAllTestNames()"]'); + const originalText = btn.innerHTML; + btn.innerHTML = '✓ Copied ' + names.length + ' tests!'; + setTimeout(() => { + btn.innerHTML = originalText; + }, 2000); + }); + } else { + showNotification('No tests visible to copy', 'warning'); + } + } + + function ansiToHtml(text) { + if (!text) return ''; + + // Basic colors + const colors = { + 30: 'black', 31: '#ef4444', 32: '#10b981', 33: '#f59e0b', + 34: '#3b82f6', 35: '#d946ef', 36: '#06b6d4', 37: '#f8fafc', + 90: '#64748b', 91: '#f87171', 92: '#34d399', 93: '#fbbf24', + 94: '#60a5fa', 95: '#e879f9', 96: '#22d3ee', 97: '#ffffff' + }; + + // Backgrounds (40-47: normal, 100-107: bright) + const bgColors = { + 40: '#000000', 41: '#7f1d1d', 42: '#064e3b', 43: '#78350f', + 44: '#1e3a8a', 45: '#581c87', 46: '#164e63', 47: '#e5e7eb', + 100: '#374151', 101: '#991b1b', 102: '#065f46', 103: '#92400e', + 104: '#1e40af', 105: '#6b21a8', 106: '#0e7490', 107: '#f9fafb' + }; + + let html = ''; + let currentStyle = []; + + // Split by escape sequences + const parts = text.split(/(\\x1b\\[[0-9;]*m)/g); + + for (const part of parts) { + if (part.startsWith('\\x1b[')) { + const codes = part.slice(2, -1).split(';').map(Number); + for (const code of codes) { + if (code === 0) currentStyle = []; + else if (code === 1) currentStyle.push('font-weight:bold'); + else if (code === 2) currentStyle.push('opacity:0.7'); + else if (code === 4) currentStyle.push('text-decoration:underline'); + else if (colors[code]) currentStyle.push(`color:${colors[code]}`); + else if (bgColors[code]) currentStyle.push(`background-color:${bgColors[code]}`); + } + } else { + if (part) { + const style = currentStyle.length ? ` style="${currentStyle.join(';')}"` : ''; + html += `${escapeHtml(part)}`; + } + } + } + return html || text; + } + + // ============================================================ + // SECTION: Charts & Comparison + // ============================================================ + + function renderCharts() { + if (typeof Chart === 'undefined') return; + + const ctxRate = document.getElementById('passRateChart').getContext('2d'); + const ctxDuration = document.getElementById('durationChart').getContext('2d'); + + new Chart(ctxRate, { + type: 'line', + data: window.chartData.passRate, + options: { responsive: true, maintainAspectRatio: false } + }); + + new Chart(ctxDuration, { + type: 'line', + data: window.chartData.duration, + options: { responsive: true, maintainAspectRatio: false } + }); + } + + // Comparison + // Get run data by ID + function getRunData(runId) { + return window.chartData.runs.find(r => r.run_id == runId); + } + + // Update run info cards + function updateRunInfo() { + const runA = document.getElementById('runASelect').value; + const runB = document.getElementById('runBSelect').value; + + const runAData = getRunData(runA); + const runBData = getRunData(runB); + + if (runAData) { + const passRate = ((runAData.passed / runAData.total) * 100).toFixed(1); + document.getElementById('runAInfo').innerHTML = ` +
Run ID: ${runAData.run_id}
+
Date: ${new Date(runAData.timestamp).toLocaleString()}
+
Total Tests: ${runAData.total}
+
Passed: ${runAData.passed}
+
Failed: ${runAData.failed}
+
Pass Rate: ${passRate}%
+ `; + } + + if (runBData) { + const passRate = ((runBData.passed / runBData.total) * 100).toFixed(1); + document.getElementById('runBInfo').innerHTML = ` +
Run ID: ${runBData.run_id}
+
Date: ${new Date(runBData.timestamp).toLocaleString()}
+
Total Tests: ${runBData.total}
+
Passed: ${runBData.passed}
+
Failed: ${runBData.failed}
+
Pass Rate: ${passRate}%
+ `; + } + } + + function compareRuns() { + const runA = document.getElementById('runASelect').value; + const runB = document.getElementById('runBSelect').value; + + if (runA === runB) { + showNotification('Please select different runs to compare', 'warning'); + return; + } + + const newFailures = []; + const fixedTests = []; + const regressions = []; + + let totalRuntimeA = 0; + let totalRuntimeB = 0; + let testsCompared = 0; + + window.pivotData.forEach(row => { + const resA = row.runs[runA]; + const resB = row.runs[runB]; + + if (resA && resB) { + // Track runtime + if (resA.runtime) totalRuntimeA += resA.runtime; + if (resB.runtime) totalRuntimeB += resB.runtime; + testsCompared++; + + // New failures: A passed, B failed + if (resA.state === 'passed' && resB.state === 'failed') { + newFailures.push({...row, runtimeA: resA.runtime, runtimeB: resB.runtime}); + } + // Fixed tests: A failed, B passed + if (resA.state === 'failed' && resB.state === 'passed') { + fixedTests.push({...row, runtimeA: resA.runtime, runtimeB: resB.runtime}); + } + // Regressions: was flaky or occasionally failing, now consistently failing + if (row.is_flaky && resB.state === 'failed' && row.fail_count > 1) { + regressions.push({...row, runtimeA: resA.runtime, runtimeB: resB.runtime}); + } + } + }); + + // Update summary cards + document.getElementById('comparisonSummary').style.display = 'grid'; + document.getElementById('newFailuresCount').textContent = newFailures.length; + document.getElementById('fixedTestsCount').textContent = fixedTests.length; + document.getElementById('regressionsCount').textContent = regressions.length; + + // Calculate runtime diff + const runtimeDiff = totalRuntimeB - totalRuntimeA; + const runtimeDiffPercent = totalRuntimeA > 0 ? ((runtimeDiff / totalRuntimeA) * 100).toFixed(1) : 0; + const runtimeColor = runtimeDiff > 0 ? '#ef4444' : runtimeDiff < 0 ? '#10b981' : 'var(--text-primary)'; + const runtimeSign = runtimeDiff > 0 ? '+' : ''; + document.getElementById('runtimeDiff').innerHTML = `${runtimeSign}${runtimeDiff.toFixed(1)}s (${runtimeSign}${runtimeDiffPercent}%)`; + + // Render lists + document.getElementById('newFailuresList').innerHTML = newFailures.map(r => + `
+
${escapeHtml(r.test_name)}
+
+ Runtime: ${r.runtimeA ? r.runtimeA.toFixed(2) + 's' : 'N/A'} → ${r.runtimeB ? r.runtimeB.toFixed(2) + 's' : 'N/A'} +
+
` + ).join('') || '
No new failures
'; + + document.getElementById('fixedTestsList').innerHTML = fixedTests.map(r => + `
+
${escapeHtml(r.test_name)}
+
+ Runtime: ${r.runtimeA ? r.runtimeA.toFixed(2) + 's' : 'N/A'} → ${r.runtimeB ? r.runtimeB.toFixed(2) + 's' : 'N/A'} +
+
` + ).join('') || '
No fixed tests
'; + + document.getElementById('regressionsList').innerHTML = regressions.map(r => + `
+
${escapeHtml(r.test_name)}
+
+ Flakiness: ${r.flakiness_score ? r.flakiness_score.toFixed(0) + '% stable' : 'N/A'} | Fails: ${r.fail_count}/${r.total_runs} +
+
` + ).join('') || '
No regressions detected
'; + } + + // Initialize on load + document.addEventListener('DOMContentLoaded', function() { + updateRunInfo(); + }); + """ + +# --- Report Generator --- + +class ReportGenerator: + def __init__(self, results_dir: Path): + self.results_dir = results_dir + self.runs: List[TestRun] = [] + self.pivot_data: List[PivotRow] = [] + self.all_labels: Set[str] = set() + + def parse_results(self): + """Parse all test run results from the results directory.""" + if not self.results_dir.exists(): + return + + for run_dir in sorted(self.results_dir.glob("run-*")): + if not run_dir.is_dir(): + continue + + metadata_file = run_dir / "artifacts" / "metadata.json" + report_file = run_dir / "reports" / "report.json" + + if not metadata_file.exists() or not report_file.exists(): + continue + + try: + with open(metadata_file, 'r') as f: + metadata = json.load(f) + with open(report_file, 'r') as f: + report = json.load(f) + except Exception as e: + print(f"Error reading {run_dir}: {e}") + continue + + # Parse tests + tests = [] + spec_reports = report[0].get('SpecReports', []) if isinstance(report, list) else [] + + # Build artifact index once per run for O(1) lookup + artifact_index = self._build_artifact_index(run_dir) + + for spec in spec_reports: + if not spec.get('LeafNodeText'): + continue + + hierarchy = spec.get('ContainerHierarchyTexts', []) + leaf_text = spec['LeafNodeText'] + full_name = ' '.join(hierarchy + [leaf_text]) if hierarchy else leaf_text + + # Collect labels + labels = [] + for l_list in spec.get('ContainerHierarchyLabels', []): + if isinstance(l_list, list): labels.extend(l_list) + if spec.get('LeafNodeLabels'): labels.extend(spec.get('LeafNodeLabels')) + self.all_labels.update(labels) + + # Find artifacts using pre-built index + artifact_meta = self._find_artifacts(artifact_index, full_name) + + tests.append(TestResult( + name=full_name, + full_name=full_name, + leaf_text=leaf_text, + state=spec['State'], + runtime=spec['RunTime'] / 1e9, + failure_message=spec.get('FailureMessage', ''), + labels=labels, + container_hierarchy=hierarchy, + start_time=spec.get('StartTime', ''), + artifact_metadata=artifact_meta + )) + + # Calculate total runtime from tests if not in metadata + total_runtime = sum(t.runtime for t in tests) + + # Read test output log + test_output_log = "" + log_file = run_dir / "reports" / "test-output.log" + if log_file.exists(): + try: + with open(log_file, 'r', encoding='utf-8', errors='replace') as f: + lines = f.readlines() + if len(lines) > 10000: + test_output_log = f"Log truncated. Showing last 10,000 of {len(lines)} lines.\\n\\n" + "".join(lines[-10000:]) + else: + test_output_log = "".join(lines) + except Exception as e: + test_output_log = f"Error reading log file: {e}" + + self.runs.append(TestRun( + run_id=str(metadata.get('run_id', run_dir.name)), + start_time=metadata.get('start_time', datetime.now().isoformat()), + total_tests=metadata.get('total_tests', len(tests)), + passed_tests=metadata.get('passed_tests', len([t for t in tests if t.state == 'passed'])), + failed_tests=metadata.get('failed_tests', len([t for t in tests if t.state == 'failed'])), + environment=metadata.get('environment', {}), + total_runtime=total_runtime, + test_output_log=test_output_log, + tests=tests, + git_commit=metadata.get('git_commit', ''), + git_branch=metadata.get('git_branch', ''), + git_dirty=metadata.get('git_dirty', '') + )) + + # Sort runs by time (newest first) + self.runs.sort(key=lambda r: r.start_time, reverse=True) + self._build_pivot_data() + + def _build_artifact_index(self, run_dir: Path) -> Dict[str, Path]: + """Build name -> artifact_dir mapping for O(1) lookup.""" + index = {} + artifacts_dir = run_dir / "artifacts" + if not artifacts_dir.exists(): + return index + for artifact_dir in artifacts_dir.glob("*"): + if not artifact_dir.is_dir(): + continue + meta_file = artifact_dir / "metadata.json" + if meta_file.exists(): + try: + with open(meta_file) as f: + data = json.load(f) + if name := data.get('name'): + index[name.strip()] = artifact_dir + except: + pass + return index + + def _find_artifacts(self, artifact_index: Dict[str, Path], test_name: str) -> Optional[Dict[str, Any]]: + """Locate artifact metadata for a specific test using pre-built index.""" + artifact_dir = artifact_index.get(test_name.strip()) + if not artifact_dir: + return None + + meta_file = artifact_dir / "metadata.json" + if not meta_file.exists(): + return None + + try: + with open(meta_file) as f: + data = json.load(f) + + data['relative_path'] = str(artifact_dir.relative_to(self.results_dir)) + data['file_contents'] = {} + + # Read log files (last 500 lines) + for log in data.get('artifacts', {}).get('log_files', []): + lp = artifact_dir / log + if lp.exists(): + try: + with open(lp) as lf: + lines = lf.readlines() + content = ''.join(lines[-500:]) + data['file_contents'][log] = { + 'content': content, + 'type': 'log', + 'truncated': len(lines) > 500, + 'total_lines': len(lines) + } + except Exception as e: + data['file_contents'][log] = {'content': str(e), 'type': 'error'} + + # Read resource files (limit 50KB) + for res in data.get('artifacts', {}).get('resource_files', [])[:10]: + rp = artifact_dir / res + if rp.exists(): + try: + with open(rp) as rf: + content = rf.read() + if len(content) > 51200: content = content[:51200] + '\\n... (truncated)' + data['file_contents'][res] = {'content': content, 'type': 'resource'} + except Exception as e: + data['file_contents'][res] = {'content': str(e), 'type': 'error'} + + # Read event files + for evt in data.get('artifacts', {}).get('event_files', []): + ep = artifact_dir / evt + if ep.exists(): + try: + with open(ep) as ef: + data['file_contents'][evt] = {'content': ef.read(), 'type': 'events'} + except Exception as e: + data['file_contents'][evt] = {'content': str(e), 'type': 'error'} + + return data + except: + pass + return None + + def _build_pivot_data(self): + """Build the pivot table data structure and analyze flakiness.""" + all_names = sorted({t.full_name for run in self.runs for t in run.tests}) + + for name in all_names: + # Get container hierarchy from first occurrence of test + test_obj = None + for run in self.runs: + test_obj = next((t for t in run.tests if t.full_name == name), None) + if test_obj: + break + + row = PivotRow( + test_name=name, + full_test_name=name, + leaf_text=test_obj.leaf_text if test_obj else name.split(' ')[-1], + container_hierarchy=test_obj.container_hierarchy if test_obj else [] + ) + + results_sequence = [] + + for run in self.runs: + result = next((t for t in run.tests if t.full_name == name), None) + if result: + row.runs[run.run_id] = { + 'state': result.state, + 'runtime': result.runtime, + 'failure_message': result.failure_message, + 'labels': result.labels, + 'artifact_metadata': result.artifact_metadata + } + row.total_runs += 1 + row.total_runtime += result.runtime + row.min_runtime = min(row.min_runtime, result.runtime) + row.max_runtime = max(row.max_runtime, result.runtime) + + if result.state == 'passed': + row.pass_count += 1 + results_sequence.append('P') + elif result.state == 'failed': + row.fail_count += 1 + results_sequence.append('F') + else: + row.skip_count += 1 + results_sequence.append('S') + else: + row.runs[run.run_id] = None + + if row.total_runs > 0: + row.pass_rate = (row.pass_count / row.total_runs) * 100 + row.avg_runtime = row.total_runtime / row.total_runs + + # Flakiness Analysis + if row.total_runs >= 2 and row.pass_count > 0 and row.fail_count > 0: + row.is_flaky = True + row.flakiness_score = 100 - 2 * abs(row.pass_rate - 50) + + # Pattern detection + if len(results_sequence) >= 3: + is_alternating = True + for i in range(len(results_sequence) - 1): + if results_sequence[i] == results_sequence[i+1]: + is_alternating = False + break + if is_alternating: row.flakiness_pattern = 'alternating' + + self.pivot_data.append(row) + + # Sort by failure count + self.pivot_data.sort(key=lambda x: (x.fail_count, -x.pass_rate), reverse=True) + + def _generate_chart_data(self) -> str: + """Generate JSON data for Chart.js.""" + chronological_runs = sorted(self.runs, key=lambda r: r.start_time) + labels = [f"Run {r.run_id}" for r in chronological_runs] + + pass_rates = [] + durations = [] + runs_data = [] + + for r in chronological_runs: + rate = (r.passed_tests / r.total_tests * 100) if r.total_tests > 0 else 0 + pass_rates.append(round(rate, 1)) + durations.append(round(r.total_runtime, 1)) + + # Add run metadata for comparison + runs_data.append({ + 'run_id': r.run_id, + 'timestamp': r.start_time, + 'total': r.total_tests, + 'passed': r.passed_tests, + 'failed': r.failed_tests, + 'runtime': round(r.total_runtime, 1) + }) + + data = { + "passRate": { + "labels": labels, + "datasets": [{ + "label": "Pass Rate (%)", + "data": pass_rates, + "borderColor": "#10b981", + "backgroundColor": "rgba(16, 185, 129, 0.1)", + "fill": True + }] + }, + "duration": { + "labels": labels, + "datasets": [{ + "label": "Total Duration (s)", + "data": durations, + "borderColor": "#3b82f6", + "backgroundColor": "rgba(59, 130, 246, 0.1)", + "fill": True + }] + }, + "runs": runs_data + } + return json.dumps(data) + + def generate_html(self, output_file: Path): + """Generate the full HTML report.""" + + # Serialize pivot data for JS + pivot_json = json.dumps([ + { + 'test_name': r.test_name, + 'is_flaky': r.is_flaky, + 'pass_count': r.pass_count, + 'fail_count': r.fail_count, + 'total_runs': r.total_runs, + 'flakiness_score': r.flakiness_score, + 'runs': r.runs + } for r in self.pivot_data + ], default=str) + + html_content = f""" + + + + + E2E Test Report + + + + +
+
+
+

E2E Test Results

+

Generated on {datetime.now().strftime('%Y-%m-%d %H:%M')}

+
+
+ +
+
+ +
+ + + {f'' if len(self.runs) >= 2 else ''} +
+ + +
+
+
+
Total Runs
+
{len(self.runs)}
+
+
+
Total Tests
+
{len(self.pivot_data)}
+
+
+
Flaky Tests
+
{len([r for r in self.pivot_data if r.is_flaky])}
+ Tests that show inconsistent results across runs - sometimes passing, sometimes failing. These may indicate timing issues, race conditions, or environmental dependencies. +
+
+
Always Failing
+
{len([r for r in self.pivot_data if r.fail_count == r.total_runs and r.total_runs > 0])}
+ Tests that failed in every single run. These are consistently broken and require immediate attention. +
+
+
Avg Runtime
+
{format_duration(sum(r.total_runtime for r in self.runs) / len(self.runs)) if self.runs else 'N/A'}
+ Average total runtime across all test runs. Helps track performance trends over time. +
+
+
Pass Rate Trend
+
+ {self._get_pass_rate_trend()} +
+ Pass rate change compared to the previous run. ↑ indicates improvement, ↓ indicates more failures, → means stable. +
+ + +
+
Latest Run Summary
+
+
+
Pass Rate
+
{self._get_latest_pass_rate()}%
+
+
+
Failures
+
{self.runs[0].failed_tests if self.runs else 0}
+
+
+
Runtime
+
{format_duration(self.runs[0].total_runtime) if self.runs else 'N/A'}
+
+
+
+ + {self.runs[0].git_branch if self.runs and self.runs[0].git_branch else 'unknown'} + + {f'' if self.runs and self.runs[0].git_dirty else ''} + + {svg_icon('copy', 12)} {self.runs[0].git_commit[:7] if self.runs and self.runs[0].git_commit else 'unknown'} + +
+
+
+ +
+
+ +
+
+ +
+
+ +
+

{svg_icon('warning', 18, '#f59e0b')} Flaky Tests Detected

+
+ {''.join(f''' +
+
+ {r.flakiness_score:.0f}% Stable + Stability score based on pass/fail ratio. Lower values indicate more inconsistent behavior. + + {r.flakiness_pattern} + Pattern: {self._get_flakiness_pattern_description(r.flakiness_pattern)} + +
+
{r.test_name}
+
+ Pass: {r.pass_count} | Fail: {r.fail_count} +
+
+ ''' for r in self.pivot_data if r.is_flaky) or '

No flaky tests detected.

'} +
+
+
+ + +
+
+ + + + + +
+ +
+ + + + + + {''.join(f'' for r in self.runs)} + + + + {self._generate_table_rows()} + +
Test NameStatsRun {r.run_id}
{r.date_str}
{format_duration(r.total_runtime)}
+
+
+ + +
+
+ + + +
+ + +
+
+

Run A

+
+
+
+

Run B

+
+
+
+ + + + +
+
+

{svg_icon('error', 16, '#ef4444')} New Failures

+
+
+
+

{svg_icon('info', 16, '#10b981')} Fixed Tests

+
+
+
+

{svg_icon('warning', 16, '#f59e0b')} Regressions

+
+
+
+
+
+ + + + + + + +""" + with open(output_file, 'w') as f: + f.write(html_content) + print(f"Report generated at: {output_file}") + + def _get_latest_pass_rate(self) -> str: + if not self.runs: return "0.0" + latest = self.runs[0] + if latest.total_tests == 0: return "0.0" + return f"{(latest.passed_tests / latest.total_tests * 100):.1f}" + + def _get_pass_rate_trend(self) -> str: + """Get pass rate trend compared to previous run.""" + if len(self.runs) < 2: + return '' + + latest = self.runs[0] + previous = self.runs[1] + + if latest.total_tests == 0 or previous.total_tests == 0: + return '' + + latest_rate = (latest.passed_tests / latest.total_tests * 100) + previous_rate = (previous.passed_tests / previous.total_tests * 100) + diff = latest_rate - previous_rate + + if abs(diff) < 0.1: + return '→ Stable' + elif diff > 0: + return f'↑ +{diff:.1f}%' + else: + return f'↓ {diff:.1f}%' + + def _get_flakiness_pattern_description(self, pattern: str) -> str: + """Get description for flakiness pattern.""" + descriptions = { + 'intermittent': 'Fails randomly with no clear pattern', + 'occasional': 'Fails infrequently, mostly passes', + 'frequent': 'Fails often, passes sometimes', + 'alternating': 'Alternates between pass and fail', + 'unstable': 'Highly unpredictable behavior' + } + return descriptions.get(pattern, 'Unknown pattern') + + def _generate_hierarchical_name(self, container_hierarchy: List[str], leaf_text: str) -> str: + """Generate breadcrumb-style HTML representation of test name.""" + if not container_hierarchy: + # No hierarchy - just show the leaf text + return f'
{html.escape(leaf_text)}
' + + # Build breadcrumb structure + parts = [] + + # Add container hierarchy items + for i, container in enumerate(container_hierarchy): + level_class = f'level-{i}' if i == 0 else '' + parts.append(f'{html.escape(container)}') + parts.append('') + + # Add leaf (actual test name) + parts.append(f'{html.escape(leaf_text)}') + + return f'
{"".join(parts)}
' + + def _generate_table_rows(self) -> str: + rows = [] + for row in self.pivot_data: + cells = [] + + # Test Name with hierarchy + hierarchical_name = self._generate_hierarchical_name(row.container_hierarchy, row.leaf_text) + # Full test name for copying (breadcrumb style) + full_test_name = ' › '.join(row.container_hierarchy + [row.leaf_text]) if row.container_hierarchy else row.leaf_text + cells.append(f''' +
+ {hierarchical_name} + +
+ ''') + + # Stats Cell + rate_class = 'rate-high' if row.pass_rate >= 90 else 'rate-medium' if row.pass_rate >= 50 else 'rate-low' + cells.append(f''' + +
+ {row.pass_rate:.1f}% + Pass rate: {row.pass_count} passed out of {row.total_runs} total runs +
+
{row.pass_count}✓ {row.fail_count}✗ / {row.total_runs}
+
avg: {row.avg_runtime:.1f}s
+ + ''') + + for run in self.runs: + result = row.runs.get(run.run_id) + if result: + # Runtime color + rt = result['runtime'] + rt_class = 'runtime-fast' if rt < 10 else 'runtime-medium' if rt < 30 else 'runtime-slow' + + # Serialize result for modal + data_json = json.dumps({ + 'test_name': row.test_name, + 'state': result['state'], + 'runtime': result['runtime'], + 'run_id': run.run_id, + 'failure_message': result['failure_message'], + 'artifact_metadata': result['artifact_metadata'] + }).replace("'", "'") + + cells.append(f''' + + {result["state"]} + {rt:.1f}s + + ''') + else: + cells.append('-') + + # Add metadata for filtering + row_meta = json.dumps({ + 'test_name': row.test_name, + 'is_flaky': row.is_flaky, + 'pass_count': row.pass_count, + 'fail_count': row.fail_count, + 'runs': row.runs + }, default=str).replace('"', '"') + + rows.append(f'{"".join(cells)}') + return "\n".join(rows) + +def main(): + if len(sys.argv) > 1: + results_dir = Path(sys.argv[1]) + else: + results_dir = Path.cwd() + + output_file = results_dir / "test_results_report.html" + + print(f"Scanning {results_dir}...") + generator = ReportGenerator(results_dir) + generator.parse_results() + + if not generator.runs: + print("No runs found.") + return + + generator.generate_html(output_file) + +if __name__ == "__main__": + main() diff --git a/test/e2e/testdata/normal-mode/agent.yaml b/test/e2e/testdata/normal-mode/agent.yaml new file mode 100644 index 00000000..f267c71e --- /dev/null +++ b/test/e2e/testdata/normal-mode/agent.yaml @@ -0,0 +1,7 @@ +apiVersion: observability.kaasops.io/v1alpha1 +kind: Vector +metadata: + name: normal-agent +spec: + agent: + image: timberio/vector:0.40.0-alpine diff --git a/test/e2e/testdata/normal-mode/aggregator.yaml b/test/e2e/testdata/normal-mode/aggregator.yaml new file mode 100644 index 00000000..e12459f8 --- /dev/null +++ b/test/e2e/testdata/normal-mode/aggregator.yaml @@ -0,0 +1,11 @@ +apiVersion: observability.kaasops.io/v1alpha1 +kind: VectorAggregator +metadata: + name: normal-aggregator +spec: + image: timberio/vector:0.40.0-alpine + replicas: 1 + selector: + matchLabels: + app: test + role: aggregator diff --git a/test/e2e/testdata/normal-mode/cluster-pipeline-pod-ns1.yaml b/test/e2e/testdata/normal-mode/cluster-pipeline-pod-ns1.yaml new file mode 100644 index 00000000..73d0a0a6 --- /dev/null +++ b/test/e2e/testdata/normal-mode/cluster-pipeline-pod-ns1.yaml @@ -0,0 +1,20 @@ +apiVersion: v1 +kind: Pod +metadata: + name: cluster-monitored-pod-1 + labels: + app: cluster-test + cluster-monitor: enabled +spec: + containers: + - name: log-generator + image: busybox:1.36 + command: + - sh + - -c + - | + while true; do + echo '{"marker":"CLUSTER_MONITORED_NS1","message":"Cluster pipeline test from ns1"}' + sleep 5 + done + restartPolicy: Always diff --git a/test/e2e/testdata/normal-mode/cluster-pipeline-pod-ns2.yaml b/test/e2e/testdata/normal-mode/cluster-pipeline-pod-ns2.yaml new file mode 100644 index 00000000..ba81ae97 --- /dev/null +++ b/test/e2e/testdata/normal-mode/cluster-pipeline-pod-ns2.yaml @@ -0,0 +1,21 @@ +apiVersion: v1 +kind: Pod +metadata: + name: cluster-monitored-pod-2 + namespace: test-normal-mode-isolated + labels: + app: cluster-test + cluster-monitor: enabled +spec: + containers: + - name: log-generator + image: busybox:1.36 + command: + - sh + - -c + - | + while true; do + echo '{"marker":"CLUSTER_MONITORED_NS2","message":"Cluster pipeline test from ns2"}' + sleep 5 + done + restartPolicy: Always diff --git a/test/e2e/testdata/normal-mode/cluster-pipeline.yaml b/test/e2e/testdata/normal-mode/cluster-pipeline.yaml new file mode 100644 index 00000000..68edad2e --- /dev/null +++ b/test/e2e/testdata/normal-mode/cluster-pipeline.yaml @@ -0,0 +1,24 @@ +apiVersion: observability.kaasops.io/v1alpha1 +kind: ClusterVectorPipeline +metadata: + name: cluster-wide-pipeline +spec: + sources: + cluster_logs: + type: kubernetes_logs + extra_label_selector: "cluster-monitor=enabled" + transforms: + add_cluster_info: + type: remap + inputs: + - cluster_logs + source: | + .cluster_pipeline = "cluster-wide-pipeline" + .collected_at = now() + sinks: + console: + type: console + inputs: + - add_cluster_info + encoding: + codec: json diff --git a/test/e2e/testdata/normal-mode/namespace-isolation-ns.yaml b/test/e2e/testdata/normal-mode/namespace-isolation-ns.yaml new file mode 100644 index 00000000..03a385a5 --- /dev/null +++ b/test/e2e/testdata/normal-mode/namespace-isolation-ns.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: test-normal-mode-isolated diff --git a/test/e2e/testdata/normal-mode/namespace-isolation-pipeline.yaml b/test/e2e/testdata/normal-mode/namespace-isolation-pipeline.yaml new file mode 100644 index 00000000..4e29ab7e --- /dev/null +++ b/test/e2e/testdata/normal-mode/namespace-isolation-pipeline.yaml @@ -0,0 +1,17 @@ +apiVersion: observability.kaasops.io/v1alpha1 +kind: VectorPipeline +metadata: + name: isolated-pipeline + namespace: test-normal-mode-isolated +spec: + sources: + my_namespace_logs: + type: kubernetes_logs + # Should only see logs from test-normal-mode-isolated namespace + sinks: + console: + type: console + inputs: + - my_namespace_logs + encoding: + codec: json diff --git a/test/e2e/testdata/normal-mode/namespace-isolation-pod-isolated.yaml b/test/e2e/testdata/normal-mode/namespace-isolation-pod-isolated.yaml new file mode 100644 index 00000000..fd28d45b --- /dev/null +++ b/test/e2e/testdata/normal-mode/namespace-isolation-pod-isolated.yaml @@ -0,0 +1,20 @@ +apiVersion: v1 +kind: Pod +metadata: + name: isolated-pod + namespace: test-normal-mode-isolated + labels: + app: isolated-app +spec: + containers: + - name: log-generator + image: busybox:1.36 + command: + - sh + - -c + - | + while true; do + echo '{"marker":"ISOLATED_NAMESPACE","message":"This is from isolated namespace"}' + sleep 5 + done + restartPolicy: Always diff --git a/test/e2e/testdata/normal-mode/namespace-isolation-pod-main.yaml b/test/e2e/testdata/normal-mode/namespace-isolation-pod-main.yaml new file mode 100644 index 00000000..ba5b6288 --- /dev/null +++ b/test/e2e/testdata/normal-mode/namespace-isolation-pod-main.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Pod +metadata: + name: main-namespace-pod + labels: + app: main-app +spec: + containers: + - name: log-generator + image: busybox:1.36 + command: + - sh + - -c + - | + while true; do + echo '{"marker":"MAIN_NAMESPACE","message":"This is from main namespace"}' + sleep 5 + done + restartPolicy: Always diff --git a/test/e2e/testdata/normal-mode/pipeline-aggregator-role.yaml b/test/e2e/testdata/normal-mode/pipeline-aggregator-role.yaml new file mode 100644 index 00000000..568fc21d --- /dev/null +++ b/test/e2e/testdata/normal-mode/pipeline-aggregator-role.yaml @@ -0,0 +1,28 @@ +apiVersion: observability.kaasops.io/v1alpha1 +kind: VectorPipeline +metadata: + name: aggregator-pipeline + labels: + app: test + role: aggregator + annotations: + vector.kaasops.io/role: "Aggregator" +spec: + sources: + vector_source: + type: vector + address: "0.0.0.0:9000" + transforms: + process: + type: remap + inputs: + - vector_source + source: | + .processed = true + sinks: + console: + type: console + inputs: + - process + encoding: + codec: json diff --git a/test/e2e/testdata/normal-mode/pipeline-basic.yaml b/test/e2e/testdata/normal-mode/pipeline-basic.yaml new file mode 100644 index 00000000..9f53aefc --- /dev/null +++ b/test/e2e/testdata/normal-mode/pipeline-basic.yaml @@ -0,0 +1,16 @@ +apiVersion: observability.kaasops.io/v1alpha1 +kind: VectorPipeline +metadata: + name: basic-pipeline +spec: + sources: + kubernetes_logs: + type: kubernetes_logs + extra_label_selector: "app=test-app" + sinks: + console: + type: console + inputs: + - kubernetes_logs + encoding: + codec: json diff --git a/test/e2e/testdata/normal-mode/pipeline-complex.yaml b/test/e2e/testdata/normal-mode/pipeline-complex.yaml new file mode 100644 index 00000000..177be4c1 --- /dev/null +++ b/test/e2e/testdata/normal-mode/pipeline-complex.yaml @@ -0,0 +1,34 @@ +apiVersion: observability.kaasops.io/v1alpha1 +kind: VectorPipeline +metadata: + name: complex-pipeline +spec: + sources: + kubernetes_logs: + type: kubernetes_logs + extra_label_selector: "app=test-app" + transforms: + parse: + type: remap + inputs: + - kubernetes_logs + source: | + .parsed = parse_json!(.message) + filter: + type: filter + inputs: + - parse + condition: '.level == "info"' + sinks: + console_all: + type: console + inputs: + - parse + encoding: + codec: json + console_filtered: + type: console + inputs: + - filter + encoding: + codec: text diff --git a/test/e2e/testdata/normal-mode/pipeline-deletable.yaml b/test/e2e/testdata/normal-mode/pipeline-deletable.yaml new file mode 100644 index 00000000..c91f2cd8 --- /dev/null +++ b/test/e2e/testdata/normal-mode/pipeline-deletable.yaml @@ -0,0 +1,16 @@ +apiVersion: observability.kaasops.io/v1alpha1 +kind: VectorPipeline +metadata: + name: deletable-pipeline +spec: + sources: + kubernetes_logs: + type: kubernetes_logs + extra_label_selector: "app=test-app" + sinks: + console: + type: console + inputs: + - kubernetes_logs + encoding: + codec: json diff --git a/test/e2e/testdata/normal-mode/pipeline-kubernetes-logs.yaml b/test/e2e/testdata/normal-mode/pipeline-kubernetes-logs.yaml new file mode 100644 index 00000000..3cb0f0ac --- /dev/null +++ b/test/e2e/testdata/normal-mode/pipeline-kubernetes-logs.yaml @@ -0,0 +1,24 @@ +apiVersion: observability.kaasops.io/v1alpha1 +kind: VectorPipeline +metadata: + name: k8s-logs-pipeline +spec: + sources: + kubernetes_logs: + type: kubernetes_logs + extra_label_selector: "app=test-app" + transforms: + filter: + type: filter + inputs: + - kubernetes_logs + condition: + type: vrl + source: '.level != "debug"' + sinks: + console: + type: console + inputs: + - filter + encoding: + codec: json diff --git a/test/e2e/testdata/normal-mode/pipeline-template.yaml b/test/e2e/testdata/normal-mode/pipeline-template.yaml new file mode 100644 index 00000000..1a2ef5e6 --- /dev/null +++ b/test/e2e/testdata/normal-mode/pipeline-template.yaml @@ -0,0 +1,16 @@ +apiVersion: observability.kaasops.io/v1alpha1 +kind: VectorPipeline +metadata: + name: {{INDEX}} +spec: + sources: + kubernetes_logs: + type: kubernetes_logs + extra_label_selector: "app=test-app" + sinks: + console: + type: console + inputs: + - kubernetes_logs + encoding: + codec: json diff --git a/test/e2e/testdata/normal-mode/test-app-pod.yaml b/test/e2e/testdata/normal-mode/test-app-pod.yaml new file mode 100644 index 00000000..6eb8d1e9 --- /dev/null +++ b/test/e2e/testdata/normal-mode/test-app-pod.yaml @@ -0,0 +1,20 @@ +apiVersion: v1 +kind: Pod +metadata: + name: test-app + labels: + app: test-app +spec: + containers: + - name: log-generator + image: busybox:1.36 + command: + - sh + - -c + - | + while true; do + echo '{"level":"info","message":"Test log from test-app","timestamp":"'$(date -Iseconds)'"}' + echo '{"level":"debug","message":"Debug log should be filtered","timestamp":"'$(date -Iseconds)'"}' + sleep 5 + done + restartPolicy: Always diff --git a/test/e2e/testdata_helper.go b/test/e2e/testdata_helper.go new file mode 100644 index 00000000..16b70346 --- /dev/null +++ b/test/e2e/testdata_helper.go @@ -0,0 +1,17 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package e2e From c7e505ced843d5dae5cebf530b3b7315bf5a4b3b Mon Sep 17 00:00:00 2001 From: aa1ex <115736127+aa1ex@users.noreply.github.com> Date: Mon, 24 Nov 2025 10:49:15 +0200 Subject: [PATCH 306/316] fix(configcheck): prevent memory leak by replacing time.After with time.NewTimer (#207) * test(configcheck): add test demonstrating time.After memory leak Add test that demonstrates the memory leak caused by using time.After() in a select loop. Each iteration allocates ~280 bytes that are not freed until the timer fires. Benchmark results: - time.After(): 283 B/op, 3 allocs/op - time.NewTimer with Stop/Reset: 0 B/op, 0 allocs/op This test will fail after the fix is applied (allocations will be 0). * fix(configcheck): prevent memory leak by replacing time.After with time.NewTimer time.After() in select loop allocates ~280 bytes per iteration that are not freed until the timer fires. Replace with time.NewTimer + Reset pattern. --- internal/config/configcheck/configcheck.go | 18 ++- .../config/configcheck/timer_leak_test.go | 122 ++++++++++++++++++ 2 files changed, 137 insertions(+), 3 deletions(-) create mode 100644 internal/config/configcheck/timer_leak_test.go diff --git a/internal/config/configcheck/configcheck.go b/internal/config/configcheck/configcheck.go index ffc68bf4..7c06bfa9 100644 --- a/internal/config/configcheck/configcheck.go +++ b/internal/config/configcheck/configcheck.go @@ -225,6 +225,12 @@ func (cc *ConfigCheck) getCheckResult(ctx context.Context, pod *corev1.Pod) (rea defer watcher.Stop() + // Use time.NewTimer instead of time.After to prevent memory leak. + // time.After() creates a new timer on each select iteration that won't be GC'd + // until it fires, causing ~280 bytes leak per iteration. + timer := time.NewTimer(cc.ConfigCheckTimeout) + defer timer.Stop() + for { select { case e := <-watcher.ResultChan(): @@ -253,11 +259,17 @@ func (cc *ConfigCheck) getCheckResult(ctx context.Context, pod *corev1.Pod) (rea return reason, ValidationError } } + // Reset timer after processing event to restart timeout window + if !timer.Stop() { + select { + case <-timer.C: + default: + } + } + timer.Reset(cc.ConfigCheckTimeout) case <-ctx.Done(): - watcher.Stop() return "", nil - case <-time.After(cc.ConfigCheckTimeout): - watcher.Stop() + case <-timer.C: return "", ConfigcheckTimeoutError } } diff --git a/internal/config/configcheck/timer_leak_test.go b/internal/config/configcheck/timer_leak_test.go new file mode 100644 index 00000000..24f8c585 --- /dev/null +++ b/internal/config/configcheck/timer_leak_test.go @@ -0,0 +1,122 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package configcheck + +import ( + "testing" + "time" +) + +// TestTimeAfterLeakAllocations demonstrates memory leak when using time.After() in a loop. +// Each call to time.After() allocates ~280 bytes that won't be freed until the timer fires. +// +// This test shows the problem with the current implementation in getCheckResult(): +// - time.After() in select loop: 283 B/op, 3 allocs/op +// - time.NewTimer with Stop/Reset: 0 B/op, 0 allocs/op +// +// In a long-running operator with frequent watch events, this causes continuous memory growth. +func TestTimeAfterLeakAllocations(t *testing.T) { + eventChan := make(chan struct{}, 1) + timeout := 10 * time.Second + + // Measure allocations for time.After() pattern (problematic) + leakyAllocs := testing.AllocsPerRun(100, func() { + eventChan <- struct{}{} + select { + case <-eventChan: + case <-time.After(timeout): + } + }) + + // Measure allocations for time.NewTimer pattern (correct) + timer := time.NewTimer(timeout) + defer timer.Stop() + + fixedAllocs := testing.AllocsPerRun(100, func() { + eventChan <- struct{}{} + select { + case <-eventChan: + if !timer.Stop() { + select { + case <-timer.C: + default: + } + } + timer.Reset(timeout) + case <-timer.C: + } + }) + + t.Logf("time.After() allocations per op: %.1f", leakyAllocs) + t.Logf("time.NewTimer allocations per op: %.1f", fixedAllocs) + + // time.After() should allocate ~3 objects per call + if leakyAllocs < 2 { + t.Errorf("Expected time.After() to allocate >= 2 objects per call, got %.1f", leakyAllocs) + } + + // time.NewTimer with Stop/Reset should allocate 0 objects + if fixedAllocs > 0.5 { + t.Errorf("Expected time.NewTimer pattern to allocate ~0 objects per call, got %.1f", fixedAllocs) + } + + // The leak: each iteration wastes ~280 bytes until timer fires + // With 1000 events and 60s timeout, that's 280KB of leaked memory per minute + t.Logf("LEAK IMPACT: %.0f allocations leaked per iteration", leakyAllocs-fixedAllocs) + t.Logf("With 1000 events and 60s ConfigCheckTimeout: ~%.0f KB leaked until timers fire", + leakyAllocs*280*1000/1024) +} + +// BenchmarkTimeAfterLeak benchmarks memory allocation with leaky time.After() +func BenchmarkTimeAfterLeak(b *testing.B) { + eventChan := make(chan struct{}, 1) + timeout := 1 * time.Hour // Very long timeout + + b.ResetTimer() + for i := 0; i < b.N; i++ { + eventChan <- struct{}{} + select { + case <-eventChan: + case <-time.After(timeout): + } + } +} + +// BenchmarkTimeAfterFixed benchmarks memory allocation with fixed timer pattern +func BenchmarkTimeAfterFixed(b *testing.B) { + eventChan := make(chan struct{}, 1) + timeout := 1 * time.Hour + + timer := time.NewTimer(timeout) + defer timer.Stop() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + eventChan <- struct{}{} + select { + case <-eventChan: + if !timer.Stop() { + select { + case <-timer.C: + default: + } + } + timer.Reset(timeout) + case <-timer.C: + } + } +} From 1bcdb33f1d9ce2a0cac31e5f5bf0453019ab28c6 Mon Sep 17 00:00:00 2001 From: aa1ex <115736127+aa1ex@users.noreply.github.com> Date: Mon, 24 Nov 2025 13:00:06 +0200 Subject: [PATCH 307/316] ci: add unit tests workflow and fix broken tests (#209) Add GitHub Actions workflow to run unit tests on every PR and push to main: - Runs `make test` which includes manifests generation, formatting, vetting - Uploads coverage report as artifact Fix unit tests to run without real cluster: - Set UseExistingCluster: false in envtest (use embedded API server) - Fix vector_controller_test.go to properly configure Vector spec and run double reconcile (finalizer + actual work) --- .github/workflows/unit-tests.yaml | 39 +++++++++++++++++++ api/v1alpha1/zz_generated.deepcopy.go | 2 +- internal/controller/suite_test.go | 2 +- internal/controller/vector_controller_test.go | 17 +++++++- 4 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/unit-tests.yaml diff --git a/.github/workflows/unit-tests.yaml b/.github/workflows/unit-tests.yaml new file mode 100644 index 00000000..c9748370 --- /dev/null +++ b/.github/workflows/unit-tests.yaml @@ -0,0 +1,39 @@ +name: Unit Tests + +on: + push: + branches: + - main + - master + pull_request: + branches: + - main + - master + workflow_dispatch: + +jobs: + unit-tests: + name: Run Unit Tests + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' + cache: true + + - name: Run unit tests + run: make test + + - name: Upload coverage report + if: always() + uses: actions/upload-artifact@v4 + with: + name: coverage-report-${{ github.run_number }} + path: cover.out + retention-days: 7 diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 5b439271..88f3346a 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -21,7 +21,7 @@ limitations under the License. package v1alpha1 import ( - v1 "k8s.io/api/core/v1" + "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" ) diff --git a/internal/controller/suite_test.go b/internal/controller/suite_test.go index b82c3251..f6d596a6 100644 --- a/internal/controller/suite_test.go +++ b/internal/controller/suite_test.go @@ -66,7 +66,7 @@ var _ = BeforeSuite(func() { By("bootstrapping test environment") testEnv = &envtest.Environment{ - UseExistingCluster: ptr.To(true), + UseExistingCluster: ptr.To(false), CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, ErrorIfCRDPathMissing: true, diff --git a/internal/controller/vector_controller_test.go b/internal/controller/vector_controller_test.go index 24e1d23e..bdf6a7b5 100644 --- a/internal/controller/vector_controller_test.go +++ b/internal/controller/vector_controller_test.go @@ -52,7 +52,15 @@ var _ = Describe("Vector Controller", func() { Name: resourceName, Namespace: "default", }, - // TODO(user): Specify other spec details if needed. + Spec: v1alpha1.VectorSpec{ + Agent: &v1alpha1.VectorAgent{ + VectorCommon: v1alpha1.VectorCommon{ + ConfigCheck: v1alpha1.ConfigCheck{ + Disabled: true, + }, + }, + }, + }, } Expect(k8sClient.Create(ctx, resource)).To(Succeed()) } @@ -78,10 +86,17 @@ var _ = Describe("Vector Controller", func() { EventChan: make(chan event.GenericEvent, 1), } + // First reconcile adds finalizer _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ NamespacedName: typeNamespacedName, }) Expect(err).NotTo(HaveOccurred()) + + // Second reconcile performs actual reconciliation + _, err = controllerReconciler.Reconcile(ctx, reconcile.Request{ + NamespacedName: typeNamespacedName, + }) + Expect(err).NotTo(HaveOccurred()) // TODO(user): Add more specific assertions depending on your controller's reconciliation logic. // Example: If you expect a certain status condition after reconciliation, verify it here. }) From e381912a38732bf9e12b50beab11bb1c0202cd73 Mon Sep 17 00:00:00 2001 From: aa1ex <115736127+aa1ex@users.noreply.github.com> Date: Mon, 24 Nov 2025 14:57:06 +0200 Subject: [PATCH 308/316] fix: buffer event channels to prevent blocking under load (#210) Buffer output channels (400) in reconcileWithDelay to prevent blocking when VectorReconciler is slow. Without buffering, reconcileWithDelay blocks on send, stops reading from input, and PipelineReconciler eventually blocks - causing deadlock. --- cmd/manager/main.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/manager/main.go b/cmd/manager/main.go index 61be4773..e435e40c 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -197,7 +197,7 @@ func main() { os.Exit(1) } - vectorAgentEventCh := make(chan event.GenericEvent) + vectorAgentEventCh := make(chan event.GenericEvent, 400) defer close(vectorAgentEventCh) if err = (&controller.VectorReconciler{ @@ -234,7 +234,7 @@ func main() { os.Exit(1) } - vectorAggregatorsEventCh := make(chan event.GenericEvent) + vectorAggregatorsEventCh := make(chan event.GenericEvent, 400) defer close(vectorAggregatorsEventCh) if err = (&controller.VectorAggregatorReconciler{ @@ -248,7 +248,7 @@ func main() { os.Exit(1) } - clusterVectorAggregatorsEventCh := make(chan event.GenericEvent) + clusterVectorAggregatorsEventCh := make(chan event.GenericEvent, 400) defer close(clusterVectorAggregatorsEventCh) if err = (&controller.ClusterVectorAggregatorReconciler{ From af14c670ee498b6accc437dc81c72ae484298386 Mon Sep 17 00:00:00 2001 From: Andrei Makarov <49369886+P0lskay@users.noreply.github.com> Date: Mon, 24 Nov 2025 21:24:59 +0300 Subject: [PATCH 309/316] Fixed "configcheck validates ClusterVectorPipeline (CVP) against all ClusterVectorAggregator instances instead of only matching ones" (#208) --- internal/controller/pipeline_controller.go | 39 +++++-- internal/pipeline/pipeline.go | 17 +-- internal/pipeline/pipeline_test.go | 59 ----------- internal/utils/k8s/label.go | 13 +++ internal/utils/k8s/label_test.go | 114 +++++++++++++++++++++ 5 files changed, 161 insertions(+), 81 deletions(-) delete mode 100644 internal/pipeline/pipeline_test.go create mode 100644 internal/utils/k8s/label_test.go diff --git a/internal/controller/pipeline_controller.go b/internal/controller/pipeline_controller.go index a1d3fa3d..ebdf5a51 100644 --- a/internal/controller/pipeline_controller.go +++ b/internal/controller/pipeline_controller.go @@ -24,24 +24,23 @@ import ( "time" "golang.org/x/sync/errgroup" - "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/predicate" - - "github.com/kaasops/vector-operator/internal/config/configcheck" - "github.com/kaasops/vector-operator/internal/vector/aggregator" - "github.com/kaasops/vector-operator/internal/vector/vectoragent" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/event" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/predicate" "github.com/kaasops/vector-operator/api/v1alpha1" "github.com/kaasops/vector-operator/internal/config" + "github.com/kaasops/vector-operator/internal/config/configcheck" "github.com/kaasops/vector-operator/internal/pipeline" + "github.com/kaasops/vector-operator/internal/utils/k8s" + "github.com/kaasops/vector-operator/internal/vector/aggregator" + "github.com/kaasops/vector-operator/internal/vector/vectoragent" ) type PipelineReconciler struct { @@ -139,12 +138,22 @@ func (r *PipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c return ctrl.Result{}, nil } pipelineCR.SetRole(pipelineVectorRole) - + var pipelineLabels map[string]string + if pipelineCR.GetLabels() != nil { + pipelineLabels = pipelineCR.GetLabels() + } eg := errgroup.Group{} if *pipelineVectorRole == v1alpha1.VectorPipelineRoleAgent { for _, vector := range vectorAgents { + var selectorLabels map[string]string + if vector.Spec.Selector != nil { + selectorLabels = vector.Spec.Selector.MatchLabels + } + if !k8s.MatchLabels(selectorLabels, pipelineLabels) { + continue + } eg.Go(func() error { vaCtrl := vectoragent.NewController(vector, r.Client, r.Clientset) cfg, byteConfig, err := config.BuildAgentConfig(config.VectorConfigParams{ @@ -184,6 +193,13 @@ func (r *PipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c if pipelineCR.GetNamespace() != "" { for _, vector := range vectorAggregators { + var selectorLabels map[string]string + if vector.Spec.Selector != nil { + selectorLabels = vector.Spec.Selector.MatchLabels + } + if !k8s.MatchLabels(selectorLabels, pipelineLabels) { + continue + } eg.Go(func() error { vaCtrl := aggregator.NewController(vector, r.Client, r.Clientset) cfg, err := config.BuildAggregatorConfig(config.VectorConfigParams{ @@ -230,6 +246,13 @@ func (r *PipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c } else { for _, vector := range clusterVectorAggregators { + var selectorLabels map[string]string + if vector.Spec.Selector != nil { + selectorLabels = vector.Spec.Selector.MatchLabels + } + if !k8s.MatchLabels(selectorLabels, pipelineLabels) { + continue + } eg.Go(func() error { vaCtrl := aggregator.NewController(vector, r.Client, r.Clientset) cfg, err := config.BuildAggregatorConfig(config.VectorConfigParams{ diff --git a/internal/pipeline/pipeline.go b/internal/pipeline/pipeline.go index 3a42ab6b..1fd33c0d 100644 --- a/internal/pipeline/pipeline.go +++ b/internal/pipeline/pipeline.go @@ -25,6 +25,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "github.com/kaasops/vector-operator/api/v1alpha1" + "github.com/kaasops/vector-operator/internal/utils/k8s" ) type Pipeline interface { @@ -82,7 +83,7 @@ func GetValidPipelines(ctx context.Context, client client.Client, filter FilterP vp.IsValid() && vp.GetRole() == filter.Role && (filter.Scope == AllPipelines || vp.Namespace == filter.Namespace) && - MatchLabels(matchLabels, vp.Labels) { + k8s.MatchLabels(matchLabels, vp.Labels) { validPipelines = append(validPipelines, vp.DeepCopy()) } } @@ -99,7 +100,7 @@ func GetValidPipelines(ctx context.Context, client client.Client, filter FilterP if !cvp.IsDeleted() && cvp.IsValid() && cvp.GetRole() == filter.Role && - MatchLabels(matchLabels, cvp.Labels) { + k8s.MatchLabels(matchLabels, cvp.Labels) { validPipelines = append(validPipelines, cvp.DeepCopy()) } } @@ -148,15 +149,3 @@ func GetClusterVectorPipelines(ctx context.Context, client client.Client) ([]v1a } return cvps.Items, nil } - -func MatchLabels(selector map[string]string, labels map[string]string) bool { - if selector == nil { - return true - } - for k, v := range selector { - if labels[k] != v { - return false - } - } - return true -} diff --git a/internal/pipeline/pipeline_test.go b/internal/pipeline/pipeline_test.go deleted file mode 100644 index d04adde6..00000000 --- a/internal/pipeline/pipeline_test.go +++ /dev/null @@ -1,59 +0,0 @@ -package pipeline - -import ( - "testing" -) - -func TestMatchLabels(t *testing.T) { - tests := []struct { - name string - selector map[string]string - labels map[string]string - want bool - }{ - { - name: "NoSelector", - selector: nil, - labels: map[string]string{"label1": "value1", "label2": "value2"}, - want: true, - }, - { - name: "MatchingLabels", - selector: map[string]string{"label1": "value1", "label2": "value2"}, - labels: map[string]string{"label1": "value1", "label2": "value2"}, - want: true, - }, - { - name: "MismatchedLabelValues", - selector: map[string]string{"label1": "value1", "label2": "value2"}, - labels: map[string]string{"label1": "value1", "label2": "mismatch"}, - want: false, - }, - { - name: "ExtraLabelsInMap", - selector: map[string]string{"label1": "value1"}, - labels: map[string]string{"label1": "value1", "label2": "value2"}, - want: true, - }, - { - name: "SelectorWithNoMatches", - selector: map[string]string{"label1": "value1", "label2": "value2"}, - labels: map[string]string{"label3": "value3"}, - want: false, - }, - { - name: "SelectorWithNoMatches2", - selector: map[string]string{"label1": "value1", "label2": "value2"}, - labels: map[string]string{"label1": "label1"}, - want: false, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - if got := MatchLabels(test.selector, test.labels); got != test.want { - t.Errorf("MatchLabels() = %v, want %v", got, test.want) - } - }) - } -} diff --git a/internal/utils/k8s/label.go b/internal/utils/k8s/label.go index 9f75c8e2..de1526f5 100644 --- a/internal/utils/k8s/label.go +++ b/internal/utils/k8s/label.go @@ -57,3 +57,16 @@ func MergeLabels(dst, src map[string]string) map[string]string { } return dst } + +// MatchLabels matches a set of Kubernetes selectors and a set of Kubernetes labels +func MatchLabels(selector map[string]string, labels map[string]string) bool { + if selector == nil { + return true + } + for k, v := range selector { + if labels[k] != v { + return false + } + } + return true +} diff --git a/internal/utils/k8s/label_test.go b/internal/utils/k8s/label_test.go new file mode 100644 index 00000000..5186d396 --- /dev/null +++ b/internal/utils/k8s/label_test.go @@ -0,0 +1,114 @@ +package k8s + +import ( + "reflect" + "testing" +) + +func TestMatchLabels(t *testing.T) { + tests := []struct { + name string + selector map[string]string + labels map[string]string + want bool + }{ + { + name: "NoSelector", + selector: nil, + labels: map[string]string{"label1": "value1", "label2": "value2"}, + want: true, + }, + { + name: "MatchingLabels", + selector: map[string]string{"label1": "value1", "label2": "value2"}, + labels: map[string]string{"label1": "value1", "label2": "value2"}, + want: true, + }, + { + name: "MismatchedLabelValues", + selector: map[string]string{"label1": "value1", "label2": "value2"}, + labels: map[string]string{"label1": "value1", "label2": "mismatch"}, + want: false, + }, + { + name: "ExtraLabelsInMap", + selector: map[string]string{"label1": "value1"}, + labels: map[string]string{"label1": "value1", "label2": "value2"}, + want: true, + }, + { + name: "SelectorWithNoMatches", + selector: map[string]string{"label1": "value1", "label2": "value2"}, + labels: map[string]string{"label3": "value3"}, + want: false, + }, + { + name: "SelectorWithNoMatches2", + selector: map[string]string{"label1": "value1", "label2": "value2"}, + labels: map[string]string{"label1": "label1"}, + want: false, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + if got := MatchLabels(test.selector, test.labels); got != test.want { + t.Errorf("MatchLabels() = %v, want %v", got, test.want) + } + }) + } +} + +func TestMergeLabels(t *testing.T) { + tests := []struct { + name string + sourceLabels map[string]string + distLabels map[string]string + want map[string]string + }{ + { + name: "EmptySource", + sourceLabels: nil, + distLabels: map[string]string{"label1": "value1", "label2": "value2"}, + want: map[string]string{"label1": "value1", "label2": "value2"}, + }, + { + name: "EmptyDist", + sourceLabels: map[string]string{"label1": "value1", "label2": "value2"}, + distLabels: nil, + want: map[string]string{"label1": "value1", "label2": "value2"}, + }, + { + name: "DifferentLabelValues", + sourceLabels: map[string]string{"label1": "value1", "label2": "value2"}, + distLabels: map[string]string{"label1": "value1", "label2": "mismatch"}, + want: map[string]string{"label1": "value1", "label2": "mismatch"}, + }, + { + name: "SameLabelValues", + sourceLabels: map[string]string{"label1": "value1"}, + distLabels: map[string]string{"label1": "value1", "label2": "value2"}, + want: map[string]string{"label1": "value1", "label2": "value2"}, + }, + { + name: "NewLabelValues", + sourceLabels: map[string]string{"label1": "value1", "label2": "value2"}, + distLabels: map[string]string{"label3": "value3"}, + want: map[string]string{"label1": "value1", "label2": "value2", "label3": "value3"}, + }, + { + name: "DifferentLabelValues2", + sourceLabels: map[string]string{"label1": "value1", "label2": "value2"}, + distLabels: map[string]string{"label1": "label1"}, + want: map[string]string{"label1": "label1", "label2": "value2"}, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + if got := MergeLabels(test.distLabels, test.sourceLabels); !reflect.DeepEqual(got, test.want) { + t.Errorf("MatchLabels() = %v, want %v", got, test.want) + } + }) + } +} From 812ed354e565a73f6c944ea6452bcea795665ad3 Mon Sep 17 00:00:00 2001 From: aa1ex <115736127+aa1ex@users.noreply.github.com> Date: Tue, 25 Nov 2025 15:15:23 +0200 Subject: [PATCH 310/316] feat: add scrapeInterval and scrapeTimeout configuration for PodMonitor (#211) Add support for configuring Prometheus scrape parameters in Vector Agent and Aggregator PodMonitors with validation and comprehensive test coverage. --- api/v1alpha1/vector_common_types.go | 10 + ...y.kaasops.io_clustervectoraggregators.yaml | 12 + ...vability.kaasops.io_vectoraggregators.yaml | 12 + .../observability.kaasops.io_vectors.yaml | 12 + docs/specification.md | 14 +- internal/vector/aggregator/podmonitor.go | 19 +- internal/vector/aggregator/podmonitor_test.go | 227 +++++++++ .../vectoragent/vectoragent_podmonitor.go | 19 +- .../vectoragent_podmonitor_test.go | 236 +++++++++ test/e2e/framework/framework.go | 29 +- test/e2e/framework/kubectl/client.go | 22 + test/e2e/podmonitor_e2e_test.go | 460 ++++++++++++++++++ .../testdata/podmonitor/agent-no-metrics.yaml | 8 + .../podmonitor/agent-with-defaults.yaml | 8 + .../podmonitor/agent-with-scrape-config.yaml | 10 + .../agent-with-updated-interval.yaml | 10 + .../podmonitor/aggregator-with-defaults.yaml | 11 + .../aggregator-with-scrape-config.yaml | 13 + .../cluster-aggregator-with-defaults.yaml | 12 + ...cluster-aggregator-with-scrape-config.yaml | 14 + .../podmonitor/pipeline-aggregator-role.yaml | 16 + .../pipeline-with-custom-exporter.yaml | 27 + .../podmonitor/pipeline-without-exporter.yaml | 16 + test/e2e/testdata/podmonitor/test-pod.yaml | 17 + 24 files changed, 1217 insertions(+), 17 deletions(-) create mode 100644 internal/vector/aggregator/podmonitor_test.go create mode 100644 internal/vector/vectoragent/vectoragent_podmonitor_test.go create mode 100644 test/e2e/podmonitor_e2e_test.go create mode 100644 test/e2e/testdata/podmonitor/agent-no-metrics.yaml create mode 100644 test/e2e/testdata/podmonitor/agent-with-defaults.yaml create mode 100644 test/e2e/testdata/podmonitor/agent-with-scrape-config.yaml create mode 100644 test/e2e/testdata/podmonitor/agent-with-updated-interval.yaml create mode 100644 test/e2e/testdata/podmonitor/aggregator-with-defaults.yaml create mode 100644 test/e2e/testdata/podmonitor/aggregator-with-scrape-config.yaml create mode 100644 test/e2e/testdata/podmonitor/cluster-aggregator-with-defaults.yaml create mode 100644 test/e2e/testdata/podmonitor/cluster-aggregator-with-scrape-config.yaml create mode 100644 test/e2e/testdata/podmonitor/pipeline-aggregator-role.yaml create mode 100644 test/e2e/testdata/podmonitor/pipeline-with-custom-exporter.yaml create mode 100644 test/e2e/testdata/podmonitor/pipeline-without-exporter.yaml create mode 100644 test/e2e/testdata/podmonitor/test-pod.yaml diff --git a/api/v1alpha1/vector_common_types.go b/api/v1alpha1/vector_common_types.go index c1cc3c1f..712b021c 100644 --- a/api/v1alpha1/vector_common_types.go +++ b/api/v1alpha1/vector_common_types.go @@ -84,6 +84,16 @@ type VectorCommon struct { // Enable internal metrics exporter // +optional InternalMetrics bool `json:"internalMetrics,omitempty"` + // ScrapeInterval defines the interval at which Prometheus should scrape metrics. + // Example values: "30s", "1m", "5m". If not specified, Prometheus default is used. + // +optional + // +kubebuilder:validation:Pattern=`^(0|([0-9]+(\.[0-9]+)?(ms|s|m|h))+)$` + ScrapeInterval string `json:"scrapeInterval,omitempty"` + // ScrapeTimeout defines the timeout for scraping metrics. + // Example values: "10s", "30s". Must be less than ScrapeInterval. If not specified, Prometheus default is used. + // +optional + // +kubebuilder:validation:Pattern=`^(0|([0-9]+(\.[0-9]+)?(ms|s|m|h))+)$` + ScrapeTimeout string `json:"scrapeTimeout,omitempty"` // List of volumes that can be mounted by containers belonging to the pod. // +optional Volumes []v1.Volume `json:"volumes,omitempty"` diff --git a/config/crd/bases/observability.kaasops.io_clustervectoraggregators.yaml b/config/crd/bases/observability.kaasops.io_clustervectoraggregators.yaml index d56d8627..ab6fa3d0 100644 --- a/config/crd/bases/observability.kaasops.io_clustervectoraggregators.yaml +++ b/config/crd/bases/observability.kaasops.io_clustervectoraggregators.yaml @@ -3147,6 +3147,18 @@ spec: schedulerName: description: SchedulerName - defines kubernetes scheduler name type: string + scrapeInterval: + description: |- + ScrapeInterval defines the interval at which Prometheus should scrape metrics. + Example values: "30s", "1m", "5m". If not specified, Prometheus default is used. + pattern: ^(0|([0-9]+(\.[0-9]+)?(ms|s|m|h))+)$ + type: string + scrapeTimeout: + description: |- + ScrapeTimeout defines the timeout for scraping metrics. + Example values: "10s", "30s". Must be less than ScrapeInterval. If not specified, Prometheus default is used. + pattern: ^(0|([0-9]+(\.[0-9]+)?(ms|s|m|h))+)$ + type: string selector: description: |- Selector defines a filter for the Vector Pipeline and Cluster Vector Pipeline by labels. diff --git a/config/crd/bases/observability.kaasops.io_vectoraggregators.yaml b/config/crd/bases/observability.kaasops.io_vectoraggregators.yaml index 64976644..d795466e 100644 --- a/config/crd/bases/observability.kaasops.io_vectoraggregators.yaml +++ b/config/crd/bases/observability.kaasops.io_vectoraggregators.yaml @@ -3141,6 +3141,18 @@ spec: schedulerName: description: SchedulerName - defines kubernetes scheduler name type: string + scrapeInterval: + description: |- + ScrapeInterval defines the interval at which Prometheus should scrape metrics. + Example values: "30s", "1m", "5m". If not specified, Prometheus default is used. + pattern: ^(0|([0-9]+(\.[0-9]+)?(ms|s|m|h))+)$ + type: string + scrapeTimeout: + description: |- + ScrapeTimeout defines the timeout for scraping metrics. + Example values: "10s", "30s". Must be less than ScrapeInterval. If not specified, Prometheus default is used. + pattern: ^(0|([0-9]+(\.[0-9]+)?(ms|s|m|h))+)$ + type: string selector: description: |- Selector defines a filter for the Vector Pipeline and Cluster Vector Pipeline by labels. diff --git a/config/crd/bases/observability.kaasops.io_vectors.yaml b/config/crd/bases/observability.kaasops.io_vectors.yaml index 53577da1..ddb3cce7 100644 --- a/config/crd/bases/observability.kaasops.io_vectors.yaml +++ b/config/crd/bases/observability.kaasops.io_vectors.yaml @@ -3149,6 +3149,18 @@ spec: schedulerName: description: SchedulerName - defines kubernetes scheduler name type: string + scrapeInterval: + description: |- + ScrapeInterval defines the interval at which Prometheus should scrape metrics. + Example values: "30s", "1m", "5m". If not specified, Prometheus default is used. + pattern: ^(0|([0-9]+(\.[0-9]+)?(ms|s|m|h))+)$ + type: string + scrapeTimeout: + description: |- + ScrapeTimeout defines the timeout for scraping metrics. + Example values: "10s", "30s". Must be less than ScrapeInterval. If not specified, Prometheus default is used. + pattern: ^(0|([0-9]+(\.[0-9]+)?(ms|s|m|h))+)$ + type: string tolerations: description: Tolerations If specified, the pod's tolerations. items: diff --git a/docs/specification.md b/docs/specification.md index 5eb3000c..96512ca3 100644 --- a/docs/specification.md +++ b/docs/specification.md @@ -10,7 +10,7 @@ # Vector Spec - + @@ -26,6 +26,18 @@ + + + + + + + + + + + + diff --git a/internal/vector/aggregator/podmonitor.go b/internal/vector/aggregator/podmonitor.go index cbf69552..48b2ca8d 100644 --- a/internal/vector/aggregator/podmonitor.go +++ b/internal/vector/aggregator/podmonitor.go @@ -22,15 +22,22 @@ func (ctrl *Controller) createVectorAggregatorPodMonitor() *monitorv1.PodMonitor matchLabels := ctrl.matchLabelsForVectorAggregator() annotations := ctrl.annotationsForVectorAggregator() + endpoint := monitorv1.PodMetricsEndpoint{ + Path: "/metrics", + Port: "prom-exporter", + } + + if ctrl.Spec.ScrapeInterval != "" { + endpoint.Interval = monitorv1.Duration(ctrl.Spec.ScrapeInterval) + } + if ctrl.Spec.ScrapeTimeout != "" { + endpoint.ScrapeTimeout = monitorv1.Duration(ctrl.Spec.ScrapeTimeout) + } + podmonitor := &monitorv1.PodMonitor{ ObjectMeta: ctrl.objectMetaVectorAggregator(labels, annotations, ctrl.Namespace), Spec: monitorv1.PodMonitorSpec{ - PodMetricsEndpoints: []monitorv1.PodMetricsEndpoint{ - { - Path: "/metrics", - Port: "prom-exporter", - }, - }, + PodMetricsEndpoints: []monitorv1.PodMetricsEndpoint{endpoint}, Selector: metav1.LabelSelector{ MatchLabels: matchLabels, }, diff --git a/internal/vector/aggregator/podmonitor_test.go b/internal/vector/aggregator/podmonitor_test.go new file mode 100644 index 00000000..b6f44fb2 --- /dev/null +++ b/internal/vector/aggregator/podmonitor_test.go @@ -0,0 +1,227 @@ +package aggregator + +import ( + "testing" + + . "github.com/onsi/gomega" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" +) + +// Helper function to create a Controller for testing +func createTestController(name, namespace string, spec *vectorv1alpha1.VectorAggregatorCommon, isCluster bool) *Controller { + agg := &vectorv1alpha1.VectorAggregator{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + } + + return &Controller{ + Name: name, + Namespace: namespace, + VectorAggregator: agg, + APIVersion: "observability.kaasops.io/v1alpha1", + Kind: "VectorAggregator", + Spec: spec, + isClusterAggregator: isCluster, + } +} + +func TestCreateVectorAggregatorPodMonitor_WithCustomSettings(t *testing.T) { + g := NewWithT(t) + + ctrl := createTestController("test-aggregator", "default", + &vectorv1alpha1.VectorAggregatorCommon{ + VectorCommon: vectorv1alpha1.VectorCommon{ + ScrapeInterval: "60s", + ScrapeTimeout: "20s", + }, + }, false) + + pm := ctrl.createVectorAggregatorPodMonitor() + + // Verify PodMonitor structure + g.Expect(pm).NotTo(BeNil(), "PodMonitor should not be nil") + g.Expect(pm.Spec.PodMetricsEndpoints).To(HaveLen(1), "Should have exactly one endpoint") + + // Verify scrape settings + endpoint := pm.Spec.PodMetricsEndpoints[0] + g.Expect(string(endpoint.Interval)).To(Equal("60s"), "scrapeInterval should be 60s") + g.Expect(string(endpoint.ScrapeTimeout)).To(Equal("20s"), "scrapeTimeout should be 20s") + + // Verify endpoint configuration + g.Expect(endpoint.Port).To(Equal("prom-exporter"), "Port should be prom-exporter") + g.Expect(endpoint.Path).To(Equal("/metrics"), "Path should be /metrics") + + // Verify metadata + g.Expect(pm.ObjectMeta.Namespace).To(Equal("default"), "Namespace should match Aggregator namespace") +} + +func TestCreateVectorAggregatorPodMonitor_WithDefaults(t *testing.T) { + g := NewWithT(t) + + ctrl := createTestController("test-aggregator", "default", + &vectorv1alpha1.VectorAggregatorCommon{ + VectorCommon: vectorv1alpha1.VectorCommon{ + // No scrape settings specified + }, + }, false) + + pm := ctrl.createVectorAggregatorPodMonitor() + + g.Expect(pm).NotTo(BeNil()) + g.Expect(pm.Spec.PodMetricsEndpoints).To(HaveLen(1)) + + endpoint := pm.Spec.PodMetricsEndpoints[0] + // When not specified, fields should be empty (Prometheus will use defaults) + g.Expect(string(endpoint.Interval)).To(BeEmpty(), "Interval should be empty when not specified") + g.Expect(string(endpoint.ScrapeTimeout)).To(BeEmpty(), "ScrapeTimeout should be empty when not specified") + + // Basic endpoint config should still be set + g.Expect(endpoint.Port).To(Equal("prom-exporter")) + g.Expect(endpoint.Path).To(Equal("/metrics")) +} + +func TestCreateVectorAggregatorPodMonitor_LabelSelector(t *testing.T) { + g := NewWithT(t) + + ctrl := createTestController("test-aggregator", "test-namespace", + &vectorv1alpha1.VectorAggregatorCommon{}, false) + + pm := ctrl.createVectorAggregatorPodMonitor() + + // Verify selector has proper labels to target only Aggregator pods + g.Expect(pm.Spec.Selector.MatchLabels).To(HaveKeyWithValue("app.kubernetes.io/component", "Aggregator"), + "Selector should include component=Aggregator label") + g.Expect(pm.Spec.Selector.MatchLabels).To(HaveKeyWithValue("app.kubernetes.io/instance", "test-aggregator"), + "Selector should include instance label matching Aggregator name") +} + +func TestCreateVectorAggregatorPodMonitor_OnlyIntervalSet(t *testing.T) { + g := NewWithT(t) + + ctrl := createTestController("test-aggregator", "default", + &vectorv1alpha1.VectorAggregatorCommon{ + VectorCommon: vectorv1alpha1.VectorCommon{ + ScrapeInterval: "1m", + // ScrapeTimeout not set + }, + }, false) + + pm := ctrl.createVectorAggregatorPodMonitor() + + endpoint := pm.Spec.PodMetricsEndpoints[0] + g.Expect(string(endpoint.Interval)).To(Equal("1m"), "Interval should be set") + g.Expect(string(endpoint.ScrapeTimeout)).To(BeEmpty(), "Timeout should remain empty") +} + +func TestCreateVectorAggregatorPodMonitor_OnlyTimeoutSet(t *testing.T) { + g := NewWithT(t) + + ctrl := createTestController("test-aggregator", "default", + &vectorv1alpha1.VectorAggregatorCommon{ + VectorCommon: vectorv1alpha1.VectorCommon{ + // ScrapeInterval not set + ScrapeTimeout: "30s", + }, + }, false) + + pm := ctrl.createVectorAggregatorPodMonitor() + + endpoint := pm.Spec.PodMetricsEndpoints[0] + g.Expect(string(endpoint.Interval)).To(BeEmpty(), "Interval should remain empty") + g.Expect(string(endpoint.ScrapeTimeout)).To(Equal("30s"), "Timeout should be set") +} + +func TestCreateVectorAggregatorPodMonitor_DurationFormats(t *testing.T) { + testCases := []struct { + name string + interval string + timeout string + expectedInt string + expectedTime string + }{ + { + name: "Seconds format", + interval: "60s", + timeout: "20s", + expectedInt: "60s", + expectedTime: "20s", + }, + { + name: "Minutes format", + interval: "10m", + timeout: "2m", + expectedInt: "10m", + expectedTime: "2m", + }, + { + name: "Mixed format", + interval: "2m30s", + timeout: "45s", + expectedInt: "2m30s", + expectedTime: "45s", + }, + { + name: "Milliseconds format", + interval: "1000ms", + timeout: "500ms", + expectedInt: "1000ms", + expectedTime: "500ms", + }, + { + name: "Hours format", + interval: "2h", + timeout: "1h", + expectedInt: "2h", + expectedTime: "1h", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + g := NewWithT(t) + + ctrl := createTestController("test-aggregator", "default", + &vectorv1alpha1.VectorAggregatorCommon{ + VectorCommon: vectorv1alpha1.VectorCommon{ + ScrapeInterval: tc.interval, + ScrapeTimeout: tc.timeout, + }, + }, false) + + pm := ctrl.createVectorAggregatorPodMonitor() + endpoint := pm.Spec.PodMetricsEndpoints[0] + + g.Expect(string(endpoint.Interval)).To(Equal(tc.expectedInt)) + g.Expect(string(endpoint.ScrapeTimeout)).To(Equal(tc.expectedTime)) + }) + } +} + +func TestCreateVectorAggregatorPodMonitor_ClusterAggregator(t *testing.T) { + g := NewWithT(t) + + ctrl := createTestController("cluster-test-aggregator", "vector-system", + &vectorv1alpha1.VectorAggregatorCommon{ + VectorCommon: vectorv1alpha1.VectorCommon{ + ScrapeInterval: "90s", + ScrapeTimeout: "25s", + }, + }, true) + + pm := ctrl.createVectorAggregatorPodMonitor() + + // Verify ClusterVectorAggregator also gets proper PodMonitor + g.Expect(pm).NotTo(BeNil()) + + endpoint := pm.Spec.PodMetricsEndpoints[0] + g.Expect(string(endpoint.Interval)).To(Equal("90s")) + g.Expect(string(endpoint.ScrapeTimeout)).To(Equal("25s")) + + // Verify selector for ClusterVectorAggregator + g.Expect(pm.Spec.Selector.MatchLabels).To(HaveKeyWithValue("app.kubernetes.io/component", "Aggregator")) + g.Expect(pm.Spec.Selector.MatchLabels).To(HaveKeyWithValue("app.kubernetes.io/instance", "cluster-test-aggregator")) +} diff --git a/internal/vector/vectoragent/vectoragent_podmonitor.go b/internal/vector/vectoragent/vectoragent_podmonitor.go index dd090779..ae44d7e6 100644 --- a/internal/vector/vectoragent/vectoragent_podmonitor.go +++ b/internal/vector/vectoragent/vectoragent_podmonitor.go @@ -11,15 +11,22 @@ func (ctrl *Controller) createVectorAgentPodMonitor() *monitorv1.PodMonitor { matchLabels := ctrl.matchLabelsForVectorAgent() annotations := ctrl.annotationsForVectorAgent() + endpoint := monitorv1.PodMetricsEndpoint{ + Path: "/metrics", + Port: "prom-exporter", + } + + if ctrl.Vector.Spec.Agent.ScrapeInterval != "" { + endpoint.Interval = monitorv1.Duration(ctrl.Vector.Spec.Agent.ScrapeInterval) + } + if ctrl.Vector.Spec.Agent.ScrapeTimeout != "" { + endpoint.ScrapeTimeout = monitorv1.Duration(ctrl.Vector.Spec.Agent.ScrapeTimeout) + } + podmonitor := &monitorv1.PodMonitor{ ObjectMeta: ctrl.objectMetaVectorAgent(labels, annotations, ctrl.Vector.Namespace), Spec: monitorv1.PodMonitorSpec{ - PodMetricsEndpoints: []monitorv1.PodMetricsEndpoint{ - { - Path: "/metrics", - Port: "prom-exporter", - }, - }, + PodMetricsEndpoints: []monitorv1.PodMetricsEndpoint{endpoint}, Selector: metav1.LabelSelector{ MatchLabels: matchLabels, }, diff --git a/internal/vector/vectoragent/vectoragent_podmonitor_test.go b/internal/vector/vectoragent/vectoragent_podmonitor_test.go new file mode 100644 index 00000000..741d38d1 --- /dev/null +++ b/internal/vector/vectoragent/vectoragent_podmonitor_test.go @@ -0,0 +1,236 @@ +package vectoragent + +import ( + "testing" + + . "github.com/onsi/gomega" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + vectorv1alpha1 "github.com/kaasops/vector-operator/api/v1alpha1" +) + +func TestCreateVectorAgentPodMonitor_WithCustomSettings(t *testing.T) { + g := NewWithT(t) + + ctrl := &Controller{ + Vector: &vectorv1alpha1.Vector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-vector", + Namespace: "default", + }, + Spec: vectorv1alpha1.VectorSpec{ + Agent: &vectorv1alpha1.VectorAgent{ + VectorCommon: vectorv1alpha1.VectorCommon{ + ScrapeInterval: "45s", + ScrapeTimeout: "15s", + }, + }, + }, + }, + } + + pm := ctrl.createVectorAgentPodMonitor() + + // Verify PodMonitor structure + g.Expect(pm).NotTo(BeNil(), "PodMonitor should not be nil") + g.Expect(pm.Spec.PodMetricsEndpoints).To(HaveLen(1), "Should have exactly one endpoint") + + // Verify scrape settings + endpoint := pm.Spec.PodMetricsEndpoints[0] + g.Expect(string(endpoint.Interval)).To(Equal("45s"), "scrapeInterval should be 45s") + g.Expect(string(endpoint.ScrapeTimeout)).To(Equal("15s"), "scrapeTimeout should be 15s") + + // Verify endpoint configuration + g.Expect(endpoint.Port).To(Equal("prom-exporter"), "Port should be prom-exporter") + g.Expect(endpoint.Path).To(Equal("/metrics"), "Path should be /metrics") + + // Verify metadata + g.Expect(pm.ObjectMeta.Namespace).To(Equal("default"), "Namespace should match Vector namespace") +} + +func TestCreateVectorAgentPodMonitor_WithDefaults(t *testing.T) { + g := NewWithT(t) + + ctrl := &Controller{ + Vector: &vectorv1alpha1.Vector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-vector", + Namespace: "default", + }, + Spec: vectorv1alpha1.VectorSpec{ + Agent: &vectorv1alpha1.VectorAgent{ + VectorCommon: vectorv1alpha1.VectorCommon{ + // No scrape settings specified + }, + }, + }, + }, + } + + pm := ctrl.createVectorAgentPodMonitor() + + g.Expect(pm).NotTo(BeNil()) + g.Expect(pm.Spec.PodMetricsEndpoints).To(HaveLen(1)) + + endpoint := pm.Spec.PodMetricsEndpoints[0] + // When not specified, fields should be empty (Prometheus will use defaults) + g.Expect(string(endpoint.Interval)).To(BeEmpty(), "Interval should be empty when not specified") + g.Expect(string(endpoint.ScrapeTimeout)).To(BeEmpty(), "ScrapeTimeout should be empty when not specified") + + // Basic endpoint config should still be set + g.Expect(endpoint.Port).To(Equal("prom-exporter")) + g.Expect(endpoint.Path).To(Equal("/metrics")) +} + +func TestCreateVectorAgentPodMonitor_LabelSelector(t *testing.T) { + g := NewWithT(t) + + ctrl := &Controller{ + Vector: &vectorv1alpha1.Vector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-vector-agent", + Namespace: "test-namespace", + }, + Spec: vectorv1alpha1.VectorSpec{ + Agent: &vectorv1alpha1.VectorAgent{}, + }, + }, + } + + pm := ctrl.createVectorAgentPodMonitor() + + // Verify selector has proper labels to target only Agent pods + g.Expect(pm.Spec.Selector.MatchLabels).To(HaveKeyWithValue("app.kubernetes.io/component", "Agent"), + "Selector should include component=Agent label") + g.Expect(pm.Spec.Selector.MatchLabels).To(HaveKeyWithValue("app.kubernetes.io/instance", "test-vector-agent"), + "Selector should include instance label matching Vector name") +} + +func TestCreateVectorAgentPodMonitor_OnlyIntervalSet(t *testing.T) { + g := NewWithT(t) + + ctrl := &Controller{ + Vector: &vectorv1alpha1.Vector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-vector", + Namespace: "default", + }, + Spec: vectorv1alpha1.VectorSpec{ + Agent: &vectorv1alpha1.VectorAgent{ + VectorCommon: vectorv1alpha1.VectorCommon{ + ScrapeInterval: "30s", + // ScrapeTimeout not set + }, + }, + }, + }, + } + + pm := ctrl.createVectorAgentPodMonitor() + + endpoint := pm.Spec.PodMetricsEndpoints[0] + g.Expect(string(endpoint.Interval)).To(Equal("30s"), "Interval should be set") + g.Expect(string(endpoint.ScrapeTimeout)).To(BeEmpty(), "Timeout should remain empty") +} + +func TestCreateVectorAgentPodMonitor_OnlyTimeoutSet(t *testing.T) { + g := NewWithT(t) + + ctrl := &Controller{ + Vector: &vectorv1alpha1.Vector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-vector", + Namespace: "default", + }, + Spec: vectorv1alpha1.VectorSpec{ + Agent: &vectorv1alpha1.VectorAgent{ + VectorCommon: vectorv1alpha1.VectorCommon{ + // ScrapeInterval not set + ScrapeTimeout: "10s", + }, + }, + }, + }, + } + + pm := ctrl.createVectorAgentPodMonitor() + + endpoint := pm.Spec.PodMetricsEndpoints[0] + g.Expect(string(endpoint.Interval)).To(BeEmpty(), "Interval should remain empty") + g.Expect(string(endpoint.ScrapeTimeout)).To(Equal("10s"), "Timeout should be set") +} + +func TestCreateVectorAgentPodMonitor_DurationFormats(t *testing.T) { + testCases := []struct { + name string + interval string + timeout string + expectedInt string + expectedTime string + }{ + { + name: "Seconds format", + interval: "30s", + timeout: "10s", + expectedInt: "30s", + expectedTime: "10s", + }, + { + name: "Minutes format", + interval: "5m", + timeout: "1m", + expectedInt: "5m", + expectedTime: "1m", + }, + { + name: "Mixed format", + interval: "1m30s", + timeout: "30s", + expectedInt: "1m30s", + expectedTime: "30s", + }, + { + name: "Milliseconds format", + interval: "500ms", + timeout: "100ms", + expectedInt: "500ms", + expectedTime: "100ms", + }, + { + name: "Hours format", + interval: "1h", + timeout: "30m", + expectedInt: "1h", + expectedTime: "30m", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + g := NewWithT(t) + + ctrl := &Controller{ + Vector: &vectorv1alpha1.Vector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-vector", + Namespace: "default", + }, + Spec: vectorv1alpha1.VectorSpec{ + Agent: &vectorv1alpha1.VectorAgent{ + VectorCommon: vectorv1alpha1.VectorCommon{ + ScrapeInterval: tc.interval, + ScrapeTimeout: tc.timeout, + }, + }, + }, + }, + } + + pm := ctrl.createVectorAgentPodMonitor() + endpoint := pm.Spec.PodMetricsEndpoints[0] + + g.Expect(string(endpoint.Interval)).To(Equal(tc.expectedInt)) + g.Expect(string(endpoint.ScrapeTimeout)).To(Equal(tc.expectedTime)) + }) + } +} diff --git a/test/e2e/framework/framework.go b/test/e2e/framework/framework.go index 32ba098d..1b46161d 100644 --- a/test/e2e/framework/framework.go +++ b/test/e2e/framework/framework.go @@ -280,19 +280,40 @@ func (f *Framework) ApplyTestDataWithoutNamespaceReplacement(path string) { Expect(err).NotTo(HaveOccurred(), "Failed to apply test data %s", path) } -// replaceNamespace replaces hardcoded namespaces in YAML content +// DeleteTestData loads and deletes a test manifest from testdata directory +// It automatically replaces any hardcoded namespace with the framework's namespace +func (f *Framework) DeleteTestData(path string) { + By(fmt.Sprintf("deleting test data: %s", path)) + + content, err := os.ReadFile(filepath.Join(f.TestDataPath, path)) + Expect(err).NotTo(HaveOccurred(), "Failed to load test data from %s", path) + + // Replace namespace in YAML if present + yamlContent := replaceNamespace(string(content), f.namespace) + + err = f.kubectl.DeleteFromYAML(yamlContent) + Expect(err).NotTo(HaveOccurred(), "Failed to delete test data %s in namespace %s", path, f.namespace) +} + +// replaceNamespace replaces namespace placeholders and fields in YAML content func replaceNamespace(yaml, namespace string) string { - // This is a simple replacement - for production use, proper YAML parsing might be better - // But for tests this is sufficient + // Replace NAMESPACE placeholders throughout the content + // This handles cases like spec.resourceNamespace: NAMESPACE + yaml = replacePlaceholder(yaml, "NAMESPACE", namespace) + + // Also replace explicit namespace fields in metadata sections + // This handles cases like: + // namespace: some-other-namespace lines := []string{} for _, line := range splitLines(yaml) { - // Replace namespace: with namespace: + // Check for " namespace:" (2 spaces + "namespace:" = 12 chars) if len(line) > 12 && line[:12] == " namespace:" { lines = append(lines, fmt.Sprintf(" namespace: %s", namespace)) } else { lines = append(lines, line) } } + return joinLines(lines) } diff --git a/test/e2e/framework/kubectl/client.go b/test/e2e/framework/kubectl/client.go index 6d13ca7f..95e2425c 100644 --- a/test/e2e/framework/kubectl/client.go +++ b/test/e2e/framework/kubectl/client.go @@ -80,6 +80,28 @@ func (c *Client) ApplyWithoutNamespaceOverride(yamlContent string) error { return err } +// DeleteFromYAML deletes resources from YAML content +func (c *Client) DeleteFromYAML(yamlContent string) error { + // Validate namespace to prevent command injection + if err := ValidateNamespace(c.namespace); err != nil { + return fmt.Errorf("namespace validation failed: %w", err) + } + + // Log command for audit and reproducibility + log.Printf("KUBECTL_CMD: kubectl delete -f - -n %s", c.namespace) + + cmd := exec.Command("kubectl", "delete", "-f", "-", "-n", c.namespace) + cmd.Stdin = strings.NewReader(yamlContent) + output, err := utils.Run(cmd) + + // Log kubectl output for debugging + if len(output) > 0 { + fmt.Printf("kubectl delete: %s\n", string(output)) + } + + return err +} + // Get retrieves a resource by name and type func (c *Client) Get(resourceType, name string) ([]byte, error) { // Validate parameters to prevent command injection diff --git a/test/e2e/podmonitor_e2e_test.go b/test/e2e/podmonitor_e2e_test.go new file mode 100644 index 00000000..839acbf5 --- /dev/null +++ b/test/e2e/podmonitor_e2e_test.go @@ -0,0 +1,460 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package e2e + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "os/exec" + "strings" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/kaasops/vector-operator/test/e2e/framework" + "github.com/kaasops/vector-operator/test/e2e/framework/config" +) + +// PodMonitor tests verify the PodMonitor creation and configuration +// including scrapeInterval and scrapeTimeout settings +var _ = Describe("PodMonitor Configuration", Label(config.LabelSmoke, config.LabelFast), Ordered, func() { + f := framework.NewUniqueFramework("test-podmonitor") + + BeforeAll(func() { + f.Setup() + }) + + AfterAll(func() { + f.Teardown() + f.PrintMetrics() + }) + + Context("Agent PodMonitor with custom scrape settings", func() { + It("should create PodMonitor with scrapeInterval and scrapeTimeout", func() { + By("deploying Vector Agent with custom scrape settings") + f.ApplyTestData("podmonitor/agent-with-scrape-config.yaml") + + // Wait for agent resources to be created + time.Sleep(5 * time.Second) + + By("verifying PodMonitor is created") + Eventually(func() error { + return checkPodMonitorExists(f.Namespace(), "podmonitor-agent-agent") + }, config.ServiceCreateTimeout, config.DefaultPollInterval).Should(Succeed()) + + By("verifying scrapeInterval is set correctly") + interval, err := getPodMonitorScrapeInterval(f.Namespace(), "podmonitor-agent-agent") + Expect(err).NotTo(HaveOccurred()) + Expect(interval).To(Equal("45s"), "scrapeInterval should be 45s") + + By("verifying scrapeTimeout is set correctly") + timeout, err := getPodMonitorScrapeTimeout(f.Namespace(), "podmonitor-agent-agent") + Expect(err).NotTo(HaveOccurred()) + Expect(timeout).To(Equal("15s"), "scrapeTimeout should be 15s") + }) + + It("should create PodMonitor with default settings when not specified", func() { + By("deploying Vector Agent with default settings") + f.ApplyTestData("podmonitor/agent-with-defaults.yaml") + + // Wait for agent resources to be created + time.Sleep(5 * time.Second) + + By("verifying PodMonitor is created") + Eventually(func() error { + return checkPodMonitorExists(f.Namespace(), "podmonitor-agent-defaults-agent") + }, config.ServiceCreateTimeout, config.DefaultPollInterval).Should(Succeed()) + + By("verifying scrapeInterval is not set (uses Prometheus default)") + interval, err := getPodMonitorScrapeInterval(f.Namespace(), "podmonitor-agent-defaults-agent") + Expect(err).NotTo(HaveOccurred()) + Expect(interval).To(BeEmpty(), "scrapeInterval should be empty when not specified") + + By("verifying scrapeTimeout is not set (uses Prometheus default)") + timeout, err := getPodMonitorScrapeTimeout(f.Namespace(), "podmonitor-agent-defaults-agent") + Expect(err).NotTo(HaveOccurred()) + Expect(timeout).To(BeEmpty(), "scrapeTimeout should be empty when not specified") + }) + + It("should NOT create PodMonitor when internalMetrics is disabled", func() { + By("deploying Vector Agent with internalMetrics disabled") + f.ApplyTestData("podmonitor/agent-no-metrics.yaml") + + // Wait for agent resources to be created + time.Sleep(5 * time.Second) + + By("verifying PodMonitor is NOT created") + Consistently(func() error { + return checkPodMonitorExists(f.Namespace(), "podmonitor-agent-no-metrics-agent") + }, 10*time.Second, time.Second).Should(HaveOccurred(), "PodMonitor should not exist when internalMetrics is disabled") + }) + }) + + Context("Aggregator PodMonitor with custom scrape settings", func() { + It("should create PodMonitor with scrapeInterval and scrapeTimeout", func() { + By("deploying VectorAggregator with custom scrape settings") + f.ApplyTestData("podmonitor/aggregator-with-scrape-config.yaml") + f.WaitForDeploymentReady("podmonitor-aggregator-aggregator") + + By("verifying PodMonitor is created") + Eventually(func() error { + return checkPodMonitorExists(f.Namespace(), "podmonitor-aggregator-aggregator") + }, config.ServiceCreateTimeout, config.DefaultPollInterval).Should(Succeed()) + + By("verifying scrapeInterval is set correctly") + interval, err := getPodMonitorScrapeInterval(f.Namespace(), "podmonitor-aggregator-aggregator") + Expect(err).NotTo(HaveOccurred()) + Expect(interval).To(Equal("60s"), "scrapeInterval should be 60s") + + By("verifying scrapeTimeout is set correctly") + timeout, err := getPodMonitorScrapeTimeout(f.Namespace(), "podmonitor-aggregator-aggregator") + Expect(err).NotTo(HaveOccurred()) + Expect(timeout).To(Equal("20s"), "scrapeTimeout should be 20s") + }) + + It("should create PodMonitor with default settings when not specified", func() { + By("deploying VectorAggregator with default settings") + f.ApplyTestData("podmonitor/aggregator-with-defaults.yaml") + f.WaitForDeploymentReady("podmonitor-aggregator-defaults-aggregator") + + By("verifying PodMonitor is created") + Eventually(func() error { + return checkPodMonitorExists(f.Namespace(), "podmonitor-aggregator-defaults-aggregator") + }, config.ServiceCreateTimeout, config.DefaultPollInterval).Should(Succeed()) + + By("verifying scrapeInterval is not set (uses Prometheus default)") + interval, err := getPodMonitorScrapeInterval(f.Namespace(), "podmonitor-aggregator-defaults-aggregator") + Expect(err).NotTo(HaveOccurred()) + Expect(interval).To(BeEmpty(), "scrapeInterval should be empty when not specified") + + By("verifying scrapeTimeout is not set (uses Prometheus default)") + timeout, err := getPodMonitorScrapeTimeout(f.Namespace(), "podmonitor-aggregator-defaults-aggregator") + Expect(err).NotTo(HaveOccurred()) + Expect(timeout).To(BeEmpty(), "scrapeTimeout should be empty when not specified") + }) + }) + + Context("PodMonitor label selectors", func() { + It("should have correct matchLabels to select only related pods", func() { + By("verifying Agent PodMonitor selector") + selector, err := getPodMonitorSelector(f.Namespace(), "podmonitor-agent-agent") + Expect(err).NotTo(HaveOccurred()) + + By("checking selector contains component=Agent") + Expect(selector).To(HaveKeyWithValue("app.kubernetes.io/component", "Agent")) + Expect(selector).To(HaveKeyWithValue("app.kubernetes.io/instance", "podmonitor-agent")) + + By("verifying Aggregator PodMonitor selector") + selectorAgg, err := getPodMonitorSelector(f.Namespace(), "podmonitor-aggregator-aggregator") + Expect(err).NotTo(HaveOccurred()) + + By("checking selector contains component=Aggregator") + Expect(selectorAgg).To(HaveKeyWithValue("app.kubernetes.io/component", "Aggregator")) + Expect(selectorAgg).To(HaveKeyWithValue("app.kubernetes.io/instance", "podmonitor-aggregator")) + }) + }) + + Context("ClusterVectorAggregator PodMonitor with custom scrape settings", func() { + It("should create PodMonitor with scrapeInterval and scrapeTimeout", func() { + By("deploying ClusterVectorAggregator with custom scrape settings") + f.ApplyTestData("podmonitor/cluster-aggregator-with-scrape-config.yaml") + f.WaitForDeploymentReady("podmonitor-cluster-agg-aggregator") + + By("verifying PodMonitor is created") + Eventually(func() error { + return checkPodMonitorExists(f.Namespace(), "podmonitor-cluster-agg-aggregator") + }, config.ServiceCreateTimeout, config.DefaultPollInterval).Should(Succeed()) + + By("verifying scrapeInterval is set correctly") + interval, err := getPodMonitorScrapeInterval(f.Namespace(), "podmonitor-cluster-agg-aggregator") + Expect(err).NotTo(HaveOccurred()) + Expect(interval).To(Equal("90s"), "scrapeInterval should be 90s") + + By("verifying scrapeTimeout is set correctly") + timeout, err := getPodMonitorScrapeTimeout(f.Namespace(), "podmonitor-cluster-agg-aggregator") + Expect(err).NotTo(HaveOccurred()) + Expect(timeout).To(Equal("25s"), "scrapeTimeout should be 25s") + }) + + It("should create PodMonitor with default settings when not specified", func() { + By("deploying ClusterVectorAggregator with default settings") + f.ApplyTestData("podmonitor/cluster-aggregator-with-defaults.yaml") + f.WaitForDeploymentReady("podmonitor-cluster-agg-defaults-aggregator") + + By("verifying PodMonitor is created") + Eventually(func() error { + return checkPodMonitorExists(f.Namespace(), "podmonitor-cluster-agg-defaults-aggregator") + }, config.ServiceCreateTimeout, config.DefaultPollInterval).Should(Succeed()) + + By("verifying scrapeInterval is not set (uses Prometheus default)") + interval, err := getPodMonitorScrapeInterval(f.Namespace(), "podmonitor-cluster-agg-defaults-aggregator") + Expect(err).NotTo(HaveOccurred()) + Expect(interval).To(BeEmpty(), "scrapeInterval should be empty when not specified") + + By("verifying scrapeTimeout is not set (uses Prometheus default)") + timeout, err := getPodMonitorScrapeTimeout(f.Namespace(), "podmonitor-cluster-agg-defaults-aggregator") + Expect(err).NotTo(HaveOccurred()) + Expect(timeout).To(BeEmpty(), "scrapeTimeout should be empty when not specified") + }) + + It("should have correct matchLabels to select only ClusterVectorAggregator pods", func() { + By("verifying ClusterVectorAggregator PodMonitor selector") + selector, err := getPodMonitorSelector(f.Namespace(), "podmonitor-cluster-agg-aggregator") + Expect(err).NotTo(HaveOccurred()) + + By("checking selector contains component=Aggregator") + Expect(selector).To(HaveKeyWithValue("app.kubernetes.io/component", "Aggregator")) + Expect(selector).To(HaveKeyWithValue("app.kubernetes.io/instance", "podmonitor-cluster-agg")) + }) + }) +}) + +// InternalMetrics tests verify the isExporterSinkExists logic +var _ = Describe("Internal Metrics Exporter Logic", Label(config.LabelSmoke, config.LabelFast), Ordered, func() { + f := framework.NewUniqueFramework("test-exporter-logic") + + BeforeAll(func() { + f.Setup() + }) + + AfterAll(func() { + f.Teardown() + f.PrintMetrics() + }) + + Context("Auto-add prometheus_exporter when not present", func() { + It("should add default prometheus_exporter when pipeline has no exporter sink", func() { + By("deploying test pod with app=test label") + f.ApplyTestData("podmonitor/test-pod.yaml") + f.WaitForPodReady("test-app") + + By("deploying Vector Agent with internalMetrics enabled") + f.ApplyTestData("podmonitor/agent-with-defaults.yaml") + time.Sleep(5 * time.Second) + + By("creating pipeline without prometheus_exporter sink") + f.ApplyTestData("podmonitor/pipeline-without-exporter.yaml") + f.WaitForPipelineValid("no-exporter-pipeline") + + By("verifying agent config contains default prometheus_exporter") + Eventually(func() bool { + return checkConfigHasExporter(f.Namespace(), "podmonitor-agent-defaults-agent", "internalMetricsSink") + }, config.ServiceCreateTimeout, config.DefaultPollInterval).Should(BeTrue(), + "Agent config should have auto-added prometheus_exporter") + }) + }) + + Context("Skip adding prometheus_exporter when already present", func() { + It("should NOT add default prometheus_exporter when pipeline already has one", func() { + By("deploying Vector Agent with internalMetrics enabled") + // Using existing agent from previous test + + By("creating pipeline WITH custom prometheus_exporter sink") + f.ApplyTestData("podmonitor/pipeline-with-custom-exporter.yaml") + f.WaitForPipelineValid("custom-exporter-pipeline") + + By("verifying agent config uses custom exporter from pipeline") + Eventually(func() bool { + return checkConfigHasExporter(f.Namespace(), "podmonitor-agent-defaults-agent", "custom_prom_exporter") + }, config.ServiceCreateTimeout, config.DefaultPollInterval).Should(BeTrue(), + "Agent config should have custom prometheus_exporter from pipeline") + + By("verifying default exporter is NOT added when custom exporter exists") + // When user provides custom prometheus_exporter, the default should NOT be added + // because isExporterSinkExists() detects the custom exporter + Consistently(func() bool { + return !checkConfigHasExporter(f.Namespace(), "podmonitor-agent-defaults-agent", "internalMetricsSink") + }, 10*time.Second, 2*time.Second).Should(BeTrue(), + "Default exporter should NOT be added when custom exporter exists") + }) + }) +}) + +// PodMonitor Update tests verify that PodMonitor updates when CRD changes +var _ = Describe("PodMonitor Update Behavior", Label(config.LabelSmoke, config.LabelFast), Ordered, func() { + f := framework.NewUniqueFramework("test-podmonitor-update") + + BeforeAll(func() { + f.Setup() + }) + + AfterAll(func() { + f.Teardown() + f.PrintMetrics() + }) + + Context("Update scrapeInterval and scrapeTimeout", func() { + It("should update PodMonitor when Agent scrapeInterval changes", func() { + By("deploying Vector Agent with initial scrapeInterval=45s") + f.ApplyTestData("podmonitor/agent-with-scrape-config.yaml") + time.Sleep(5 * time.Second) + + By("verifying initial scrapeInterval is 45s") + Eventually(func() string { + interval, _ := getPodMonitorScrapeInterval(f.Namespace(), "podmonitor-agent-agent") + return interval + }, config.ServiceCreateTimeout, config.DefaultPollInterval).Should(Equal("45s"), + "Initial scrapeInterval should be 45s") + + By("updating Vector Agent with new scrapeInterval=90s") + f.ApplyTestData("podmonitor/agent-with-updated-interval.yaml") + + By("verifying PodMonitor scrapeInterval updates to 90s") + Eventually(func() string { + interval, _ := getPodMonitorScrapeInterval(f.Namespace(), "podmonitor-agent-agent") + return interval + }, config.ServiceCreateTimeout, config.DefaultPollInterval).Should(Equal("90s"), + "Updated scrapeInterval should be 90s") + + By("verifying scrapeTimeout also updates to 30s") + Eventually(func() string { + timeout, _ := getPodMonitorScrapeTimeout(f.Namespace(), "podmonitor-agent-agent") + return timeout + }, config.ServiceCreateTimeout, config.DefaultPollInterval).Should(Equal("30s"), + "Updated scrapeTimeout should be 30s") + }) + }) +}) + +// PodMonitor Cleanup tests verify that PodMonitor is deleted when Vector CR is deleted +var _ = Describe("PodMonitor Cleanup Behavior", Label(config.LabelSmoke, config.LabelFast), Ordered, func() { + f := framework.NewUniqueFramework("test-podmonitor-cleanup") + + BeforeAll(func() { + f.Setup() + }) + + AfterAll(func() { + f.Teardown() + f.PrintMetrics() + }) + + Context("Delete Vector CR", func() { + It("should delete PodMonitor when Agent is deleted", func() { + By("deploying Vector Agent with PodMonitor") + f.ApplyTestData("podmonitor/agent-with-scrape-config.yaml") + time.Sleep(5 * time.Second) + + By("verifying PodMonitor exists") + Eventually(func() error { + return checkPodMonitorExists(f.Namespace(), "podmonitor-agent-agent") + }, config.ServiceCreateTimeout, config.DefaultPollInterval).Should(Succeed(), + "PodMonitor should exist after Agent creation") + + By("deleting Vector Agent CR") + f.DeleteTestData("podmonitor/agent-with-scrape-config.yaml") + + By("verifying PodMonitor is cleaned up") + Eventually(func() error { + return checkPodMonitorExists(f.Namespace(), "podmonitor-agent-agent") + }, config.ServiceCreateTimeout, config.DefaultPollInterval).Should(HaveOccurred(), + "PodMonitor should be deleted when Agent is deleted") + }) + + It("should delete PodMonitor when Aggregator is deleted", func() { + By("deploying VectorAggregator with PodMonitor") + f.ApplyTestData("podmonitor/aggregator-with-scrape-config.yaml") + f.WaitForDeploymentReady("podmonitor-aggregator-aggregator") + + By("verifying PodMonitor exists") + Eventually(func() error { + return checkPodMonitorExists(f.Namespace(), "podmonitor-aggregator-aggregator") + }, config.ServiceCreateTimeout, config.DefaultPollInterval).Should(Succeed(), + "PodMonitor should exist after Aggregator creation") + + By("deleting VectorAggregator CR") + f.DeleteTestData("podmonitor/aggregator-with-scrape-config.yaml") + + By("verifying PodMonitor is cleaned up") + Eventually(func() error { + return checkPodMonitorExists(f.Namespace(), "podmonitor-aggregator-aggregator") + }, config.ServiceCreateTimeout, config.DefaultPollInterval).Should(HaveOccurred(), + "PodMonitor should be deleted when Aggregator is deleted") + }) + }) +}) + +// Helper functions for PodMonitor verification + +func checkPodMonitorExists(namespace, name string) error { + cmd := exec.Command("kubectl", "get", "podmonitor", name, "-n", namespace) + _, err := cmd.Output() + return err +} + +func getPodMonitorScrapeInterval(namespace, name string) (string, error) { + cmd := exec.Command("kubectl", "get", "podmonitor", name, "-n", namespace, + "-o", "jsonpath={.spec.podMetricsEndpoints[0].interval}") + output, err := cmd.Output() + if err != nil { + return "", err + } + return strings.TrimSpace(string(output)), nil +} + +func getPodMonitorScrapeTimeout(namespace, name string) (string, error) { + cmd := exec.Command("kubectl", "get", "podmonitor", name, "-n", namespace, + "-o", "jsonpath={.spec.podMetricsEndpoints[0].scrapeTimeout}") + output, err := cmd.Output() + if err != nil { + return "", err + } + return strings.TrimSpace(string(output)), nil +} + +func getPodMonitorSelector(namespace, name string) (map[string]string, error) { + cmd := exec.Command("kubectl", "get", "podmonitor", name, "-n", namespace, "-o", "json") + output, err := cmd.Output() + if err != nil { + return nil, err + } + + var podMonitor struct { + Spec struct { + Selector struct { + MatchLabels map[string]string `json:"matchLabels"` + } `json:"selector"` + } `json:"spec"` + } + + if err := json.Unmarshal(output, &podMonitor); err != nil { + return nil, fmt.Errorf("failed to parse PodMonitor JSON: %w", err) + } + + return podMonitor.Spec.Selector.MatchLabels, nil +} + +func checkConfigHasExporter(namespace, secretName, exporterName string) bool { + // Get the secret containing vector config + cmd := exec.Command("kubectl", "get", "secret", secretName, "-n", namespace, + "-o", "jsonpath={.data['agent\\.json']}") + output, err := cmd.Output() + if err != nil { + return false + } + + // Decode base64 + decoded, err := base64.StdEncoding.DecodeString(string(output)) + if err != nil { + return false + } + + // Check if exporter name is in the config + return strings.Contains(string(decoded), exporterName) +} diff --git a/test/e2e/testdata/podmonitor/agent-no-metrics.yaml b/test/e2e/testdata/podmonitor/agent-no-metrics.yaml new file mode 100644 index 00000000..ab7820ec --- /dev/null +++ b/test/e2e/testdata/podmonitor/agent-no-metrics.yaml @@ -0,0 +1,8 @@ +apiVersion: observability.kaasops.io/v1alpha1 +kind: Vector +metadata: + name: podmonitor-agent-no-metrics +spec: + agent: + image: timberio/vector:0.40.0-alpine + internalMetrics: false diff --git a/test/e2e/testdata/podmonitor/agent-with-defaults.yaml b/test/e2e/testdata/podmonitor/agent-with-defaults.yaml new file mode 100644 index 00000000..cf1a2d7e --- /dev/null +++ b/test/e2e/testdata/podmonitor/agent-with-defaults.yaml @@ -0,0 +1,8 @@ +apiVersion: observability.kaasops.io/v1alpha1 +kind: Vector +metadata: + name: podmonitor-agent-defaults +spec: + agent: + image: timberio/vector:0.40.0-alpine + internalMetrics: true diff --git a/test/e2e/testdata/podmonitor/agent-with-scrape-config.yaml b/test/e2e/testdata/podmonitor/agent-with-scrape-config.yaml new file mode 100644 index 00000000..15ad9a41 --- /dev/null +++ b/test/e2e/testdata/podmonitor/agent-with-scrape-config.yaml @@ -0,0 +1,10 @@ +apiVersion: observability.kaasops.io/v1alpha1 +kind: Vector +metadata: + name: podmonitor-agent +spec: + agent: + image: timberio/vector:0.40.0-alpine + internalMetrics: true + scrapeInterval: "45s" + scrapeTimeout: "15s" diff --git a/test/e2e/testdata/podmonitor/agent-with-updated-interval.yaml b/test/e2e/testdata/podmonitor/agent-with-updated-interval.yaml new file mode 100644 index 00000000..b0aed2c3 --- /dev/null +++ b/test/e2e/testdata/podmonitor/agent-with-updated-interval.yaml @@ -0,0 +1,10 @@ +apiVersion: observability.kaasops.io/v1alpha1 +kind: Vector +metadata: + name: podmonitor-agent +spec: + agent: + image: timberio/vector:0.40.0-alpine + internalMetrics: true + scrapeInterval: "90s" # Updated from 45s to 90s + scrapeTimeout: "30s" # Updated from 15s to 30s diff --git a/test/e2e/testdata/podmonitor/aggregator-with-defaults.yaml b/test/e2e/testdata/podmonitor/aggregator-with-defaults.yaml new file mode 100644 index 00000000..fd23b10e --- /dev/null +++ b/test/e2e/testdata/podmonitor/aggregator-with-defaults.yaml @@ -0,0 +1,11 @@ +apiVersion: observability.kaasops.io/v1alpha1 +kind: VectorAggregator +metadata: + name: podmonitor-aggregator-defaults +spec: + image: timberio/vector:0.40.0-alpine + replicas: 1 + internalMetrics: true + selector: + matchLabels: + app: test diff --git a/test/e2e/testdata/podmonitor/aggregator-with-scrape-config.yaml b/test/e2e/testdata/podmonitor/aggregator-with-scrape-config.yaml new file mode 100644 index 00000000..48906128 --- /dev/null +++ b/test/e2e/testdata/podmonitor/aggregator-with-scrape-config.yaml @@ -0,0 +1,13 @@ +apiVersion: observability.kaasops.io/v1alpha1 +kind: VectorAggregator +metadata: + name: podmonitor-aggregator +spec: + image: timberio/vector:0.40.0-alpine + replicas: 1 + internalMetrics: true + scrapeInterval: "60s" + scrapeTimeout: "20s" + selector: + matchLabels: + app: test diff --git a/test/e2e/testdata/podmonitor/cluster-aggregator-with-defaults.yaml b/test/e2e/testdata/podmonitor/cluster-aggregator-with-defaults.yaml new file mode 100644 index 00000000..21dcb5f4 --- /dev/null +++ b/test/e2e/testdata/podmonitor/cluster-aggregator-with-defaults.yaml @@ -0,0 +1,12 @@ +apiVersion: observability.kaasops.io/v1alpha1 +kind: ClusterVectorAggregator +metadata: + name: podmonitor-cluster-agg-defaults +spec: + resourceNamespace: NAMESPACE + image: timberio/vector:0.40.0-alpine + replicas: 1 + internalMetrics: true + selector: + matchLabels: + app: cluster-test diff --git a/test/e2e/testdata/podmonitor/cluster-aggregator-with-scrape-config.yaml b/test/e2e/testdata/podmonitor/cluster-aggregator-with-scrape-config.yaml new file mode 100644 index 00000000..4dc91374 --- /dev/null +++ b/test/e2e/testdata/podmonitor/cluster-aggregator-with-scrape-config.yaml @@ -0,0 +1,14 @@ +apiVersion: observability.kaasops.io/v1alpha1 +kind: ClusterVectorAggregator +metadata: + name: podmonitor-cluster-agg +spec: + resourceNamespace: NAMESPACE + image: timberio/vector:0.40.0-alpine + replicas: 1 + internalMetrics: true + scrapeInterval: "90s" + scrapeTimeout: "25s" + selector: + matchLabels: + app: cluster-test diff --git a/test/e2e/testdata/podmonitor/pipeline-aggregator-role.yaml b/test/e2e/testdata/podmonitor/pipeline-aggregator-role.yaml new file mode 100644 index 00000000..9a6fc9b8 --- /dev/null +++ b/test/e2e/testdata/podmonitor/pipeline-aggregator-role.yaml @@ -0,0 +1,16 @@ +apiVersion: observability.kaasops.io/v1alpha1 +kind: VectorPipeline +metadata: + name: aggregator-test-pipeline + labels: + app: test +spec: + sources: + http_source: + type: http_server + address: "0.0.0.0:8080" + sinks: + blackhole: + type: blackhole + inputs: + - http_source diff --git a/test/e2e/testdata/podmonitor/pipeline-with-custom-exporter.yaml b/test/e2e/testdata/podmonitor/pipeline-with-custom-exporter.yaml new file mode 100644 index 00000000..ed3db6b3 --- /dev/null +++ b/test/e2e/testdata/podmonitor/pipeline-with-custom-exporter.yaml @@ -0,0 +1,27 @@ +apiVersion: observability.kaasops.io/v1alpha1 +kind: VectorPipeline +metadata: + name: custom-exporter-pipeline + labels: + app: test +spec: + sources: + k8s_logs: + type: kubernetes_logs + extra_label_selector: "app=test" + transforms: + log_to_metric: + type: log_to_metric + inputs: + - k8s_logs + metrics: + - type: counter + field: message + name: log_lines_total + namespace: custom + sinks: + custom_prom_exporter: + type: prometheus_exporter + inputs: + - log_to_metric + address: "0.0.0.0:9599" diff --git a/test/e2e/testdata/podmonitor/pipeline-without-exporter.yaml b/test/e2e/testdata/podmonitor/pipeline-without-exporter.yaml new file mode 100644 index 00000000..f80f9e87 --- /dev/null +++ b/test/e2e/testdata/podmonitor/pipeline-without-exporter.yaml @@ -0,0 +1,16 @@ +apiVersion: observability.kaasops.io/v1alpha1 +kind: VectorPipeline +metadata: + name: no-exporter-pipeline + labels: + app: test +spec: + sources: + k8s_logs: + type: kubernetes_logs + extra_label_selector: "app=test" + sinks: + blackhole: + type: blackhole + inputs: + - k8s_logs diff --git a/test/e2e/testdata/podmonitor/test-pod.yaml b/test/e2e/testdata/podmonitor/test-pod.yaml new file mode 100644 index 00000000..dae0145c --- /dev/null +++ b/test/e2e/testdata/podmonitor/test-pod.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Pod +metadata: + name: test-app + labels: + app: test +spec: + containers: + - name: nginx + image: nginx:alpine + command: ["/bin/sh", "-c"] + args: + - | + while true; do + echo "Test log message from test-app" + sleep 5 + done From 68c12c6642cce214025e2391b78156d0611b0b3b Mon Sep 17 00:00:00 2001 From: aa1ex <115736127+aa1ex@users.noreply.github.com> Date: Tue, 25 Nov 2025 17:18:07 +0200 Subject: [PATCH 311/316] test(e2e): add CVP selector matching regression tests (#212) Regression tests for issue #201 / PR #208 - verify CVP is validated only against CVA with matching selector. --- test/e2e/framework/framework.go | 73 ++++++++++ test/e2e/framework/kubectl/client.go | 18 +++ test/e2e/selector_matching_e2e_test.go | 132 ++++++++++++++++++ .../e2e/testdata/selector-matching/agent.yaml | 7 + .../selector-matching/cva-matching.yaml | 12 ++ .../selector-matching/cva-no-selector.yaml | 9 ++ .../selector-matching/cva-non-matching.yaml | 13 ++ .../selector-matching/cvp-no-labels.yaml | 24 ++++ .../selector-matching/cvp-with-labels.yaml | 26 ++++ 9 files changed, 314 insertions(+) create mode 100644 test/e2e/selector_matching_e2e_test.go create mode 100644 test/e2e/testdata/selector-matching/agent.yaml create mode 100644 test/e2e/testdata/selector-matching/cva-matching.yaml create mode 100644 test/e2e/testdata/selector-matching/cva-no-selector.yaml create mode 100644 test/e2e/testdata/selector-matching/cva-non-matching.yaml create mode 100644 test/e2e/testdata/selector-matching/cvp-no-labels.yaml create mode 100644 test/e2e/testdata/selector-matching/cvp-with-labels.yaml diff --git a/test/e2e/framework/framework.go b/test/e2e/framework/framework.go index 1b46161d..ffece648 100644 --- a/test/e2e/framework/framework.go +++ b/test/e2e/framework/framework.go @@ -702,6 +702,50 @@ func (f *Framework) VerifyAggregatorHasPipeline(aggregatorName, pipelineName str return nil } +// VerifyAggregatorHasClusterPipeline verifies that an aggregator Secret contains the specified ClusterVectorPipeline +func (f *Framework) VerifyAggregatorHasClusterPipeline(aggregatorName, pipelineName string) error { + // Get the aggregator's vector config from the Secret + // The config is stored in a Secret with name pattern: {aggregatorName}-aggregator + secretName := fmt.Sprintf("%s-aggregator", aggregatorName) + + // Get base64-encoded config from Secret + encodedConfig, err := f.kubectl.GetWithJsonPath("secret", secretName, ".data['config\\.json']") + if err != nil { + return fmt.Errorf("failed to get aggregator secret %s: %w", secretName, err) + } + + if encodedConfig == "" { + return fmt.Errorf("aggregator secret %s has no config.json data", secretName) + } + + // Check size before decoding to prevent DoS via large payloads + maxEncodedSize := MaxConfigSize * 4 / 3 + if len(encodedConfig) > maxEncodedSize { + return fmt.Errorf("config too large: %d bytes (max %d bytes)", len(encodedConfig), maxEncodedSize) + } + + // Decode base64 + configBytes, err := base64.StdEncoding.DecodeString(encodedConfig) + if err != nil { + return fmt.Errorf("failed to decode base64 config from secret %s: %w", secretName, err) + } + config := string(configBytes) + + if config == "" { + return fmt.Errorf("aggregator %s config is empty after decoding", aggregatorName) + } + + // Check if the cluster pipeline name appears in the config + // ClusterVectorPipeline components are prefixed with only pipelinename- (no namespace prefix) + expectedPrefix := fmt.Sprintf("%s-", pipelineName) + if !strings.Contains(config, expectedPrefix) { + return fmt.Errorf("cluster pipeline %s not found in aggregator %s config (expected prefix: %s)", + pipelineName, aggregatorName, expectedPrefix) + } + + return nil +} + // ApplyTestDataWithVars loads and applies a test manifest with variable substitution func (f *Framework) ApplyTestDataWithVars(path string, vars map[string]string) { By(fmt.Sprintf("applying test data with vars: %s", path)) @@ -728,6 +772,17 @@ func (f *Framework) DeleteResource(kind, name string) { Expect(err).NotTo(HaveOccurred(), "Failed to delete %s %s in namespace %s", kind, name, f.namespace) } +// DeleteClusterResource deletes a cluster-scoped Kubernetes resource (no namespace) +func (f *Framework) DeleteClusterResource(kind, name string) { + By(fmt.Sprintf("deleting cluster resource %s %s", kind, name)) + client := kubectl.NewClient("") + err := client.DeleteClusterScoped(kind, name) + if err != nil { + // Log warning but don't fail - resource might already be deleted + GinkgoWriter.Printf("Warning: failed to delete %s %s: %v\n", kind, name, err) + } +} + // WaitForPodReadyInNamespace waits for a pod to become ready in a specific namespace func (f *Framework) WaitForPodReadyInNamespace(podName, namespace string) { By(fmt.Sprintf("waiting for pod %s to be ready in namespace %s", podName, namespace)) @@ -779,6 +834,24 @@ func (f *Framework) WaitForClusterPipelineValid(name string) { "ClusterVectorPipeline %s did not become valid", name) } +// WaitForClusterPipelineInvalid waits for a ClusterVectorPipeline to become invalid (for negative tests) +func (f *Framework) WaitForClusterPipelineInvalid(name string) { + By(fmt.Sprintf("waiting for ClusterVectorPipeline %s to become invalid", name)) + start := time.Now() + defer func() { + duration := time.Since(start) + GinkgoWriter.Printf("⏱️ ClusterVectorPipeline %s invalidated in %v\n", name, duration) + }() + + // ClusterVectorPipeline is cluster-scoped, so we use a client without namespace + client := kubectl.NewClient("") + Eventually(func() string { + result, _ := client.GetWithJsonPath("clustervectorpipeline", name, ".status.configCheckResult") + return result + }, config.PipelineValidTimeout, config.DefaultPollInterval).Should(Equal("false"), + "ClusterVectorPipeline %s did not become invalid", name) +} + // GetClusterPipelineAnnotation retrieves a specific annotation from a ClusterVectorPipeline func (f *Framework) GetClusterPipelineAnnotation(name, annotationKey string) string { jsonPath := fmt.Sprintf(".metadata.annotations['%s']", annotationKey) diff --git a/test/e2e/framework/kubectl/client.go b/test/e2e/framework/kubectl/client.go index 95e2425c..6dc35df7 100644 --- a/test/e2e/framework/kubectl/client.go +++ b/test/e2e/framework/kubectl/client.go @@ -237,6 +237,24 @@ func (c *Client) Delete(resourceType, name string) error { return err } +// DeleteClusterScoped deletes a cluster-scoped resource (no namespace) +func (c *Client) DeleteClusterScoped(resourceType, name string) error { + // Validate parameters to prevent command injection + if err := ValidateResourceType(resourceType); err != nil { + return fmt.Errorf("resource type validation failed: %w", err) + } + if err := ValidateResourceName(name); err != nil { + return fmt.Errorf("resource name validation failed: %w", err) + } + + // Log command for audit and reproducibility + log.Printf("KUBECTL_CMD: kubectl delete %s %s --ignore-not-found", resourceType, name) + + cmd := exec.Command("kubectl", "delete", resourceType, name, "--ignore-not-found") + _, err := utils.Run(cmd) + return err +} + // CreateNamespace creates a namespace func CreateNamespace(name string) error { // Validate namespace to prevent command injection diff --git a/test/e2e/selector_matching_e2e_test.go b/test/e2e/selector_matching_e2e_test.go new file mode 100644 index 00000000..8219f8f7 --- /dev/null +++ b/test/e2e/selector_matching_e2e_test.go @@ -0,0 +1,132 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package e2e + +import ( + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/kaasops/vector-operator/test/e2e/framework" + "github.com/kaasops/vector-operator/test/e2e/framework/config" +) + +// Resource names used in selector matching tests +const ( + selectorTestAgent = "test-agent" + labeledPipelineName = "labeled-pipeline" + unlabeledPipelineName = "unlabeled-pipeline" + matchingAggregatorName = "matching-aggregator" + nonMatchingAggregatorName = "non-matching-aggregator" + noSelectorAggregatorName = "no-selector-aggregator" + matchingAggregatorDeployment = matchingAggregatorName + "-aggregator" + nonMatchingAggregatorDeploy = nonMatchingAggregatorName + "-aggregator" + noSelectorAggregatorDeploy = noSelectorAggregatorName + "-aggregator" +) + +// Selector Matching tests verify that ClusterVectorPipeline is validated only against +// ClusterVectorAggregator instances whose selector matches the pipeline's labels. +// This is a regression test for issue #201 / PR #208. +// +// The bug was: configcheck validated CVP against ALL CVA instances instead of only +// those whose selector matches the CVP's labels. This caused validation failures +// when a CVP used features/config only available on specific aggregators. +var _ = Describe("Selector Matching", Label(config.LabelSmoke, config.LabelFast), Ordered, func() { + f := framework.NewUniqueFramework("test-selector-matching") + + BeforeAll(func() { + f.Setup() + + By("deploying Vector Agent") + f.ApplyTestData("selector-matching/agent.yaml") + + // Give controller time to process Vector CR and create DaemonSet + time.Sleep(5 * time.Second) + }) + + AfterAll(func() { + // Clean up cluster-scoped resources + By("cleaning up ClusterVectorPipelines") + f.DeleteClusterResource("clustervectorpipeline", labeledPipelineName) + f.DeleteClusterResource("clustervectorpipeline", unlabeledPipelineName) + + By("cleaning up ClusterVectorAggregators") + f.DeleteClusterResource("clustervectoraggregator", matchingAggregatorName) + f.DeleteClusterResource("clustervectoraggregator", nonMatchingAggregatorName) + f.DeleteClusterResource("clustervectoraggregator", noSelectorAggregatorName) + + f.Teardown() + f.PrintMetrics() + }) + + Context("CVP with labels matching CVA selector", func() { + It("should validate CVP only against matching CVA", func() { + By("deploying ClusterVectorAggregator with matching selector (team: platform)") + f.ApplyTestData("selector-matching/cva-matching.yaml") + f.WaitForDeploymentReady(matchingAggregatorDeployment) + + By("deploying ClusterVectorAggregator with non-matching selector (team: backend)") + f.ApplyTestData("selector-matching/cva-non-matching.yaml") + f.WaitForDeploymentReady(nonMatchingAggregatorDeploy) + + By("creating ClusterVectorPipeline with label team: platform") + f.ApplyTestDataWithoutNamespaceReplacement("selector-matching/cvp-with-labels.yaml") + + By("waiting for CVP to become valid") + // The pipeline should be valid because it matches "matching-aggregator" + // Before the fix in PR #208, the pipeline would be validated against ALL aggregators, + // potentially causing validation failures against non-matching aggregators + f.WaitForClusterPipelineValid(labeledPipelineName) + + By("verifying CVP role is agent (kubernetes_logs source)") + role := f.GetClusterPipelineStatus(labeledPipelineName, "role") + Expect(role).To(Equal("agent"), "Pipeline with kubernetes_logs source should have agent role") + + By("verifying CVP is processed by agent") + Eventually(func() error { + return f.VerifyAgentHasClusterPipeline(selectorTestAgent, labeledPipelineName) + }, config.ServiceCreateTimeout, config.DefaultPollInterval).Should(Succeed(), + "Pipeline should be in agent's config") + }) + }) + + Context("CVP without labels with CVA without selector", func() { + It("should validate CVP against CVA without selector", func() { + By("deploying ClusterVectorAggregator without selector") + f.ApplyTestData("selector-matching/cva-no-selector.yaml") + f.WaitForDeploymentReady(noSelectorAggregatorDeploy) + + By("creating ClusterVectorPipeline without labels") + f.ApplyTestDataWithoutNamespaceReplacement("selector-matching/cvp-no-labels.yaml") + + By("waiting for CVP to become valid") + // Pipeline without labels should match aggregator without selector + f.WaitForClusterPipelineValid(unlabeledPipelineName) + + By("verifying CVP role is agent (kubernetes_logs source)") + role := f.GetClusterPipelineStatus(unlabeledPipelineName, "role") + Expect(role).To(Equal("agent"), "Pipeline with kubernetes_logs source should have agent role") + + By("verifying CVP is processed by agent") + Eventually(func() error { + return f.VerifyAgentHasClusterPipeline(selectorTestAgent, unlabeledPipelineName) + }, config.ServiceCreateTimeout, config.DefaultPollInterval).Should(Succeed(), + "Pipeline should be in agent's config") + }) + }) +}) diff --git a/test/e2e/testdata/selector-matching/agent.yaml b/test/e2e/testdata/selector-matching/agent.yaml new file mode 100644 index 00000000..0d9fec79 --- /dev/null +++ b/test/e2e/testdata/selector-matching/agent.yaml @@ -0,0 +1,7 @@ +apiVersion: observability.kaasops.io/v1alpha1 +kind: Vector +metadata: + name: test-agent +spec: + agent: + image: timberio/vector:0.40.0-alpine diff --git a/test/e2e/testdata/selector-matching/cva-matching.yaml b/test/e2e/testdata/selector-matching/cva-matching.yaml new file mode 100644 index 00000000..700354d5 --- /dev/null +++ b/test/e2e/testdata/selector-matching/cva-matching.yaml @@ -0,0 +1,12 @@ +# ClusterVectorAggregator with selector that MATCHES pipeline labels +apiVersion: observability.kaasops.io/v1alpha1 +kind: ClusterVectorAggregator +metadata: + name: matching-aggregator +spec: + resourceNamespace: NAMESPACE + image: timberio/vector:0.40.0-alpine + replicas: 1 + selector: + matchLabels: + team: platform diff --git a/test/e2e/testdata/selector-matching/cva-no-selector.yaml b/test/e2e/testdata/selector-matching/cva-no-selector.yaml new file mode 100644 index 00000000..6a9f9073 --- /dev/null +++ b/test/e2e/testdata/selector-matching/cva-no-selector.yaml @@ -0,0 +1,9 @@ +# ClusterVectorAggregator without selector (matches all pipelines) +apiVersion: observability.kaasops.io/v1alpha1 +kind: ClusterVectorAggregator +metadata: + name: no-selector-aggregator +spec: + resourceNamespace: NAMESPACE + image: timberio/vector:0.40.0-alpine + replicas: 1 diff --git a/test/e2e/testdata/selector-matching/cva-non-matching.yaml b/test/e2e/testdata/selector-matching/cva-non-matching.yaml new file mode 100644 index 00000000..a8f5ce80 --- /dev/null +++ b/test/e2e/testdata/selector-matching/cva-non-matching.yaml @@ -0,0 +1,13 @@ +# ClusterVectorAggregator with selector that does NOT match pipeline labels +# This aggregator requires "team: backend" but our pipeline has "team: platform" +apiVersion: observability.kaasops.io/v1alpha1 +kind: ClusterVectorAggregator +metadata: + name: non-matching-aggregator +spec: + resourceNamespace: NAMESPACE + image: timberio/vector:0.40.0-alpine + replicas: 1 + selector: + matchLabels: + team: backend diff --git a/test/e2e/testdata/selector-matching/cvp-no-labels.yaml b/test/e2e/testdata/selector-matching/cvp-no-labels.yaml new file mode 100644 index 00000000..2abfa787 --- /dev/null +++ b/test/e2e/testdata/selector-matching/cvp-no-labels.yaml @@ -0,0 +1,24 @@ +# ClusterVectorPipeline without labels - should match aggregator with no selector +apiVersion: observability.kaasops.io/v1alpha1 +kind: ClusterVectorPipeline +metadata: + name: unlabeled-pipeline +spec: + sources: + logs: + type: kubernetes_logs + extra_label_selector: "app.kubernetes.io/name=vector" + transforms: + parse: + type: remap + inputs: + - logs + source: | + .unlabeled_pipeline = true + sinks: + console: + type: console + inputs: + - parse + encoding: + codec: json diff --git a/test/e2e/testdata/selector-matching/cvp-with-labels.yaml b/test/e2e/testdata/selector-matching/cvp-with-labels.yaml new file mode 100644 index 00000000..d5d0ccd9 --- /dev/null +++ b/test/e2e/testdata/selector-matching/cvp-with-labels.yaml @@ -0,0 +1,26 @@ +# ClusterVectorPipeline with labels that match "matching-aggregator" selector +apiVersion: observability.kaasops.io/v1alpha1 +kind: ClusterVectorPipeline +metadata: + name: labeled-pipeline + labels: + team: platform +spec: + sources: + logs: + type: kubernetes_logs + extra_label_selector: "app.kubernetes.io/name=vector" + transforms: + parse: + type: remap + inputs: + - logs + source: | + .labeled_pipeline = true + sinks: + console: + type: console + inputs: + - parse + encoding: + codec: json From 915c4e295790d98a11dddf048889372fcde7a545 Mon Sep 17 00:00:00 2001 From: Aleksandr Aleksandrov Date: Wed, 26 Nov 2025 09:06:27 +0200 Subject: [PATCH 312/316] chore: prepare release v0.4.0 Signed-off-by: Aleksandr Aleksandrov --- helm/charts/vector-operator/Chart.yaml | 4 +- ...y.kaasops.io_clustervectoraggregators.yaml | 12 ++ ...vability.kaasops.io_vectoraggregators.yaml | 12 ++ .../observability.kaasops.io_vectors.yaml | 12 ++ helm/index.yaml | 109 ++++++++++-------- helm/packages/vector-operator-0.8.0.tgz | Bin 0 -> 103158 bytes 6 files changed, 99 insertions(+), 50 deletions(-) create mode 100644 helm/packages/vector-operator-0.8.0.tgz diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index beafa686..a9ca582a 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: "0.7.2" +version: "0.8.0" # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.3.3" +appVersion: "v0.4.0" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectoraggregators.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectoraggregators.yaml index d56d8627..ab6fa3d0 100644 --- a/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectoraggregators.yaml +++ b/helm/charts/vector-operator/crds/observability.kaasops.io_clustervectoraggregators.yaml @@ -3147,6 +3147,18 @@ spec: schedulerName: description: SchedulerName - defines kubernetes scheduler name type: string + scrapeInterval: + description: |- + ScrapeInterval defines the interval at which Prometheus should scrape metrics. + Example values: "30s", "1m", "5m". If not specified, Prometheus default is used. + pattern: ^(0|([0-9]+(\.[0-9]+)?(ms|s|m|h))+)$ + type: string + scrapeTimeout: + description: |- + ScrapeTimeout defines the timeout for scraping metrics. + Example values: "10s", "30s". Must be less than ScrapeInterval. If not specified, Prometheus default is used. + pattern: ^(0|([0-9]+(\.[0-9]+)?(ms|s|m|h))+)$ + type: string selector: description: |- Selector defines a filter for the Vector Pipeline and Cluster Vector Pipeline by labels. diff --git a/helm/charts/vector-operator/crds/observability.kaasops.io_vectoraggregators.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_vectoraggregators.yaml index 64976644..d795466e 100644 --- a/helm/charts/vector-operator/crds/observability.kaasops.io_vectoraggregators.yaml +++ b/helm/charts/vector-operator/crds/observability.kaasops.io_vectoraggregators.yaml @@ -3141,6 +3141,18 @@ spec: schedulerName: description: SchedulerName - defines kubernetes scheduler name type: string + scrapeInterval: + description: |- + ScrapeInterval defines the interval at which Prometheus should scrape metrics. + Example values: "30s", "1m", "5m". If not specified, Prometheus default is used. + pattern: ^(0|([0-9]+(\.[0-9]+)?(ms|s|m|h))+)$ + type: string + scrapeTimeout: + description: |- + ScrapeTimeout defines the timeout for scraping metrics. + Example values: "10s", "30s". Must be less than ScrapeInterval. If not specified, Prometheus default is used. + pattern: ^(0|([0-9]+(\.[0-9]+)?(ms|s|m|h))+)$ + type: string selector: description: |- Selector defines a filter for the Vector Pipeline and Cluster Vector Pipeline by labels. diff --git a/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml b/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml index 53577da1..ddb3cce7 100644 --- a/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml +++ b/helm/charts/vector-operator/crds/observability.kaasops.io_vectors.yaml @@ -3149,6 +3149,18 @@ spec: schedulerName: description: SchedulerName - defines kubernetes scheduler name type: string + scrapeInterval: + description: |- + ScrapeInterval defines the interval at which Prometheus should scrape metrics. + Example values: "30s", "1m", "5m". If not specified, Prometheus default is used. + pattern: ^(0|([0-9]+(\.[0-9]+)?(ms|s|m|h))+)$ + type: string + scrapeTimeout: + description: |- + ScrapeTimeout defines the timeout for scraping metrics. + Example values: "10s", "30s". Must be less than ScrapeInterval. If not specified, Prometheus default is used. + pattern: ^(0|([0-9]+(\.[0-9]+)?(ms|s|m|h))+)$ + type: string tolerations: description: Tolerations If specified, the pod's tolerations. items: diff --git a/helm/index.yaml b/helm/index.yaml index 75d50fd5..9839d55a 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.4.0 + created: "2025-11-26T09:01:23.744013+02:00" + description: A Helm chart to install Vector Operator + digest: d0dbed9d90db94095875ecd12f9d3cf2fc61259179f71b28acad405748f9bd72 + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.8.0.tgz + version: 0.8.0 - apiVersion: v2 appVersion: v0.3.3 - created: "2025-11-17T12:42:54.486235+02:00" + created: "2025-11-26T09:01:23.740079+02:00" description: A Helm chart to install Vector Operator digest: d1e04fd4039e06ce24d89feb9707a7f4a65f3fd4b2bec6f4f0d937b4c9775c4f home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.7.2 - apiVersion: v2 appVersion: v0.3.2 - created: "2025-11-17T12:42:54.484496+02:00" + created: "2025-11-26T09:01:23.738231+02:00" description: A Helm chart to install Vector Operator digest: 94e6f3d7ad7f41a8edf03e72ffe2f2586f9d43d0762899025a274b1c2329088c home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.7.1 - apiVersion: v2 appVersion: v0.3.2 - created: "2025-11-17T12:42:54.487982+02:00" + created: "2025-11-26T09:01:23.742288+02:00" description: A Helm chart to install Vector Operator digest: 67fbdd5181070c542bc7b52457ff15962d6b1dcefe495939f076703f71cd0bde home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: "0.7" - apiVersion: v2 appVersion: v0.3.0 - created: "2025-11-17T12:42:54.482541+02:00" + created: "2025-11-26T09:01:23.736104+02:00" description: A Helm chart to install Vector Operator digest: 69262a286e22bfbf7571297f05d17dfc8f19e6215faa42f4dbdabea8a5610586 home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: "0.6" - apiVersion: v2 appVersion: v0.2.0 - created: "2025-11-17T12:42:54.480921+02:00" + created: "2025-11-26T09:01:23.734275+02:00" description: A Helm chart to install Vector Operator digest: f0e89cc2f3b641588e603107ba4aedc5f1ec585452c88bac46784226e56751e2 home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: "0.5" - apiVersion: v2 appVersion: v0.1.2 - created: "2025-11-17T12:42:54.479082+02:00" + created: "2025-11-26T09:01:23.732474+02:00" description: A Helm chart to install Vector Operator digest: e1fe0e96c146c7c275c181e727c8a60f21898cabe90629851a2920d2915f84b7 home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: "0.4" - apiVersion: v2 appVersion: v0.1.1 - created: "2025-11-17T12:42:54.477278+02:00" + created: "2025-11-26T09:01:23.730637+02:00" description: A Helm chart to install Vector Operator digest: a916c9e9f81bdbf9f734073fb453a6b67d7a724ed7ff4326d7884b136c103ce5 home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: "0.3" - apiVersion: v2 appVersion: v0.1.1 - created: "2025-11-17T12:42:54.475585+02:00" + created: "2025-11-26T09:01:23.728761+02:00" description: A Helm chart to install Vector Operator digest: 582d95c6f63134f6cd815bcb85adce5770e179de21a979ec76f91ab7d8531b45 home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: "0.2" - apiVersion: v2 appVersion: v0.1.1 - created: "2025-11-17T12:42:54.473719+02:00" + created: "2025-11-26T09:01:23.727083+02:00" description: A Helm chart to install Vector Operator digest: 3ac5a422f3f1861528f737a0fea077cad5f7b7516db3fe5d392c887d5d3459d5 home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: 0.1.1 - apiVersion: v2 appVersion: v0.1.0 - created: "2025-11-17T12:42:54.471729+02:00" + created: "2025-11-26T09:01:23.725146+02:00" description: A Helm chart to install Vector Operator digest: 191ec4f83f11541df19680ce220992c84fb10210f0d54a5acc29361ccfd787bb home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.1.0 - apiVersion: v2 appVersion: pre-v0.1.0-r1 - created: "2025-11-17T12:42:54.469521+02:00" + created: "2025-11-26T09:01:23.722901+02:00" description: A Helm chart to install Vector Operator digest: 01bd2e347c5782127511a0a0fbbab72508cbe667664f2c9b615cabb80a4c40c7 home: https://github.com/kaasops/vector-operator @@ -146,7 +159,7 @@ entries: version: 0.1.0-rc1 - apiVersion: v2 appVersion: v0.0.40 - created: "2025-11-17T12:42:54.46547+02:00" + created: "2025-11-26T09:01:23.719221+02:00" description: A Helm chart to install Vector Operator digest: c50e673e811b8d4c03ad45c92ce23b9bc54eef4665091e70fab9a4b7e4c6c3f1 home: https://github.com/kaasops/vector-operator @@ -159,7 +172,7 @@ entries: version: 0.0.40 - apiVersion: v2 appVersion: v0.0.39 - created: "2025-11-17T12:42:54.464527+02:00" + created: "2025-11-26T09:01:23.718536+02:00" description: A Helm chart to install Vector Operator digest: e1de38c869bf896bfb9f5f615c329d3ccce3bd91cb64bb6fa1c783a40ea290a2 home: https://github.com/kaasops/vector-operator @@ -172,7 +185,7 @@ entries: version: 0.0.39 - apiVersion: v2 appVersion: v0.0.38 - created: "2025-11-17T12:42:54.463849+02:00" + created: "2025-11-26T09:01:23.717595+02:00" description: A Helm chart to install Vector Operator digest: 565b148184400900f5572b06ceebaac6340af5e5fd1122b308bdbfcbc2d2040a home: https://github.com/kaasops/vector-operator @@ -185,7 +198,7 @@ entries: version: 0.0.38 - apiVersion: v2 appVersion: v0.0.37 - created: "2025-11-17T12:42:54.463138+02:00" + created: "2025-11-26T09:01:23.716914+02:00" description: A Helm chart to install Vector Operator digest: 65e10ee46e6855ba95f51e21ca14abc4411191b260c6b96ec72c775e73c1e331 home: https://github.com/kaasops/vector-operator @@ -198,7 +211,7 @@ entries: version: 0.0.37 - apiVersion: v2 appVersion: v0.0.36 - created: "2025-11-17T12:42:54.46229+02:00" + created: "2025-11-26T09:01:23.716227+02:00" description: A Helm chart to install Vector Operator digest: 972b6b4048b6d17b0616786e49915ef52cb7c9573cfb6eb359b6c19b66eabe31 home: https://github.com/kaasops/vector-operator @@ -211,7 +224,7 @@ entries: version: 0.0.36 - apiVersion: v2 appVersion: v0.0.35 - created: "2025-11-17T12:42:54.461582+02:00" + created: "2025-11-26T09:01:23.715432+02:00" description: A Helm chart to install Vector Operator digest: d03ba759c42f2bd8d8f1df71702ee4f26a73b8bc28760ca7254af20d811cee8a home: https://github.com/kaasops/vector-operator @@ -224,7 +237,7 @@ entries: version: 0.0.35 - apiVersion: v2 appVersion: v0.0.34 - created: "2025-11-17T12:42:54.460887+02:00" + created: "2025-11-26T09:01:23.714694+02:00" description: A Helm chart to install Vector Operator digest: 01e9d488ee78c8603d821f96344edee568ebcb42049d586de37b8df39b372bd4 home: https://github.com/kaasops/vector-operator @@ -237,7 +250,7 @@ entries: version: 0.0.34 - apiVersion: v2 appVersion: v0.0.33 - created: "2025-11-17T12:42:54.460169+02:00" + created: "2025-11-26T09:01:23.713985+02:00" description: A Helm chart to install Vector Operator digest: fcde3c94a0fa6caa5f3d333226c95b7c85ede8489d46277e1222a868ed4ec8c3 home: https://github.com/kaasops/vector-operator @@ -250,7 +263,7 @@ entries: version: 0.0.33 - apiVersion: v2 appVersion: v0.0.32 - created: "2025-11-17T12:42:54.458973+02:00" + created: "2025-11-26T09:01:23.712942+02:00" description: A Helm chart to install Vector Operator digest: 26323037ec47f1703ea930a99ab4ec8fb93b44975ce969514ea68d4130017015 home: https://github.com/kaasops/vector-operator @@ -263,7 +276,7 @@ entries: version: 0.0.32 - apiVersion: v2 appVersion: v0.0.31 - created: "2025-11-17T12:42:54.457945+02:00" + created: "2025-11-26T09:01:23.711577+02:00" description: A Helm chart to install Vector Operator digest: 45b924c07a825e0f7cd3fb534a6ffd16604790d13be1aff59150c045474754e3 home: https://github.com/kaasops/vector-operator @@ -276,7 +289,7 @@ entries: version: 0.0.31 - apiVersion: v2 appVersion: v0.0.30 - created: "2025-11-17T12:42:54.457232+02:00" + created: "2025-11-26T09:01:23.71088+02:00" description: A Helm chart to install Vector Operator digest: 03beda549d15f50325028ea29af5f2065ac0b8adf3078bf7dc1312981aa5e7db home: https://github.com/kaasops/vector-operator @@ -289,7 +302,7 @@ entries: version: 0.0.30 - apiVersion: v2 appVersion: v0.0.29 - created: "2025-11-17T12:42:54.456151+02:00" + created: "2025-11-26T09:01:23.710203+02:00" description: A Helm chart to install Vector Operator digest: 0f025fc3a924b37b8c4131c4d8cfa437d2d4e557ab9476ed3e69a00232c7dca6 home: https://github.com/kaasops/vector-operator @@ -302,7 +315,7 @@ entries: version: 0.0.29 - apiVersion: v2 appVersion: v0.0.28 - created: "2025-11-17T12:42:54.455461+02:00" + created: "2025-11-26T09:01:23.709359+02:00" description: A Helm chart to install Vector Operator digest: af856d41314313e04f15e7143409a9c564c6ca610b0d2eaec3112add8573e668 home: https://github.com/kaasops/vector-operator @@ -315,7 +328,7 @@ entries: version: 0.0.28 - apiVersion: v2 appVersion: v0.0.27 - created: "2025-11-17T12:42:54.454738+02:00" + created: "2025-11-26T09:01:23.708679+02:00" description: A Helm chart to install Vector Operator digest: 631e2ff02bbd7f247cb486494fd2af60c57cc551066a6a3858226551bc1745a4 home: https://github.com/kaasops/vector-operator @@ -328,7 +341,7 @@ entries: version: 0.0.27 - apiVersion: v2 appVersion: v0.0.26 - created: "2025-11-17T12:42:54.453874+02:00" + created: "2025-11-26T09:01:23.707987+02:00" description: A Helm chart to install Vector Operator digest: 760a2833f4c1a33466982419b079ff18d996331ebacc40cf93b0f55229cdb7db home: https://github.com/kaasops/vector-operator @@ -341,7 +354,7 @@ entries: version: 0.0.26 - apiVersion: v2 appVersion: v0.0.25 - created: "2025-11-17T12:42:54.453002+02:00" + created: "2025-11-26T09:01:23.707018+02:00" description: A Helm chart to install Vector Operator digest: fd22b996b071b6d85740ccf76e85cb640fa717c2620748d206d3f4fdd44cbcc2 home: https://github.com/kaasops/vector-operator @@ -354,7 +367,7 @@ entries: version: 0.0.25 - apiVersion: v2 appVersion: v0.0.24 - created: "2025-11-17T12:42:54.452119+02:00" + created: "2025-11-26T09:01:23.706221+02:00" description: A Helm chart to install Vector Operator digest: ea257e60ecde063a0d1ed52ce5e3283245b8f0e2daba58ea3a5adb0ba82d7799 home: https://github.com/kaasops/vector-operator @@ -367,7 +380,7 @@ entries: version: 0.0.24 - apiVersion: v2 appVersion: v0.0.23 - created: "2025-11-17T12:42:54.451084+02:00" + created: "2025-11-26T09:01:23.705483+02:00" description: A Helm chart to install Vector Operator digest: 546d202b3b9263f789b88335263191098dfcabd5d8698105f37cad24d56a8ed0 home: https://github.com/kaasops/vector-operator @@ -380,7 +393,7 @@ entries: version: 0.0.23 - apiVersion: v2 appVersion: v0.0.22 - created: "2025-11-17T12:42:54.450342+02:00" + created: "2025-11-26T09:01:23.704543+02:00" description: A Helm chart to install Vector Operator digest: bf96ddc8ac61e9d6beb8bc763fbf3fa6025d950b29d70d80de6e8a0ea45e0411 home: https://github.com/kaasops/vector-operator @@ -393,7 +406,7 @@ entries: version: 0.0.22 - apiVersion: v2 appVersion: v0.0.21 - created: "2025-11-17T12:42:54.44935+02:00" + created: "2025-11-26T09:01:23.703482+02:00" description: A Helm chart to install Vector Operator digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d home: https://github.com/kaasops/vector-operator @@ -406,7 +419,7 @@ entries: version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2025-11-17T12:42:54.448068+02:00" + created: "2025-11-26T09:01:23.702496+02:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -419,7 +432,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2025-11-17T12:42:54.447643+02:00" + created: "2025-11-26T09:01:23.702037+02:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -432,7 +445,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2025-11-17T12:42:54.447209+02:00" + created: "2025-11-26T09:01:23.701367+02:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -445,7 +458,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2025-11-17T12:42:54.44676+02:00" + created: "2025-11-26T09:01:23.700943+02:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -458,7 +471,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2025-11-17T12:42:54.445975+02:00" + created: "2025-11-26T09:01:23.700521+02:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -471,7 +484,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2025-11-17T12:42:54.44548+02:00" + created: "2025-11-26T09:01:23.700061+02:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -484,7 +497,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2025-11-17T12:42:54.44504+02:00" + created: "2025-11-26T09:01:23.699601+02:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -497,7 +510,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2025-11-17T12:42:54.444616+02:00" + created: "2025-11-26T09:01:23.699134+02:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -510,7 +523,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2025-11-17T12:42:54.443996+02:00" + created: "2025-11-26T09:01:23.697504+02:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -523,7 +536,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2025-11-17T12:42:54.442767+02:00" + created: "2025-11-26T09:01:23.696883+02:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -536,7 +549,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2025-11-17T12:42:54.442243+02:00" + created: "2025-11-26T09:01:23.696351+02:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -549,7 +562,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2025-11-17T12:42:54.467253+02:00" + created: "2025-11-26T09:01:23.720735+02:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -562,7 +575,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2025-11-17T12:42:54.46693+02:00" + created: "2025-11-26T09:01:23.720404+02:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -575,7 +588,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2025-11-17T12:42:54.466432+02:00" + created: "2025-11-26T09:01:23.720082+02:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -588,7 +601,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2025-11-17T12:42:54.465943+02:00" + created: "2025-11-26T09:01:23.719663+02:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -601,7 +614,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2025-11-17T12:42:54.441691+02:00" + created: "2025-11-26T09:01:23.695752+02:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -612,4 +625,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2025-11-17T12:42:54.440609+02:00" +generated: "2025-11-26T09:01:23.694663+02:00" diff --git a/helm/packages/vector-operator-0.8.0.tgz b/helm/packages/vector-operator-0.8.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..63d40267d3ecdf60d1524de48d53323c22fd1d57 GIT binary patch literal 103158 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 S(*}5=~gN;8H|@ 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

A|7`eWDXY=bM(c+(C-35KHxl&DM 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 d|Jy(NXaDS<{j=%${{jF2|No6(vhM)E4* Date: Sun, 11 Jan 2026 13:47:24 +0200 Subject: [PATCH 313/316] docs: fix pipeline role descriptions and add missing aggregator CRDs/RBAC (#215) --- docs/design.md | 28 ++++++++++++++++++++++------ docs/journald-logs.md | 6 +++--- docs/logs-from-file.md | 2 +- docs/quick-start.md | 10 +++++++++- 4 files changed, 35 insertions(+), 11 deletions(-) diff --git a/docs/design.md b/docs/design.md index 2959dec2..99faae99 100644 --- a/docs/design.md +++ b/docs/design.md @@ -31,12 +31,19 @@ Specification access to [this](https://github.com/kaasops/vector-operator/blob/m The `VectorPipeline` is a namespace-scoped CRD. The `VectorPipeline` CRD defines Sources, Transforms and Sinks rules for Vector. All `VectorPipelines`, with validated configuration file, added to Vector configuration file. -Depending on the types of sources, it can take on the role of either an agent or an aggregator. +The pipeline role is determined automatically based on source types: + +**Agent role** (routed to Vector DaemonSet): +- Only [kubernetes_logs](https://vector.dev/docs/reference/configuration/sources/kubernetes_logs/) source type is allowed +- Collects logs only from the namespace where VectorPipeline is defined +- `extra_namespace_label_selector` is auto-configured to the pipeline's namespace; setting a different namespace will result in an error + +**Aggregator role** (routed to VectorAggregator in the same namespace): +- Supports aggregator source types: `kafka`, `http_server`, `amqp`, `socket`, `redis`, `nats`, etc. +- No namespace restrictions on data sources ## Restrictions -- For source available only [kubernetes_logs](https://vector.dev/docs/reference/configuration/sources/kubernetes_logs/) type -- For source field `extra_namespace_label_selector` cannot be installed. The operator control this field and sets the namespace there, where VectorPipeline is defined. -- All source types must belong to the same role. +- All sources in a pipeline must belong to the same role. Mixing agent and aggregator source types is not allowed. ## Specification Specification access to [this](https://github.com/kaasops/vector-operator/blob/main/docs/specification.md#vectorpipelinespec-clustervectorpipelinespec) page @@ -45,9 +52,18 @@ Specification access to [this](https://github.com/kaasops/vector-operator/blob/m The `ClusterVectorPipeline` is a cluster-scoped CRD. The `ClusterVectorPipeline` CRD defines Sources, Transforms and Sinks rules for Vector. All `ClusterVectorPipelines`, with validated configuration file, added to Vector configuration file. -Depending on the types of sources, it can take on the role of either an agent or an aggregator. +The pipeline role is determined automatically based on source types: + +**Agent role** (routed to Vector DaemonSet): +- Supports all agent source types: `kubernetes_logs`, `file`, `journald`, `host_metrics`, `docker_logs`, etc. +- Can collect logs from any namespace +- No restrictions on `extra_namespace_label_selector` -ClusterVectorPipelines works like VectorPipeline, but without restrictions. +**Aggregator role** (routed to ClusterVectorAggregator): +- Supports aggregator source types: `kafka`, `http_server`, `amqp`, `socket`, `redis`, `nats`, etc. + +## Restrictions +- All sources in a pipeline must belong to the same role. Mixing agent and aggregator source types is not allowed. ## Specification Specification access to [this](https://github.com/kaasops/vector-operator/blob/main/docs/specification.md#vectorpipelinespec-clustervectorpipelinespec) page diff --git a/docs/journald-logs.md b/docs/journald-logs.md index 3f9881c2..18616d55 100644 --- a/docs/journald-logs.md +++ b/docs/journald-logs.md @@ -1,10 +1,10 @@ # Secure credential -If you want collect service journald logs from node you can use example. +If you want to collect service journald logs from node you can use example. -> Type `journald` in source block work only in ClusterVectorPipeline. In VectorPipeline can use only `kubernetes_logs` type +> Type `journald` is an agent source type that requires node-level access. In VectorPipeline with an agent role, only `kubernetes_logs` is allowed. Use ClusterVectorPipeline for `journald` sources. -> If you want collect journald logs, needs to use vector-agent container with journalctl. `timberio/vector:0.48.0-debian` - for example +> If you want to collect journald logs, needs to use vector-agent container with journalctl. `timberio/vector:0.48.0-debian` - for example ```yaml diff --git a/docs/logs-from-file.md b/docs/logs-from-file.md index fe8cb775..aa38b969 100644 --- a/docs/logs-from-file.md +++ b/docs/logs-from-file.md @@ -2,7 +2,7 @@ If you want collect logs from file (like k8s-audit logs) you can use example. -> Type `file` in source block work only in ClusterVectorPipeline. In VectorPipeline can use only `kubernetes_logs` type +> Type `file` is an agent source type that requires node-level access. In VectorPipeline with agent role, only `kubernetes_logs` is allowed. Use ClusterVectorPipeline for `file` sources. ```yaml diff --git a/docs/quick-start.md b/docs/quick-start.md index 8a76173b..61adfcbe 100644 --- a/docs/quick-start.md +++ b/docs/quick-start.md @@ -78,7 +78,9 @@ git clone https://github.com/kaasops/vector-operator.git cd vector-operator kubectl apply -f config/crd/bases/observability.kaasops.io_vectors.yaml kubectl apply -f config/crd/bases/observability.kaasops.io_vectorpipelines.yaml -kubectl apply -f config/crd/bases/observability.kaasops.io_clustervectorpipelines.yaml +kubectl apply -f config/crd/bases/observability.kaasops.io_clustervectorpipelines.yaml +kubectl apply -f config/crd/bases/observability.kaasops.io_vectoraggregators.yaml +kubectl apply -f config/crd/bases/observability.kaasops.io_clustervectoraggregators.yaml ``` ## Start Vector Operator @@ -177,6 +179,8 @@ rules: - clustervectorpipelines - vectorpipelines - vectors + - vectoraggregators + - clustervectoraggregators verbs: - create - delete @@ -191,6 +195,8 @@ rules: - clustervectorpipelines/status - vectorpipelines/status - vectors/status + - vectoraggregators/status + - clustervectoraggregators/status verbs: - get - patch @@ -394,4 +400,6 @@ kubectl delete clusterrolebinding vector-operator kubectl delete -f config/crd/bases/observability.kaasops.io_vectors.yaml kubectl delete -f config/crd/bases/observability.kaasops.io_vectorpipelines.yaml kubectl delete -f config/crd/bases/observability.kaasops.io_clustervectorpipelines.yaml +kubectl delete -f config/crd/bases/observability.kaasops.io_vectoraggregators.yaml +kubectl delete -f config/crd/bases/observability.kaasops.io_clustervectoraggregators.yaml ``` From 3a8738c48ef8634b98f52c4d7c40d2b64ac407dc Mon Sep 17 00:00:00 2001 From: aa1ex <115736127+aa1ex@users.noreply.github.com> Date: Mon, 12 Jan 2026 15:19:18 +0200 Subject: [PATCH 314/316] Fix/vectorpipeline namespace validation (#216) * test(e2e): add regression test for VectorPipeline namespace validation * fix: validate VectorPipeline only against VectorAggregator in same namespace --- internal/controller/pipeline_controller.go | 4 + test/e2e/framework/framework.go | 11 ++ test/e2e/namespace_validation_e2e_test.go | 124 ++++++++++++++++++ .../namespace-validation/aggregator-ns1.yaml | 18 +++ .../namespace-validation/aggregator-ns2.yaml | 21 +++ .../namespace-validation/pipeline.yaml | 28 ++++ .../second-namespace.yaml | 6 + .../testdata/namespace-validation/secret.yaml | 10 ++ 8 files changed, 222 insertions(+) create mode 100644 test/e2e/namespace_validation_e2e_test.go create mode 100644 test/e2e/testdata/namespace-validation/aggregator-ns1.yaml create mode 100644 test/e2e/testdata/namespace-validation/aggregator-ns2.yaml create mode 100644 test/e2e/testdata/namespace-validation/pipeline.yaml create mode 100644 test/e2e/testdata/namespace-validation/second-namespace.yaml create mode 100644 test/e2e/testdata/namespace-validation/secret.yaml diff --git a/internal/controller/pipeline_controller.go b/internal/controller/pipeline_controller.go index ebdf5a51..ebdecbf2 100644 --- a/internal/controller/pipeline_controller.go +++ b/internal/controller/pipeline_controller.go @@ -193,6 +193,10 @@ func (r *PipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c if pipelineCR.GetNamespace() != "" { for _, vector := range vectorAggregators { + // VectorPipeline should only be validated against VectorAggregator in the same namespace + if vector.Namespace != pipelineCR.GetNamespace() { + continue + } var selectorLabels map[string]string if vector.Spec.Selector != nil { selectorLabels = vector.Spec.Selector.MatchLabels diff --git a/test/e2e/framework/framework.go b/test/e2e/framework/framework.go index ffece648..38c3f6e7 100644 --- a/test/e2e/framework/framework.go +++ b/test/e2e/framework/framework.go @@ -783,6 +783,17 @@ func (f *Framework) DeleteClusterResource(kind, name string) { } } +// DeleteResourceInNamespace deletes a Kubernetes resource in a specific namespace +func (f *Framework) DeleteResourceInNamespace(kind, name, namespace string) { + By(fmt.Sprintf("deleting %s %s in namespace %s", kind, name, namespace)) + client := kubectl.NewClient(namespace) + err := client.Delete(kind, name) + if err != nil { + // Log warning but don't fail - resource might already be deleted + GinkgoWriter.Printf("Warning: failed to delete %s %s in namespace %s: %v\n", kind, name, namespace, err) + } +} + // WaitForPodReadyInNamespace waits for a pod to become ready in a specific namespace func (f *Framework) WaitForPodReadyInNamespace(podName, namespace string) { By(fmt.Sprintf("waiting for pod %s to be ready in namespace %s", podName, namespace)) diff --git a/test/e2e/namespace_validation_e2e_test.go b/test/e2e/namespace_validation_e2e_test.go new file mode 100644 index 00000000..b894f237 --- /dev/null +++ b/test/e2e/namespace_validation_e2e_test.go @@ -0,0 +1,124 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package e2e + +import ( + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/kaasops/vector-operator/test/e2e/framework" + "github.com/kaasops/vector-operator/test/e2e/framework/config" +) + +// Resource names used in namespace validation tests +const ( + nsValidationSecondNamespace = "test-ns-validation-other" + nsValidationAggregatorWithSecret = "aggregator-with-secret" + nsValidationAggregatorNoSecret = "aggregator-without-secret" + nsValidationPipeline = "test-pipeline" + nsValidationSecret = "test-credentials" +) + +// Namespace Validation tests verify that VectorPipeline is validated only against +// VectorAggregator instances in the SAME namespace, not all aggregators with matching selectors. +// +// This is a regression test for the issue where VectorPipeline validation checked against +// ALL VectorAggregators with matching label selectors, regardless of namespace. +// This caused validation failures when aggregators in other namespaces were missing +// required secrets/resources that only existed in the pipeline's namespace. +// +// Related to issue #201 - the fix for ClusterVectorPipeline selector matching did not +// address the namespace isolation for namespaced VectorPipeline resources. +var _ = Describe("Namespace Validation Isolation", Label(config.LabelRegression), Ordered, func() { + f := framework.NewUniqueFramework("test-ns-validation") + + BeforeAll(func() { + f.Setup() + + By("creating second namespace for isolation test") + f.ApplyTestDataWithoutNamespaceReplacement("namespace-validation/second-namespace.yaml") + + // Give the namespace time to be created + time.Sleep(2 * time.Second) + }) + + AfterAll(func() { + By("cleaning up VectorPipeline") + f.DeleteResource("vectorpipeline", nsValidationPipeline) + + By("cleaning up VectorAggregators") + f.DeleteResource("vectoraggregator", nsValidationAggregatorWithSecret) + f.DeleteResourceInNamespace("vectoraggregator", nsValidationAggregatorNoSecret, nsValidationSecondNamespace) + + By("cleaning up secret") + f.DeleteResource("secret", nsValidationSecret) + + By("cleaning up second namespace") + f.DeleteClusterResource("namespace", nsValidationSecondNamespace) + + f.Teardown() + f.PrintMetrics() + }) + + Context("VectorPipeline with VectorAggregators in different namespaces", func() { + It("should validate VectorPipeline only against VectorAggregator in the same namespace", func() { + By("creating secret in main namespace") + f.ApplyTestData("namespace-validation/secret.yaml") + + By("deploying VectorAggregator WITH secret in main namespace") + f.ApplyTestData("namespace-validation/aggregator-ns1.yaml") + + By("deploying VectorAggregator WITHOUT secret in second namespace") + // This aggregator references the same secret that does NOT exist in the second namespace + // If the bug exists, the pipeline will be validated against this aggregator and fail + f.ApplyTestDataWithoutNamespaceReplacement("namespace-validation/aggregator-ns2.yaml") + + By("waiting for aggregator in main namespace to be ready") + f.WaitForDeploymentReady(nsValidationAggregatorWithSecret + "-aggregator") + + // Note: The aggregator in the second namespace may not become ready + // because the secret doesn't exist there, but that's expected and irrelevant + // for this test. What matters is that the pipeline in namespace 1 + // should NOT be validated against it. + + By("creating VectorPipeline with matching labels in main namespace") + f.ApplyTestData("namespace-validation/pipeline.yaml") + + By("waiting for VectorPipeline to become valid") + // If the bug exists: + // - Pipeline is validated against BOTH aggregators (both have matching selector) + // - Validation against aggregator in second namespace FAILS (missing secret) + // - Pipeline status becomes invalid + // + // If the bug is fixed: + // - Pipeline is validated ONLY against aggregator in the same namespace + // - Secret exists in main namespace + // - Pipeline status becomes valid + f.WaitForPipelineValid(nsValidationPipeline) + + By("verifying VectorPipeline has aggregator role") + role := f.GetPipelineStatus(nsValidationPipeline, "role") + Expect(role).To(Equal("aggregator"), "Pipeline with vector source should have aggregator role") + + // Note: We don't verify that the aggregator config contains the pipeline here + // because that's tested in other tests. The key assertion is that validation + // passes quickly (not timing out waiting for the aggregator in the other namespace). + }) + }) +}) diff --git a/test/e2e/testdata/namespace-validation/aggregator-ns1.yaml b/test/e2e/testdata/namespace-validation/aggregator-ns1.yaml new file mode 100644 index 00000000..244170a3 --- /dev/null +++ b/test/e2e/testdata/namespace-validation/aggregator-ns1.yaml @@ -0,0 +1,18 @@ +# VectorAggregator in the MAIN namespace with the required secret +# This aggregator has an env var from a secret that EXISTS in this namespace +apiVersion: observability.kaasops.io/v1alpha1 +kind: VectorAggregator +metadata: + name: aggregator-with-secret +spec: + image: timberio/vector:0.40.0-alpine + replicas: 1 + selector: + matchLabels: + app: ns-validation-test + env: + - name: API_KEY + valueFrom: + secretKeyRef: + name: test-credentials + key: api-key diff --git a/test/e2e/testdata/namespace-validation/aggregator-ns2.yaml b/test/e2e/testdata/namespace-validation/aggregator-ns2.yaml new file mode 100644 index 00000000..ed29b0d1 --- /dev/null +++ b/test/e2e/testdata/namespace-validation/aggregator-ns2.yaml @@ -0,0 +1,21 @@ +# VectorAggregator in the SECOND namespace WITHOUT the required secret +# This aggregator references the same secret that does NOT exist in this namespace +# If the bug exists, VectorPipeline from namespace 1 will be validated against this +# aggregator, and validation will fail because the secret doesn't exist here +apiVersion: observability.kaasops.io/v1alpha1 +kind: VectorAggregator +metadata: + name: aggregator-without-secret + namespace: test-ns-validation-other +spec: + image: timberio/vector:0.40.0-alpine + replicas: 1 + selector: + matchLabels: + app: ns-validation-test + env: + - name: API_KEY + valueFrom: + secretKeyRef: + name: test-credentials + key: api-key diff --git a/test/e2e/testdata/namespace-validation/pipeline.yaml b/test/e2e/testdata/namespace-validation/pipeline.yaml new file mode 100644 index 00000000..b9c64656 --- /dev/null +++ b/test/e2e/testdata/namespace-validation/pipeline.yaml @@ -0,0 +1,28 @@ +# VectorPipeline with labels matching the aggregators' selector +# This pipeline should ONLY be validated against the aggregator in the SAME namespace +# Bug: It gets validated against ALL aggregators with matching selector (regardless of namespace) +apiVersion: observability.kaasops.io/v1alpha1 +kind: VectorPipeline +metadata: + name: test-pipeline + labels: + app: ns-validation-test +spec: + sources: + vector_source: + type: vector + address: "0.0.0.0:9000" + transforms: + process: + type: remap + inputs: + - vector_source + source: | + .namespace_validation_test = true + sinks: + console: + type: console + inputs: + - process + encoding: + codec: json diff --git a/test/e2e/testdata/namespace-validation/second-namespace.yaml b/test/e2e/testdata/namespace-validation/second-namespace.yaml new file mode 100644 index 00000000..96f60d07 --- /dev/null +++ b/test/e2e/testdata/namespace-validation/second-namespace.yaml @@ -0,0 +1,6 @@ +# Second namespace for testing namespace isolation in VectorPipeline validation +# This namespace will have a VectorAggregator WITHOUT the required secret +apiVersion: v1 +kind: Namespace +metadata: + name: test-ns-validation-other diff --git a/test/e2e/testdata/namespace-validation/secret.yaml b/test/e2e/testdata/namespace-validation/secret.yaml new file mode 100644 index 00000000..c2be536c --- /dev/null +++ b/test/e2e/testdata/namespace-validation/secret.yaml @@ -0,0 +1,10 @@ +# Secret that will be used by VectorAggregator in the main namespace +# This secret will NOT exist in the second namespace, causing validation failure +# if the bug exists (pipeline validated against aggregator in wrong namespace) +apiVersion: v1 +kind: Secret +metadata: + name: test-credentials +type: Opaque +stringData: + api-key: "test-api-key-value" From a31e1d2817de761e6eee7ac8a86e80d92ce5d1a5 Mon Sep 17 00:00:00 2001 From: Aleksandr Aleksandrov Date: Mon, 12 Jan 2026 15:22:28 +0200 Subject: [PATCH 315/316] update helm chart version Signed-off-by: Aleksandr Aleksandrov --- helm/charts/vector-operator/Chart.yaml | 4 +- helm/index.yaml | 111 +++++++++++++----------- helm/packages/vector-operator-0.8.1.tgz | Bin 0 -> 103164 bytes 3 files changed, 64 insertions(+), 51 deletions(-) create mode 100644 helm/packages/vector-operator-0.8.1.tgz diff --git a/helm/charts/vector-operator/Chart.yaml b/helm/charts/vector-operator/Chart.yaml index a9ca582a..975f7806 100644 --- a/helm/charts/vector-operator/Chart.yaml +++ b/helm/charts/vector-operator/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: "0.8.0" +version: "0.8.1" # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.4.0" +appVersion: "v0.4.1" home: https://github.com/kaasops/vector-operator sources: diff --git a/helm/index.yaml b/helm/index.yaml index 9839d55a..4de98b7e 100644 --- a/helm/index.yaml +++ b/helm/index.yaml @@ -1,9 +1,22 @@ apiVersion: v1 entries: vector-operator: + - apiVersion: v2 + appVersion: v0.4.1 + created: "2026-01-12T15:21:52.961025+02:00" + description: A Helm chart to install Vector Operator + digest: 194c5f639c367ffa964a121bbddd2d12dca8c634ecac7c622698319f5bb4edc1 + home: https://github.com/kaasops/vector-operator + name: vector-operator + sources: + - https://github.com/kaasops/vector-operator + type: application + urls: + - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.8.1.tgz + version: 0.8.1 - apiVersion: v2 appVersion: v0.4.0 - created: "2025-11-26T09:01:23.744013+02:00" + created: "2026-01-12T15:21:52.959284+02:00" description: A Helm chart to install Vector Operator digest: d0dbed9d90db94095875ecd12f9d3cf2fc61259179f71b28acad405748f9bd72 home: https://github.com/kaasops/vector-operator @@ -16,7 +29,7 @@ entries: version: 0.8.0 - apiVersion: v2 appVersion: v0.3.3 - created: "2025-11-26T09:01:23.740079+02:00" + created: "2026-01-12T15:21:52.954929+02:00" description: A Helm chart to install Vector Operator digest: d1e04fd4039e06ce24d89feb9707a7f4a65f3fd4b2bec6f4f0d937b4c9775c4f home: https://github.com/kaasops/vector-operator @@ -29,7 +42,7 @@ entries: version: 0.7.2 - apiVersion: v2 appVersion: v0.3.2 - created: "2025-11-26T09:01:23.738231+02:00" + created: "2026-01-12T15:21:52.952852+02:00" description: A Helm chart to install Vector Operator digest: 94e6f3d7ad7f41a8edf03e72ffe2f2586f9d43d0762899025a274b1c2329088c home: https://github.com/kaasops/vector-operator @@ -42,7 +55,7 @@ entries: version: 0.7.1 - apiVersion: v2 appVersion: v0.3.2 - created: "2025-11-26T09:01:23.742288+02:00" + created: "2026-01-12T15:21:52.957255+02:00" description: A Helm chart to install Vector Operator digest: 67fbdd5181070c542bc7b52457ff15962d6b1dcefe495939f076703f71cd0bde home: https://github.com/kaasops/vector-operator @@ -55,7 +68,7 @@ entries: version: "0.7" - apiVersion: v2 appVersion: v0.3.0 - created: "2025-11-26T09:01:23.736104+02:00" + created: "2026-01-12T15:21:52.95058+02:00" description: A Helm chart to install Vector Operator digest: 69262a286e22bfbf7571297f05d17dfc8f19e6215faa42f4dbdabea8a5610586 home: https://github.com/kaasops/vector-operator @@ -68,7 +81,7 @@ entries: version: "0.6" - apiVersion: v2 appVersion: v0.2.0 - created: "2025-11-26T09:01:23.734275+02:00" + created: "2026-01-12T15:21:52.94872+02:00" description: A Helm chart to install Vector Operator digest: f0e89cc2f3b641588e603107ba4aedc5f1ec585452c88bac46784226e56751e2 home: https://github.com/kaasops/vector-operator @@ -81,7 +94,7 @@ entries: version: "0.5" - apiVersion: v2 appVersion: v0.1.2 - created: "2025-11-26T09:01:23.732474+02:00" + created: "2026-01-12T15:21:52.946885+02:00" description: A Helm chart to install Vector Operator digest: e1fe0e96c146c7c275c181e727c8a60f21898cabe90629851a2920d2915f84b7 home: https://github.com/kaasops/vector-operator @@ -94,7 +107,7 @@ entries: version: "0.4" - apiVersion: v2 appVersion: v0.1.1 - created: "2025-11-26T09:01:23.730637+02:00" + created: "2026-01-12T15:21:52.944818+02:00" description: A Helm chart to install Vector Operator digest: a916c9e9f81bdbf9f734073fb453a6b67d7a724ed7ff4326d7884b136c103ce5 home: https://github.com/kaasops/vector-operator @@ -107,7 +120,7 @@ entries: version: "0.3" - apiVersion: v2 appVersion: v0.1.1 - created: "2025-11-26T09:01:23.728761+02:00" + created: "2026-01-12T15:21:52.942628+02:00" description: A Helm chart to install Vector Operator digest: 582d95c6f63134f6cd815bcb85adce5770e179de21a979ec76f91ab7d8531b45 home: https://github.com/kaasops/vector-operator @@ -120,7 +133,7 @@ entries: version: "0.2" - apiVersion: v2 appVersion: v0.1.1 - created: "2025-11-26T09:01:23.727083+02:00" + created: "2026-01-12T15:21:52.94077+02:00" description: A Helm chart to install Vector Operator digest: 3ac5a422f3f1861528f737a0fea077cad5f7b7516db3fe5d392c887d5d3459d5 home: https://github.com/kaasops/vector-operator @@ -133,7 +146,7 @@ entries: version: 0.1.1 - apiVersion: v2 appVersion: v0.1.0 - created: "2025-11-26T09:01:23.725146+02:00" + created: "2026-01-12T15:21:52.938795+02:00" description: A Helm chart to install Vector Operator digest: 191ec4f83f11541df19680ce220992c84fb10210f0d54a5acc29361ccfd787bb home: https://github.com/kaasops/vector-operator @@ -146,7 +159,7 @@ entries: version: 0.1.0 - apiVersion: v2 appVersion: pre-v0.1.0-r1 - created: "2025-11-26T09:01:23.722901+02:00" + created: "2026-01-12T15:21:52.936173+02:00" description: A Helm chart to install Vector Operator digest: 01bd2e347c5782127511a0a0fbbab72508cbe667664f2c9b615cabb80a4c40c7 home: https://github.com/kaasops/vector-operator @@ -159,7 +172,7 @@ entries: version: 0.1.0-rc1 - apiVersion: v2 appVersion: v0.0.40 - created: "2025-11-26T09:01:23.719221+02:00" + created: "2026-01-12T15:21:52.932136+02:00" description: A Helm chart to install Vector Operator digest: c50e673e811b8d4c03ad45c92ce23b9bc54eef4665091e70fab9a4b7e4c6c3f1 home: https://github.com/kaasops/vector-operator @@ -172,7 +185,7 @@ entries: version: 0.0.40 - apiVersion: v2 appVersion: v0.0.39 - created: "2025-11-26T09:01:23.718536+02:00" + created: "2026-01-12T15:21:52.931415+02:00" description: A Helm chart to install Vector Operator digest: e1de38c869bf896bfb9f5f615c329d3ccce3bd91cb64bb6fa1c783a40ea290a2 home: https://github.com/kaasops/vector-operator @@ -185,7 +198,7 @@ entries: version: 0.0.39 - apiVersion: v2 appVersion: v0.0.38 - created: "2025-11-26T09:01:23.717595+02:00" + created: "2026-01-12T15:21:52.930689+02:00" description: A Helm chart to install Vector Operator digest: 565b148184400900f5572b06ceebaac6340af5e5fd1122b308bdbfcbc2d2040a home: https://github.com/kaasops/vector-operator @@ -198,7 +211,7 @@ entries: version: 0.0.38 - apiVersion: v2 appVersion: v0.0.37 - created: "2025-11-26T09:01:23.716914+02:00" + created: "2026-01-12T15:21:52.929527+02:00" description: A Helm chart to install Vector Operator digest: 65e10ee46e6855ba95f51e21ca14abc4411191b260c6b96ec72c775e73c1e331 home: https://github.com/kaasops/vector-operator @@ -211,7 +224,7 @@ entries: version: 0.0.37 - apiVersion: v2 appVersion: v0.0.36 - created: "2025-11-26T09:01:23.716227+02:00" + created: "2026-01-12T15:21:52.928814+02:00" description: A Helm chart to install Vector Operator digest: 972b6b4048b6d17b0616786e49915ef52cb7c9573cfb6eb359b6c19b66eabe31 home: https://github.com/kaasops/vector-operator @@ -224,7 +237,7 @@ entries: version: 0.0.36 - apiVersion: v2 appVersion: v0.0.35 - created: "2025-11-26T09:01:23.715432+02:00" + created: "2026-01-12T15:21:52.928093+02:00" description: A Helm chart to install Vector Operator digest: d03ba759c42f2bd8d8f1df71702ee4f26a73b8bc28760ca7254af20d811cee8a home: https://github.com/kaasops/vector-operator @@ -237,7 +250,7 @@ entries: version: 0.0.35 - apiVersion: v2 appVersion: v0.0.34 - created: "2025-11-26T09:01:23.714694+02:00" + created: "2026-01-12T15:21:52.927367+02:00" description: A Helm chart to install Vector Operator digest: 01e9d488ee78c8603d821f96344edee568ebcb42049d586de37b8df39b372bd4 home: https://github.com/kaasops/vector-operator @@ -250,7 +263,7 @@ entries: version: 0.0.34 - apiVersion: v2 appVersion: v0.0.33 - created: "2025-11-26T09:01:23.713985+02:00" + created: "2026-01-12T15:21:52.926406+02:00" description: A Helm chart to install Vector Operator digest: fcde3c94a0fa6caa5f3d333226c95b7c85ede8489d46277e1222a868ed4ec8c3 home: https://github.com/kaasops/vector-operator @@ -263,7 +276,7 @@ entries: version: 0.0.33 - apiVersion: v2 appVersion: v0.0.32 - created: "2025-11-26T09:01:23.712942+02:00" + created: "2026-01-12T15:21:52.925386+02:00" description: A Helm chart to install Vector Operator digest: 26323037ec47f1703ea930a99ab4ec8fb93b44975ce969514ea68d4130017015 home: https://github.com/kaasops/vector-operator @@ -276,7 +289,7 @@ entries: version: 0.0.32 - apiVersion: v2 appVersion: v0.0.31 - created: "2025-11-26T09:01:23.711577+02:00" + created: "2026-01-12T15:21:52.924352+02:00" description: A Helm chart to install Vector Operator digest: 45b924c07a825e0f7cd3fb534a6ffd16604790d13be1aff59150c045474754e3 home: https://github.com/kaasops/vector-operator @@ -289,7 +302,7 @@ entries: version: 0.0.31 - apiVersion: v2 appVersion: v0.0.30 - created: "2025-11-26T09:01:23.71088+02:00" + created: "2026-01-12T15:21:52.923148+02:00" description: A Helm chart to install Vector Operator digest: 03beda549d15f50325028ea29af5f2065ac0b8adf3078bf7dc1312981aa5e7db home: https://github.com/kaasops/vector-operator @@ -302,7 +315,7 @@ entries: version: 0.0.30 - apiVersion: v2 appVersion: v0.0.29 - created: "2025-11-26T09:01:23.710203+02:00" + created: "2026-01-12T15:21:52.922428+02:00" description: A Helm chart to install Vector Operator digest: 0f025fc3a924b37b8c4131c4d8cfa437d2d4e557ab9476ed3e69a00232c7dca6 home: https://github.com/kaasops/vector-operator @@ -315,7 +328,7 @@ entries: version: 0.0.29 - apiVersion: v2 appVersion: v0.0.28 - created: "2025-11-26T09:01:23.709359+02:00" + created: "2026-01-12T15:21:52.921671+02:00" description: A Helm chart to install Vector Operator digest: af856d41314313e04f15e7143409a9c564c6ca610b0d2eaec3112add8573e668 home: https://github.com/kaasops/vector-operator @@ -328,7 +341,7 @@ entries: version: 0.0.28 - apiVersion: v2 appVersion: v0.0.27 - created: "2025-11-26T09:01:23.708679+02:00" + created: "2026-01-12T15:21:52.920817+02:00" description: A Helm chart to install Vector Operator digest: 631e2ff02bbd7f247cb486494fd2af60c57cc551066a6a3858226551bc1745a4 home: https://github.com/kaasops/vector-operator @@ -341,7 +354,7 @@ entries: version: 0.0.27 - apiVersion: v2 appVersion: v0.0.26 - created: "2025-11-26T09:01:23.707987+02:00" + created: "2026-01-12T15:21:52.919707+02:00" description: A Helm chart to install Vector Operator digest: 760a2833f4c1a33466982419b079ff18d996331ebacc40cf93b0f55229cdb7db home: https://github.com/kaasops/vector-operator @@ -354,7 +367,7 @@ entries: version: 0.0.26 - apiVersion: v2 appVersion: v0.0.25 - created: "2025-11-26T09:01:23.707018+02:00" + created: "2026-01-12T15:21:52.918901+02:00" description: A Helm chart to install Vector Operator digest: fd22b996b071b6d85740ccf76e85cb640fa717c2620748d206d3f4fdd44cbcc2 home: https://github.com/kaasops/vector-operator @@ -367,7 +380,7 @@ entries: version: 0.0.25 - apiVersion: v2 appVersion: v0.0.24 - created: "2025-11-26T09:01:23.706221+02:00" + created: "2026-01-12T15:21:52.918099+02:00" description: A Helm chart to install Vector Operator digest: ea257e60ecde063a0d1ed52ce5e3283245b8f0e2daba58ea3a5adb0ba82d7799 home: https://github.com/kaasops/vector-operator @@ -380,7 +393,7 @@ entries: version: 0.0.24 - apiVersion: v2 appVersion: v0.0.23 - created: "2025-11-26T09:01:23.705483+02:00" + created: "2026-01-12T15:21:52.916952+02:00" description: A Helm chart to install Vector Operator digest: 546d202b3b9263f789b88335263191098dfcabd5d8698105f37cad24d56a8ed0 home: https://github.com/kaasops/vector-operator @@ -393,7 +406,7 @@ entries: version: 0.0.23 - apiVersion: v2 appVersion: v0.0.22 - created: "2025-11-26T09:01:23.704543+02:00" + created: "2026-01-12T15:21:52.916181+02:00" description: A Helm chart to install Vector Operator digest: bf96ddc8ac61e9d6beb8bc763fbf3fa6025d950b29d70d80de6e8a0ea45e0411 home: https://github.com/kaasops/vector-operator @@ -406,7 +419,7 @@ entries: version: 0.0.22 - apiVersion: v2 appVersion: v0.0.21 - created: "2025-11-26T09:01:23.703482+02:00" + created: "2026-01-12T15:21:52.915165+02:00" description: A Helm chart to install Vector Operator digest: d37b3064c0374d71e06c0131bcac2bf9e60ec4d62fcbbb20704c5277eabd899d home: https://github.com/kaasops/vector-operator @@ -419,7 +432,7 @@ entries: version: 0.0.21 - apiVersion: v2 appVersion: v0.0.20 - created: "2025-11-26T09:01:23.702496+02:00" + created: "2026-01-12T15:21:52.914135+02:00" description: A Helm chart to install Vector Operator digest: b95cd9ea8b74fde85175411129f77bf7a7afb4e9324ba2d02d489d0d6ef42d6d home: https://github.com/kaasops/vector-operator @@ -432,7 +445,7 @@ entries: version: 0.0.20 - apiVersion: v2 appVersion: v0.0.19 - created: "2025-11-26T09:01:23.702037+02:00" + created: "2026-01-12T15:21:52.913455+02:00" description: A Helm chart to install Vector Operator digest: bc1acd8b21a95e373702daa9c4ce4226b28f56b9c9299482d47b200baddbec14 home: https://github.com/kaasops/vector-operator @@ -445,7 +458,7 @@ entries: version: 0.0.19 - apiVersion: v2 appVersion: v0.0.18 - created: "2025-11-26T09:01:23.701367+02:00" + created: "2026-01-12T15:21:52.913017+02:00" description: A Helm chart to install Vector Operator digest: 2bf9cde6eec7b00bfc70d7ac79b1e9d4bf3a406749c6b2bd816f20efd0cb44c3 home: https://github.com/kaasops/vector-operator @@ -458,7 +471,7 @@ entries: version: 0.0.18 - apiVersion: v2 appVersion: v0.0.17 - created: "2025-11-26T09:01:23.700943+02:00" + created: "2026-01-12T15:21:52.912575+02:00" description: A Helm chart to install Vector Operator digest: edb51a059b9231f9bc2e2e0dd82c432d0e799a6767a7829ee113054478e098ed home: https://github.com/kaasops/vector-operator @@ -471,7 +484,7 @@ entries: version: 0.0.17 - apiVersion: v2 appVersion: v0.0.16 - created: "2025-11-26T09:01:23.700521+02:00" + created: "2026-01-12T15:21:52.912097+02:00" description: A Helm chart to install Vector Operator digest: 06e33602d72c44cf6779152df4936133ed87e228dd71cbb6615aa4c2666a1ee1 home: https://github.com/kaasops/vector-operator @@ -484,7 +497,7 @@ entries: version: 0.0.16 - apiVersion: v2 appVersion: v0.0.15 - created: "2025-11-26T09:01:23.700061+02:00" + created: "2026-01-12T15:21:52.911604+02:00" description: A Helm chart to install Vector Operator digest: 6c9f5ba7a914329caa4f93342d3415fcf4e5fe39f5b7db69173896ea13a47c5b home: https://github.com/kaasops/vector-operator @@ -497,7 +510,7 @@ entries: version: 0.0.15 - apiVersion: v2 appVersion: v0.0.14 - created: "2025-11-26T09:01:23.699601+02:00" + created: "2026-01-12T15:21:52.910662+02:00" description: A Helm chart to install Vector Operator digest: 9f7a3b66247dea7f826b2b38202b0ddfa72b30ecc0954d75be36e066deda9df9 home: https://github.com/kaasops/vector-operator @@ -510,7 +523,7 @@ entries: version: 0.0.14 - apiVersion: v2 appVersion: v0.0.13 - created: "2025-11-26T09:01:23.699134+02:00" + created: "2026-01-12T15:21:52.91013+02:00" description: A Helm chart to install Vector Operator digest: c88a1866a20fb2aea4a23886e6e60080eba9ae7ef2706f492d9b329dc9ddf49b home: https://github.com/kaasops/vector-operator @@ -523,7 +536,7 @@ entries: version: 0.0.13 - apiVersion: v2 appVersion: v0.0.12 - created: "2025-11-26T09:01:23.697504+02:00" + created: "2026-01-12T15:21:52.909546+02:00" description: A Helm chart to install Vector Operator digest: 384e8fd8f8f743036eaf1415d893158256a2ad9daddcb17a3d0701a528d9f0df home: https://github.com/kaasops/vector-operator @@ -536,7 +549,7 @@ entries: version: 0.0.12 - apiVersion: v2 appVersion: v0.0.11 - created: "2025-11-26T09:01:23.696883+02:00" + created: "2026-01-12T15:21:52.908883+02:00" description: A Helm chart to install Vector Operator digest: 29e1e04c1706b88ef61ed6c91a45847e6069843419515a33046c5929b179e273 home: https://github.com/kaasops/vector-operator @@ -549,7 +562,7 @@ entries: version: 0.0.11 - apiVersion: v2 appVersion: v0.0.10 - created: "2025-11-26T09:01:23.696351+02:00" + created: "2026-01-12T15:21:52.908194+02:00" description: A Helm chart to install Vector Operator digest: f4398224ce88b852b319c950d0f39bfd5e6181801c1fac1b42b069dd2d358078 home: https://github.com/kaasops/vector-operator @@ -562,7 +575,7 @@ entries: version: 0.0.10 - apiVersion: v2 appVersion: v0.0.9 - created: "2025-11-26T09:01:23.720735+02:00" + created: "2026-01-12T15:21:52.933834+02:00" description: A Helm chart to install Vector Operator digest: 66c528b6daa9f6fb9a8dd91895b69151f3f0183f4685ba4a2bc026fac27f25a7 home: https://github.com/kaasops/vector-operator @@ -575,7 +588,7 @@ entries: version: 0.0.9 - apiVersion: v2 appVersion: v0.0.8 - created: "2025-11-26T09:01:23.720404+02:00" + created: "2026-01-12T15:21:52.933495+02:00" description: A Helm chart to install Vector Operator digest: 21c4c214cd0206abb743e82ac757804d644de08d80eb5f2edbb82ff9668cfed3 home: https://github.com/kaasops/vector-operator @@ -588,7 +601,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2025-11-26T09:01:23.720082+02:00" + created: "2026-01-12T15:21:52.933132+02:00" description: A Helm chart to install Vector Operator digest: 27915a2bf70da3f66d08cf4a1f6c41ad38937759ad52eaf8b19f5a3e348e2f2e home: https://github.com/kaasops/vector-operator @@ -601,7 +614,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2025-11-26T09:01:23.719663+02:00" + created: "2026-01-12T15:21:52.932612+02:00" description: A Helm chart to install Vector Operator digest: 26760fbc2018336c12e8726307a624970ee994c4ffa021cc216c13669bd82f09 home: https://github.com/kaasops/vector-operator @@ -614,7 +627,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2025-11-26T09:01:23.695752+02:00" + created: "2026-01-12T15:21:52.907703+02:00" description: A Helm chart to install Vector Operator digest: 1d6034027ae2f08a9dbea4d6ee9a1604117ae44d9daceb3f654b87a99175251f home: https://github.com/kaasops/vector-operator @@ -625,4 +638,4 @@ entries: urls: - https://kaasops.github.io/vector-operator/helm/packages/vector-operator-0.0.1.tgz version: 0.0.1 -generated: "2025-11-26T09:01:23.694663+02:00" +generated: "2026-01-12T15:21:52.906678+02:00" diff --git a/helm/packages/vector-operator-0.8.1.tgz b/helm/packages/vector-operator-0.8.1.tgz new file mode 100644 index 0000000000000000000000000000000000000000..a9993b3e9e30c86817f25a6944d578bd83960431 GIT binary patch literal 103164 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�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�dUZGlH&&~H+@$L<_{*efyTf@>(&>mMYq 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 CTfwLR literal 0 HcmV?d00001 From 2c1a34ebdade626799fd0f65382a7e520c2451bc Mon Sep 17 00:00:00 2001 From: "nikita.tikhonov" Date: Sun, 25 Jan 2026 18:11:59 +0300 Subject: [PATCH 316/316] add extra args support --- api/v1alpha1/vector_common_types.go | 1 + api/v1alpha1/zz_generated.deepcopy.go | 5 +++++ .../observability.kaasops.io_clustervectoraggregators.yaml | 4 ++++ .../bases/observability.kaasops.io_vectoraggregators.yaml | 4 ++++ config/crd/bases/observability.kaasops.io_vectors.yaml | 4 ++++ helm/charts/vector-operator/values.yaml | 3 +++ internal/vector/vectoragent/vectoragent_daemonset.go | 2 +- 7 files changed, 22 insertions(+), 1 deletion(-) diff --git a/api/v1alpha1/vector_common_types.go b/api/v1alpha1/vector_common_types.go index 712b021c..d47c3a66 100644 --- a/api/v1alpha1/vector_common_types.go +++ b/api/v1alpha1/vector_common_types.go @@ -112,6 +112,7 @@ type VectorCommon struct { CompressConfigFile bool `json:"compressConfigFile,omitempty"` ConfigReloaderImage string `json:"configReloaderImage,omitempty"` ConfigReloaderResources v1.ResourceRequirements `json:"configReloaderResources,omitempty"` + Args []string `json:"args,omitempty"` } // ApiSpec is the Schema for the Vector Agent GraphQL API - https://vector.dev/docs/reference/api/ diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 88f3346a..be22a3ab 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -519,6 +519,11 @@ func (in *VectorCommon) DeepCopyInto(out *VectorCommon) { } in.ConfigCheck.DeepCopyInto(&out.ConfigCheck) in.ConfigReloaderResources.DeepCopyInto(&out.ConfigReloaderResources) + if in.Args != nil { + in, out := &in.Args, &out.Args + *out = make([]string, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VectorCommon. diff --git a/config/crd/bases/observability.kaasops.io_clustervectoraggregators.yaml b/config/crd/bases/observability.kaasops.io_clustervectoraggregators.yaml index ab6fa3d0..bce1d1d2 100644 --- a/config/crd/bases/observability.kaasops.io_clustervectoraggregators.yaml +++ b/config/crd/bases/observability.kaasops.io_clustervectoraggregators.yaml @@ -990,6 +990,10 @@ spec: playground: type: boolean type: object + args: + items: + type: string + type: array compressConfigFile: description: 'Compress config file to fix: metadata.annotations: Too long: must have at most 262144 characters' diff --git a/config/crd/bases/observability.kaasops.io_vectoraggregators.yaml b/config/crd/bases/observability.kaasops.io_vectoraggregators.yaml index d795466e..b9f8fe15 100644 --- a/config/crd/bases/observability.kaasops.io_vectoraggregators.yaml +++ b/config/crd/bases/observability.kaasops.io_vectoraggregators.yaml @@ -988,6 +988,10 @@ spec: playground: type: boolean type: object + args: + items: + type: string + type: array compressConfigFile: description: 'Compress config file to fix: metadata.annotations: Too long: must have at most 262144 characters' diff --git a/config/crd/bases/observability.kaasops.io_vectors.yaml b/config/crd/bases/observability.kaasops.io_vectors.yaml index ddb3cce7..20015ed8 100644 --- a/config/crd/bases/observability.kaasops.io_vectors.yaml +++ b/config/crd/bases/observability.kaasops.io_vectors.yaml @@ -998,6 +998,10 @@ spec: playground: type: boolean type: object + args: + items: + type: string + type: array compressConfigFile: description: 'Compress config file to fix: metadata.annotations: Too long: must have at most 262144 characters' diff --git a/helm/charts/vector-operator/values.yaml b/helm/charts/vector-operator/values.yaml index e07a2d37..d66ba94b 100644 --- a/helm/charts/vector-operator/values.yaml +++ b/helm/charts/vector-operator/values.yaml @@ -82,6 +82,9 @@ vector: # env: # - name: "testenv" # value: "testvalues" + # args: + # --allocation-tracing + secrets: {} # - name: elastic-creds diff --git a/internal/vector/vectoragent/vectoragent_daemonset.go b/internal/vector/vectoragent/vectoragent_daemonset.go index 33c318ea..2d81c539 100644 --- a/internal/vector/vectoragent/vectoragent_daemonset.go +++ b/internal/vector/vectoragent/vectoragent_daemonset.go @@ -239,7 +239,7 @@ func (ctrl *Controller) VectorAgentContainer() *corev1.Container { container := &corev1.Container{ Name: ctrl.getNameVectorAgent(), Image: ctrl.Vector.Spec.Agent.Image, - Args: []string{"--config-dir", "/etc/vector", "--watch-config"}, + Args: append([]string{"--config-dir", "/etc/vector", "--watch-config"}, ctrl.Vector.Spec.Agent.Args...), Env: ctrl.generateVectorAgentEnvs(), Ports: []corev1.ContainerPort{ {

agentagent image Image for Vector agent. timberio/vector:0.48.0-distroless-libc by default
api ApiSpec
internalMetricsEnable internal metrics exporter. When enabled, a PodMonitor resource is created for Prometheus scraping. By default - false
scrapeIntervalInterval at which Prometheus should scrape metrics from the internal metrics exporter. Examples: "30s", "1m", "5m". Only used when internalMetrics is true. If not specified, Prometheus default is used.
scrapeTimeoutTimeout for scraping metrics. Must be less than scrapeInterval. Examples: "10s", "30s". Only used when internalMetrics is true. If not specified, Prometheus default is used.
service Temporary field for enabling service for Vector DaemonSet. By default - false

Go Report Card + + + telegram link +