diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 15251b657..9eefbcab5 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,6 +1,6 @@ { "name": "istio build-tools", - "image": "gcr.io/istio-testing/build-tools:master-4759bf88d40172234fc6a0b9e11a4c5f1ea58a90", + "image": "gcr.io/istio-testing/build-tools:master-12939d7be6baee95d63b1a9d7c4e194f1b241257", "privileged": true, "remoteEnv": { "USE_GKE_GCLOUD_AUTH_PLUGIN": "True", diff --git a/.github/workflows/update-deps.yaml b/.github/workflows/update-deps.yaml index 335c3f7a9..ff4e1c883 100644 --- a/.github/workflows/update-deps.yaml +++ b/.github/workflows/update-deps.yaml @@ -23,7 +23,7 @@ jobs: update-deps: runs-on: ubuntu-latest container: - image: gcr.io/istio-testing/build-tools:master-4759bf88d40172234fc6a0b9e11a4c5f1ea58a90 + image: gcr.io/istio-testing/build-tools:master-12939d7be6baee95d63b1a9d7c4e194f1b241257 options: --entrypoint '' steps: diff --git a/Makefile.core.mk b/Makefile.core.mk index 24cc1bd1b..fc5e3922d 100644 --- a/Makefile.core.mk +++ b/Makefile.core.mk @@ -468,12 +468,12 @@ OPM ?= $(LOCALBIN)/opm ISTIOCTL ?= $(LOCALBIN)/istioctl ## Tool Versions -OPERATOR_SDK_VERSION ?= v1.37.0 -HELM_VERSION ?= v3.16.2 -CONTROLLER_TOOLS_VERSION ?= v0.16.4 -OPM_VERSION ?= v1.47.0 -OLM_VERSION ?= 0.28.0 -GITLEAKS_VERSION ?= v8.21.1 +OPERATOR_SDK_VERSION ?= v1.38.0 +HELM_VERSION ?= v3.16.3 +CONTROLLER_TOOLS_VERSION ?= v0.16.5 +OPM_VERSION ?= v1.48.0 +OLM_VERSION ?= v0.30.0 +GITLEAKS_VERSION ?= v8.21.2 ISTIOCTL_VERSION ?= 1.23.0 # GENERATE_RELATED_IMAGES defines whether `spec.relatedImages` is going to be generated or not diff --git a/PROJECT b/PROJECT index 4a9317002..cda1498f7 100644 --- a/PROJECT +++ b/PROJECT @@ -24,7 +24,7 @@ resources: namespaced: false controller: true domain: sailoperator.io - kind: RemoteIstio + kind: IstioRevision path: github.com/istio-ecosystem/sail-operator/api/v1alpha1 version: v1alpha1 - api: @@ -32,7 +32,7 @@ resources: namespaced: false controller: true domain: sailoperator.io - kind: IstioRevision + kind: IstioRevisionTag path: github.com/istio-ecosystem/sail-operator/api/v1alpha1 version: v1alpha1 - api: diff --git a/api/v1alpha1/istio_types.go b/api/v1alpha1/istio_types.go index 0b9715a5f..f20e027a7 100644 --- a/api/v1alpha1/istio_types.go +++ b/api/v1alpha1/istio_types.go @@ -51,10 +51,10 @@ type IstioSpec struct { // +sail:profile // The built-in installation configuration profile to use. // The 'default' profile is always applied. On OpenShift, the 'openshift' profile is also applied on top of 'default'. - // Must be one of: ambient, default, demo, empty, openshift-ambient, openshift, preview, stable. + // Must be one of: ambient, default, demo, empty, openshift-ambient, openshift, preview, remote, stable. // +++PROFILES-DROPDOWN-HIDDEN-UNTIL-WE-FULLY-IMPLEMENT-THEM+++operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Profile",xDescriptors={"urn:alm:descriptor:com.tectonic.ui:fieldGroup:General", "urn:alm:descriptor:com.tectonic.ui:select:ambient", "urn:alm:descriptor:com.tectonic.ui:select:default", "urn:alm:descriptor:com.tectonic.ui:select:demo", "urn:alm:descriptor:com.tectonic.ui:select:empty", "urn:alm:descriptor:com.tectonic.ui:select:external", "urn:alm:descriptor:com.tectonic.ui:select:minimal", "urn:alm:descriptor:com.tectonic.ui:select:preview", "urn:alm:descriptor:com.tectonic.ui:select:remote"} // +operator-sdk:csv:customresourcedefinitions:type=spec,xDescriptors={"urn:alm:descriptor:com.tectonic.ui:hidden"} - // +kubebuilder:validation:Enum=ambient;default;demo;empty;openshift-ambient;openshift;preview;stable + // +kubebuilder:validation:Enum=ambient;default;demo;empty;openshift-ambient;openshift;preview;remote;stable Profile string `json:"profile,omitempty"` // Namespace to which the Istio components should be installed. Note that this field is immutable. @@ -227,6 +227,9 @@ const ( // IstioReasonIstiodNotReady indicates that the control plane is fully reconciled, but istiod is not ready. IstioReasonIstiodNotReady IstioConditionReason = "IstiodNotReady" + // IstioReasonRemoteIstiodNotReady indicates that the control plane is fully reconciled, but the remote istiod is not ready. + IstioReasonRemoteIstiodNotReady IstioConditionReason = "RemoteIstiodNotReady" + // IstioReasonReadinessCheckFailed indicates that readiness could not be ascertained. IstioReasonReadinessCheckFailed IstioConditionReason = "ReadinessCheckFailed" ) diff --git a/api/v1alpha1/istiocni_types.go b/api/v1alpha1/istiocni_types.go index aa56044a1..5a9c5d754 100644 --- a/api/v1alpha1/istiocni_types.go +++ b/api/v1alpha1/istiocni_types.go @@ -37,10 +37,10 @@ type IstioCNISpec struct { // +sail:profile // The built-in installation configuration profile to use. // The 'default' profile is always applied. On OpenShift, the 'openshift' profile is also applied on top of 'default'. - // Must be one of: ambient, default, demo, empty, openshift-ambient, openshift, preview, stable. + // Must be one of: ambient, default, demo, empty, openshift-ambient, openshift, preview, remote, stable. // +++PROFILES-DROPDOWN-HIDDEN-UNTIL-WE-FULLY-IMPLEMENT-THEM+++operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Profile",xDescriptors={"urn:alm:descriptor:com.tectonic.ui:fieldGroup:General", "urn:alm:descriptor:com.tectonic.ui:select:ambient", "urn:alm:descriptor:com.tectonic.ui:select:default", "urn:alm:descriptor:com.tectonic.ui:select:demo", "urn:alm:descriptor:com.tectonic.ui:select:empty", "urn:alm:descriptor:com.tectonic.ui:select:external", "urn:alm:descriptor:com.tectonic.ui:select:minimal", "urn:alm:descriptor:com.tectonic.ui:select:preview", "urn:alm:descriptor:com.tectonic.ui:select:remote"} // +operator-sdk:csv:customresourcedefinitions:type=spec,xDescriptors={"urn:alm:descriptor:com.tectonic.ui:hidden"} - // +kubebuilder:validation:Enum=ambient;default;demo;empty;openshift-ambient;openshift;preview;stable + // +kubebuilder:validation:Enum=ambient;default;demo;empty;openshift-ambient;openshift;preview;remote;stable Profile string `json:"profile,omitempty"` // Namespace to which the Istio CNI component should be installed. diff --git a/api/v1alpha1/istiorevision_types.go b/api/v1alpha1/istiorevision_types.go index 675af9450..b3b103dfe 100644 --- a/api/v1alpha1/istiorevision_types.go +++ b/api/v1alpha1/istiorevision_types.go @@ -28,11 +28,6 @@ const ( // IstioRevisionSpec defines the desired state of IstioRevision // +kubebuilder:validation:XValidation:rule="self.values.global.istioNamespace == self.__namespace__",message="spec.values.global.istioNamespace must match spec.namespace" type IstioRevisionSpec struct { - // Type indicates whether this revision represents a local or a remote control plane installation. - // +kubebuilder:default=Local - // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" - Type IstioRevisionType `json:"type"` - // +sail:version // Defines the version of Istio to install. // Must be one of: v1.23.2. @@ -178,16 +173,6 @@ const ( IstioRevisionReasonHealthy IstioRevisionConditionReason = "Healthy" ) -type IstioRevisionType string - -const ( - // IstioRevisionTypeLocal indicates that the revision represents a local control plane installation. - IstioRevisionTypeLocal IstioRevisionType = "Local" - - // IstioRevisionTypeRemote indicates that the revision represents a remote control plane installation. - IstioRevisionTypeRemote IstioRevisionType = "Remote" -) - // +kubebuilder:object:root=true // +kubebuilder:resource:scope=Cluster,shortName=istiorev,categories=istio-io // +kubebuilder:subresource:status diff --git a/api/v1alpha1/istiorevisiontags_types.go b/api/v1alpha1/istiorevisiontags_types.go new file mode 100644 index 000000000..8d2788564 --- /dev/null +++ b/api/v1alpha1/istiorevisiontags_types.go @@ -0,0 +1,204 @@ +// Copyright Istio Authors +// +// 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 ( + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + IstioRevisionTagKind = "IstioRevisionTag" + DefaultRevisionTag = "default" +) + +// IstioRevisionTagSpec defines the desired state of IstioRevisionTag +type IstioRevisionTagSpec struct { + // +kubebuilder:validation:Required + TargetRef IstioRevisionTagTargetReference `json:"targetRef"` +} + +// IstioRevisionTagTargetReference can reference either Istio or IstioRevision objects in the cluster. +type IstioRevisionTagTargetReference struct { + // Kind is the kind of the target resource. + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + // +kubebuilder:validation:Required + Kind string `json:"kind"` + + // Name is the name of the target resource. + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + // +kubebuilder:validation:Required + Name string `json:"name"` +} + +// IstioRevisionStatus defines the observed state of IstioRevision +type IstioRevisionTagStatus struct { + // ObservedGeneration is the most recent generation observed for this + // IstioRevisionTag object. It corresponds to the object's generation, which is + // updated on mutation by the API Server. The information in the status + // pertains to this particular generation of the object. + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + + // Represents the latest available observations of the object's current state. + Conditions []IstioRevisionTagCondition `json:"conditions,omitempty"` + + // Reports the current state of the object. + State IstioRevisionTagConditionReason `json:"state,omitempty"` + + // IstiodNamespace stores the namespace of the corresponding Istiod instance + IstiodNamespace string `json:"istiodNamespace"` + + // IstioRevision stores the name of the referenced IstioRevision + IstioRevision string `json:"istioRevision"` +} + +// GetCondition returns the condition of the specified type +func (s *IstioRevisionTagStatus) GetCondition(conditionType IstioRevisionTagConditionType) IstioRevisionTagCondition { + if s != nil { + for i := range s.Conditions { + if s.Conditions[i].Type == conditionType { + return s.Conditions[i] + } + } + } + return IstioRevisionTagCondition{Type: conditionType, Status: metav1.ConditionUnknown} +} + +// SetCondition sets a specific condition in the list of conditions +func (s *IstioRevisionTagStatus) SetCondition(condition IstioRevisionTagCondition) { + var now time.Time + if testTime == nil { + now = time.Now() + } else { + now = *testTime + } + + // The lastTransitionTime only gets serialized out to the second. This can + // break update skipping, as the time in the resource returned from the client + // may not match the time in our cached status during a reconcile. We truncate + // here to save any problems down the line. + lastTransitionTime := metav1.NewTime(now.Truncate(time.Second)) + + for i, prevCondition := range s.Conditions { + if prevCondition.Type == condition.Type { + if prevCondition.Status != condition.Status { + condition.LastTransitionTime = lastTransitionTime + } else { + condition.LastTransitionTime = prevCondition.LastTransitionTime + } + s.Conditions[i] = condition + return + } + } + + // If the condition does not exist, initialize the lastTransitionTime + condition.LastTransitionTime = lastTransitionTime + s.Conditions = append(s.Conditions, condition) +} + +// IstioRevisionCondition represents a specific observation of the IstioRevision object's state. +type IstioRevisionTagCondition struct { + // The type of this condition. + Type IstioRevisionTagConditionType `json:"type,omitempty"` + + // The status of this condition. Can be True, False or Unknown. + Status metav1.ConditionStatus `json:"status,omitempty"` + + // Unique, single-word, CamelCase reason for the condition's last transition. + Reason IstioRevisionTagConditionReason `json:"reason,omitempty"` + + // Human-readable message indicating details about the last transition. + Message string `json:"message,omitempty"` + + // Last time the condition transitioned from one status to another. + LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"` +} + +// IstioRevisionConditionType represents the type of the condition. Condition stages are: +// Installed, Reconciled, Ready +type IstioRevisionTagConditionType string + +// IstioRevisionConditionReason represents a short message indicating how the condition came +// to be in its present state. +type IstioRevisionTagConditionReason string + +const ( + // IstioRevisionConditionReconciled signifies whether the controller has + // successfully reconciled the resources defined through the CR. + IstioRevisionTagConditionReconciled IstioRevisionTagConditionType = "Reconciled" + + // IstioRevisionTagNameAlreadyExists indicates that the a revision with the same name as the IstioRevisionTag already exists. + IstioRevisionTagReasonNameAlreadyExists IstioRevisionTagConditionReason = "NameAlreadyExists" + + // IstioRevisionTagReasonReferenceNotFound indicates that the resource referenced by the tag's TargetRef was not found + IstioRevisionTagReasonReferenceNotFound IstioRevisionTagConditionReason = "RefNotFound" + + // IstioRevisionReasonReconcileError indicates that the reconciliation of the resource has failed, but will be retried. + IstioRevisionTagReasonReconcileError IstioRevisionTagConditionReason = "ReconcileError" +) + +const ( + // IstioRevisionConditionInUse signifies whether any workload is configured to use the revision. + IstioRevisionTagConditionInUse IstioRevisionTagConditionType = "InUse" + + // IstioRevisionReasonReferencedByWorkloads indicates that the revision is referenced by at least one pod or namespace. + IstioRevisionTagReasonReferencedByWorkloads IstioRevisionTagConditionReason = "ReferencedByWorkloads" + + // IstioRevisionReasonNotReferenced indicates that the revision is not referenced by any pod or namespace. + IstioRevisionTagReasonNotReferenced IstioRevisionTagConditionReason = "NotReferencedByAnything" + + // IstioRevisionReasonUsageCheckFailed indicates that the operator could not check whether any workloads use the revision. + IstioRevisionTagReasonUsageCheckFailed IstioRevisionTagConditionReason = "UsageCheckFailed" +) + +const ( + // IstioRevisionTagReasonHealthy indicates that the revision tag has been successfully reconciled and is in use. + IstioRevisionTagReasonHealthy IstioRevisionTagConditionReason = "Healthy" +) + +// +kubebuilder:object:root=true +// +kubebuilder:resource:scope=Cluster,shortName=istiorevtag,categories=istio-io +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.state",description="The current state of this object." +// +kubebuilder:printcolumn:name="In use",type="string",JSONPath=".status.conditions[?(@.type==\"InUse\")].status",description="Whether the tag is being used by workloads." +// +kubebuilder:printcolumn:name="Revision",type="string",JSONPath=".status.istioRevision",description="The IstioRevision this object is referencing." +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="The age of the object" + +// IstioRevisionTag references a Istio or IstioRevision object and serves as an alias for sidecar injection. +type IstioRevisionTag struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec IstioRevisionTagSpec `json:"spec,omitempty"` + Status IstioRevisionTagStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// IstioRevisionList contains a list of IstioRevision +type IstioRevisionTagList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []IstioRevisionTag `json:"items"` +} + +func init() { + SchemeBuilder.Register(&IstioRevisionTag{}, &IstioRevisionTagList{}) +} diff --git a/api/v1alpha1/values_types.gen.go b/api/v1alpha1/values_types.gen.go index c2eea6744..86776216d 100644 --- a/api/v1alpha1/values_types.gen.go +++ b/api/v1alpha1/values_types.gen.go @@ -932,9 +932,7 @@ type Values struct { // // Deprecated: Marked as deprecated in pkg/apis/values_types.proto. IstiodRemote *IstiodRemoteConfig `json:"istiodRemote,omitempty"` - // Specifies the aliases for the Istio control plane revision. A MutatingWebhookConfiguration - // is created for each alias. - RevisionTags []string `json:"revisionTags,omitempty"` + // The name of the default revision in the cluster. DefaultRevision *string `json:"defaultRevision,omitempty"` // Specifies which installation configuration profile to apply. @@ -1671,6 +1669,7 @@ type MeshConfigExtensionProvider struct { // Configures a Datadog tracing provider. Datadog *MeshConfigExtensionProviderDatadogTracingProvider `json:"datadog,omitempty"` + // +hidefromdoc // Configures a Stackdriver provider. Stackdriver *MeshConfigExtensionProviderStackdriverProvider `json:"stackdriver,omitempty"` @@ -1811,6 +1810,10 @@ type MeshConfigExtensionProviderEnvoyExternalAuthorizationHttpProvider struct { // or if the authorization service has returned a HTTP 5xx error. // Default is false and the request will be rejected with "Forbidden" response. FailOpen *bool `json:"failOpen,omitempty"` + // If true, clears route cache in order to allow the external authorization service to correctly affect routing decisions. + // If true, recalculate routes with the new ExtAuthZ added/removed headers. + // Default is false + ClearRouteCache *bool `json:"clearRouteCache,omitempty"` // Sets the HTTP status that is returned to the client when there is a network error to the authorization service. // The default status is "403" (HTTP Forbidden). StatusOnError *string `json:"statusOnError,omitempty"` @@ -1899,6 +1902,10 @@ type MeshConfigExtensionProviderEnvoyExternalAuthorizationGrpcProvider struct { // or if the authorization service has returned a HTTP 5xx error. // Default is false. For HTTP request, it will be rejected with 403 (HTTP Forbidden). For TCP connection, it will be closed immediately. FailOpen *bool `json:"failOpen,omitempty"` + // If true, clears route cache in order to allow the external authorization service to correctly affect routing decisions. + // If true, recalculate routes with the new ExtAuthZ added/removed headers. + // Default is false + ClearRouteCache *bool `json:"clearRouteCache,omitempty"` // Sets the HTTP status that is returned to the client when there is a network error to the authorization service. // The default status is "403" (HTTP Forbidden). StatusOnError *string `json:"statusOnError,omitempty"` @@ -2334,7 +2341,7 @@ type MeshConfigExtensionProviderGrpcService struct { // Optional. Specifies the timeout for the GRPC request. Timeout *metav1.Duration `json:"timeout,omitempty"` // Optional. Additional metadata to include in streams initiated to the GrpcService. This can be used for - // scenarios in which additional ad hoc authorization headers (e.g. “x-foo-bar: baz-key“) are to + // scenarios in which additional ad hoc authorization headers (e.g. "x-foo-bar: baz-key") are to // be injected. InitialMetadata []*MeshConfigExtensionProviderHttpHeader `json:"initialMetadata,omitempty"` } @@ -2686,15 +2693,18 @@ type Tracing struct { // Use a Lightstep tracer. // NOTE: For Istio 1.15+, this configuration option will result // in using OpenTelemetry-based Lightstep integration. + // +hidefromdoc Lightstep *TracingLightstep `json:"lightstep,omitempty"` // Use a Datadog tracer. Datadog *TracingDatadog `json:"datadog,omitempty"` // Use a Stackdriver tracer. + // +hidefromdoc Stackdriver *TracingStackdriver `json:"stackdriver,omitempty"` // Use an OpenCensus tracer exporting to an OpenCensus agent. + // +hidefromdoc OpenCensusAgent *TracingOpenCensusAgent `json:"openCensusAgent,omitempty"` // Configures the custom tags to be added to active span by all proxies (i.e. sidecars // and gateways). // The key represents the name of the tag. @@ -2722,6 +2732,9 @@ type Tracing struct { // uses Istio mutual TLS and shares the root CA with Pilot, specify the TLS // mode as `ISTIO_MUTUAL`. TlsSettings *ClientTLSSettings `json:"tlsSettings,omitempty"` + // Determines whether or not trace spans generated by Envoy will include Istio specific tags. + // By default Istio specific tags are included in the trace spans. + EnableIstioTags *bool `json:"enableIstioTags,omitempty"` } // SDS defines secret discovery service(SDS) configuration to be used by the proxy. diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 6587fcd95..5cefb3402 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -1209,6 +1209,134 @@ func (in *IstioRevisionStatus) DeepCopy() *IstioRevisionStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IstioRevisionTag) DeepCopyInto(out *IstioRevisionTag) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IstioRevisionTag. +func (in *IstioRevisionTag) DeepCopy() *IstioRevisionTag { + if in == nil { + return nil + } + out := new(IstioRevisionTag) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *IstioRevisionTag) 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 *IstioRevisionTagCondition) DeepCopyInto(out *IstioRevisionTagCondition) { + *out = *in + in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IstioRevisionTagCondition. +func (in *IstioRevisionTagCondition) DeepCopy() *IstioRevisionTagCondition { + if in == nil { + return nil + } + out := new(IstioRevisionTagCondition) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IstioRevisionTagList) DeepCopyInto(out *IstioRevisionTagList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]IstioRevisionTag, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IstioRevisionTagList. +func (in *IstioRevisionTagList) DeepCopy() *IstioRevisionTagList { + if in == nil { + return nil + } + out := new(IstioRevisionTagList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *IstioRevisionTagList) 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 *IstioRevisionTagSpec) DeepCopyInto(out *IstioRevisionTagSpec) { + *out = *in + out.TargetRef = in.TargetRef +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IstioRevisionTagSpec. +func (in *IstioRevisionTagSpec) DeepCopy() *IstioRevisionTagSpec { + if in == nil { + return nil + } + out := new(IstioRevisionTagSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IstioRevisionTagStatus) DeepCopyInto(out *IstioRevisionTagStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]IstioRevisionTagCondition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IstioRevisionTagStatus. +func (in *IstioRevisionTagStatus) DeepCopy() *IstioRevisionTagStatus { + if in == nil { + return nil + } + out := new(IstioRevisionTagStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IstioRevisionTagTargetReference) DeepCopyInto(out *IstioRevisionTagTargetReference) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IstioRevisionTagTargetReference. +func (in *IstioRevisionTagTargetReference) DeepCopy() *IstioRevisionTagTargetReference { + if in == nil { + return nil + } + out := new(IstioRevisionTagTargetReference) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *IstioSpec) DeepCopyInto(out *IstioSpec) { *out = *in @@ -1935,6 +2063,11 @@ func (in *MeshConfigExtensionProviderEnvoyExternalAuthorizationGrpcProvider) Dee *out = new(bool) **out = **in } + if in.ClearRouteCache != nil { + in, out := &in.ClearRouteCache, &out.ClearRouteCache + *out = new(bool) + **out = **in + } if in.StatusOnError != nil { in, out := &in.StatusOnError, &out.StatusOnError *out = new(string) @@ -1985,6 +2118,11 @@ func (in *MeshConfigExtensionProviderEnvoyExternalAuthorizationHttpProvider) Dee *out = new(bool) **out = **in } + if in.ClearRouteCache != nil { + in, out := &in.ClearRouteCache, &out.ClearRouteCache + *out = new(bool) + **out = **in + } if in.StatusOnError != nil { in, out := &in.StatusOnError, &out.StatusOnError *out = new(string) @@ -4831,6 +4969,11 @@ func (in *Tracing) DeepCopyInto(out *Tracing) { *out = new(ClientTLSSettings) (*in).DeepCopyInto(*out) } + if in.EnableIstioTags != nil { + in, out := &in.EnableIstioTags, &out.EnableIstioTags + *out = new(bool) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Tracing. @@ -5111,11 +5254,6 @@ func (in *Values) DeepCopyInto(out *Values) { *out = new(IstiodRemoteConfig) (*in).DeepCopyInto(*out) } - if in.RevisionTags != nil { - in, out := &in.RevisionTags, &out.RevisionTags - *out = make([]string, len(*in)) - copy(*out, *in) - } if in.DefaultRevision != nil { in, out := &in.DefaultRevision, &out.DefaultRevision *out = new(string) diff --git a/bundle.Dockerfile b/bundle.Dockerfile index 88d1b7d50..1b1540d5a 100644 --- a/bundle.Dockerfile +++ b/bundle.Dockerfile @@ -6,7 +6,7 @@ LABEL operators.operatorframework.io.bundle.manifests.v1=manifests/ LABEL operators.operatorframework.io.bundle.metadata.v1=metadata/ LABEL operators.operatorframework.io.bundle.package.v1=servicemeshoperator3 LABEL operators.operatorframework.io.bundle.channels.v1="candidates" -LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.37.0 +LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.38.0 LABEL operators.operatorframework.io.metrics.mediatype.v1=metrics+v1 LABEL operators.operatorframework.io.metrics.project_layout=go.kubebuilder.io/v4 diff --git a/bundle/manifests/extensions.istio.io_wasmplugins.yaml b/bundle/manifests/extensions.istio.io_wasmplugins.yaml index 22803cb14..98dec3286 100644 --- a/bundle/manifests/extensions.istio.io_wasmplugins.yaml +++ b/bundle/manifests/extensions.istio.io_wasmplugins.yaml @@ -305,6 +305,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -321,8 +327,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. diff --git a/bundle/manifests/networking.istio.io_destinationrules.yaml b/bundle/manifests/networking.istio.io_destinationrules.yaml index 998f51d54..92a9d4be0 100644 --- a/bundle/manifests/networking.istio.io_destinationrules.yaml +++ b/bundle/manifests/networking.istio.io_destinationrules.yaml @@ -1884,6 +1884,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -1900,8 +1906,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. @@ -3801,6 +3805,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -3817,8 +3827,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. @@ -5718,6 +5726,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -5734,8 +5748,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. diff --git a/bundle/manifests/networking.istio.io_envoyfilters.yaml b/bundle/manifests/networking.istio.io_envoyfilters.yaml index f2181cfc6..af202fd69 100644 --- a/bundle/manifests/networking.istio.io_envoyfilters.yaml +++ b/bundle/manifests/networking.istio.io_envoyfilters.yaml @@ -345,6 +345,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -361,8 +367,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. diff --git a/bundle/manifests/networking.istio.io_gateways.yaml b/bundle/manifests/networking.istio.io_gateways.yaml index c6307b2ed..c4d5dbd8a 100644 --- a/bundle/manifests/networking.istio.io_gateways.yaml +++ b/bundle/manifests/networking.istio.io_gateways.yaml @@ -196,6 +196,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -212,8 +218,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. @@ -425,6 +429,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -441,8 +451,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. @@ -654,6 +662,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -670,8 +684,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. diff --git a/bundle/manifests/networking.istio.io_proxyconfigs.yaml b/bundle/manifests/networking.istio.io_proxyconfigs.yaml index a9a4ab728..a5b7a373b 100644 --- a/bundle/manifests/networking.istio.io_proxyconfigs.yaml +++ b/bundle/manifests/networking.istio.io_proxyconfigs.yaml @@ -89,6 +89,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -105,8 +111,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. diff --git a/bundle/manifests/networking.istio.io_serviceentries.yaml b/bundle/manifests/networking.istio.io_serviceentries.yaml index dab356fed..2f35395b6 100644 --- a/bundle/manifests/networking.istio.io_serviceentries.yaml +++ b/bundle/manifests/networking.istio.io_serviceentries.yaml @@ -258,6 +258,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -274,8 +280,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. @@ -551,6 +555,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -567,8 +577,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. @@ -844,6 +852,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -860,8 +874,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. diff --git a/bundle/manifests/networking.istio.io_sidecars.yaml b/bundle/manifests/networking.istio.io_sidecars.yaml index dd6b32b37..38a3a21fa 100644 --- a/bundle/manifests/networking.istio.io_sidecars.yaml +++ b/bundle/manifests/networking.istio.io_sidecars.yaml @@ -503,6 +503,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -519,8 +525,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. @@ -1041,6 +1045,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -1057,8 +1067,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. @@ -1579,6 +1587,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -1595,8 +1609,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. diff --git a/bundle/manifests/networking.istio.io_virtualservices.yaml b/bundle/manifests/networking.istio.io_virtualservices.yaml index b07b71aa1..30590fe26 100644 --- a/bundle/manifests/networking.istio.io_virtualservices.yaml +++ b/bundle/manifests/networking.istio.io_virtualservices.yaml @@ -1003,6 +1003,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -1019,8 +1025,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. @@ -2039,6 +2043,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -2055,8 +2065,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. @@ -3075,6 +3083,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -3091,8 +3105,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. diff --git a/bundle/manifests/networking.istio.io_workloadentries.yaml b/bundle/manifests/networking.istio.io_workloadentries.yaml index 8ff93a410..5a42a45b6 100644 --- a/bundle/manifests/networking.istio.io_workloadentries.yaml +++ b/bundle/manifests/networking.istio.io_workloadentries.yaml @@ -121,6 +121,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -137,8 +143,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. @@ -277,6 +281,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -293,8 +303,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. @@ -433,6 +441,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -449,8 +463,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. diff --git a/bundle/manifests/networking.istio.io_workloadgroups.yaml b/bundle/manifests/networking.istio.io_workloadgroups.yaml index 486c0e689..44679f157 100644 --- a/bundle/manifests/networking.istio.io_workloadgroups.yaml +++ b/bundle/manifests/networking.istio.io_workloadgroups.yaml @@ -249,6 +249,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -265,8 +271,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. @@ -535,6 +539,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -551,8 +561,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. @@ -821,6 +829,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -837,8 +851,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. diff --git a/bundle/manifests/sailoperator.io_istiocnis.yaml b/bundle/manifests/sailoperator.io_istiocnis.yaml index 29623eb00..f0ab6cc4f 100644 --- a/bundle/manifests/sailoperator.io_istiocnis.yaml +++ b/bundle/manifests/sailoperator.io_istiocnis.yaml @@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.4 + controller-gen.kubebuilder.io/version: v0.16.5 creationTimestamp: null name: istiocnis.sailoperator.io spec: @@ -70,7 +70,7 @@ spec: description: |- The built-in installation configuration profile to use. The 'default' profile is always applied. On OpenShift, the 'openshift' profile is also applied on top of 'default'. - Must be one of: ambient, default, demo, empty, openshift-ambient, openshift, preview, stable. + Must be one of: ambient, default, demo, empty, openshift-ambient, openshift, preview, remote, stable. enum: - ambient - default @@ -79,6 +79,7 @@ spec: - openshift-ambient - openshift - preview + - remote - stable type: string values: diff --git a/bundle/manifests/sailoperator.io_istiorevisions.yaml b/bundle/manifests/sailoperator.io_istiorevisions.yaml index 8e871f2ee..a7034ea4b 100644 --- a/bundle/manifests/sailoperator.io_istiorevisions.yaml +++ b/bundle/manifests/sailoperator.io_istiorevisions.yaml @@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.4 + controller-gen.kubebuilder.io/version: v0.16.5 creationTimestamp: null name: istiorevisions.sailoperator.io spec: @@ -78,14 +78,6 @@ spec: x-kubernetes-validations: - message: Value is immutable rule: self == oldSelf - type: - default: Local - description: Type indicates whether this revision represents a local - or a remote control plane installation. - type: string - x-kubernetes-validations: - - message: Value is immutable - rule: self == oldSelf values: description: Defines the values to be passed to the Helm charts when installing Istio. @@ -3792,6 +3784,11 @@ spec: description: Address of the Datadog Agent. type: string type: object + enableIstioTags: + description: |- + Determines whether or not trace spans generated by Envoy will include Istio specific tags. + By default Istio specific tags are included in the trace spans. + type: boolean lightstep: description: |- Use a Lightstep tracer. @@ -4281,6 +4278,12 @@ spec: implements the Envoy ext_authz filter authorization check service using the gRPC API. properties: + clearRouteCache: + description: |- + If true, clears route cache in order to allow the external authorization service to correctly affect routing decisions. + If true, recalculate routes with the new ExtAuthZ added/removed headers. + Default is false + type: boolean failOpen: description: |- If true, the HTTP request or TCP connection will be allowed even if the communication with the authorization service has failed, @@ -4350,6 +4353,12 @@ spec: implements the Envoy ext_authz filter authorization check service using the HTTP API. properties: + clearRouteCache: + description: |- + If true, clears route cache in order to allow the external authorization service to correctly affect routing decisions. + If true, recalculate routes with the new ExtAuthZ added/removed headers. + Default is false + type: boolean failOpen: description: |- If true, the user request will be allowed even if the communication with the authorization service has failed, @@ -4919,7 +4928,7 @@ spec: initialMetadata: description: |- Optional. Additional metadata to include in streams initiated to the GrpcService. This can be used for - scenarios in which additional ad hoc authorization headers (e.g. “x-foo-bar: baz-key“) are to + scenarios in which additional ad hoc authorization headers (e.g. "x-foo-bar: baz-key") are to be injected. items: properties: @@ -9185,13 +9194,6 @@ spec: description: Identifies the revision this installation is associated with. type: string - revisionTags: - description: |- - Specifies the aliases for the Istio control plane revision. A MutatingWebhookConfiguration - is created for each alias. - items: - type: string - type: array sidecarInjectorWebhook: description: Configuration for the sidecar injector webhook. properties: @@ -9386,7 +9388,6 @@ spec: type: string required: - namespace - - type - version type: object x-kubernetes-validations: diff --git a/bundle/manifests/sailoperator.io_istiorevisiontags.yaml b/bundle/manifests/sailoperator.io_istiorevisiontags.yaml new file mode 100644 index 000000000..90db7a5a2 --- /dev/null +++ b/bundle/manifests/sailoperator.io_istiorevisiontags.yaml @@ -0,0 +1,149 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.5 + creationTimestamp: null + name: istiorevisiontags.sailoperator.io +spec: + group: sailoperator.io + names: + categories: + - istio-io + kind: IstioRevisionTag + listKind: IstioRevisionTagList + plural: istiorevisiontags + shortNames: + - istiorevtag + singular: istiorevisiontag + scope: Cluster + versions: + - additionalPrinterColumns: + - description: The current state of this object. + jsonPath: .status.state + name: Status + type: string + - description: Whether the tag is being used by workloads. + jsonPath: .status.conditions[?(@.type=="InUse")].status + name: In use + type: string + - description: The IstioRevision this object is referencing. + jsonPath: .status.istioRevision + name: Revision + type: string + - description: The age of the object + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: IstioRevisionTag references a Istio or IstioRevision object and + serves as an alias for sidecar injection. + 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: IstioRevisionTagSpec defines the desired state of IstioRevisionTag + properties: + targetRef: + description: IstioRevisionTagTargetReference can reference either + Istio or IstioRevision objects in the cluster. + properties: + kind: + description: Kind is the kind of the target resource. + maxLength: 253 + minLength: 1 + type: string + name: + description: Name is the name of the target resource. + maxLength: 253 + minLength: 1 + type: string + required: + - kind + - name + type: object + required: + - targetRef + type: object + status: + description: IstioRevisionStatus defines the observed state of IstioRevision + properties: + conditions: + description: Represents the latest available observations of the object's + current state. + items: + description: IstioRevisionCondition represents a specific observation + of the IstioRevision object's state. + properties: + lastTransitionTime: + description: Last time the condition transitioned from one status + to another. + format: date-time + type: string + message: + description: Human-readable message indicating details about + the last transition. + type: string + reason: + description: Unique, single-word, CamelCase reason for the condition's + last transition. + type: string + status: + description: The status of this condition. Can be True, False + or Unknown. + type: string + type: + description: The type of this condition. + type: string + type: object + type: array + istioRevision: + description: IstioRevision stores the name of the referenced IstioRevision + type: string + istiodNamespace: + description: IstiodNamespace stores the namespace of the corresponding + Istiod instance + type: string + observedGeneration: + description: |- + ObservedGeneration is the most recent generation observed for this + IstioRevisionTag object. It corresponds to the object's generation, which is + updated on mutation by the API Server. The information in the status + pertains to this particular generation of the object. + format: int64 + type: integer + state: + description: Reports the current state of the object. + type: string + required: + - istioRevision + - istiodNamespace + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/bundle/manifests/sailoperator.io_istios.yaml b/bundle/manifests/sailoperator.io_istios.yaml index 642746603..c0057ea2c 100644 --- a/bundle/manifests/sailoperator.io_istios.yaml +++ b/bundle/manifests/sailoperator.io_istios.yaml @@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.4 + controller-gen.kubebuilder.io/version: v0.16.5 creationTimestamp: null name: istios.sailoperator.io spec: @@ -95,7 +95,7 @@ spec: description: |- The built-in installation configuration profile to use. The 'default' profile is always applied. On OpenShift, the 'openshift' profile is also applied on top of 'default'. - Must be one of: ambient, default, demo, empty, openshift-ambient, openshift, preview, stable. + Must be one of: ambient, default, demo, empty, openshift-ambient, openshift, preview, remote, stable. enum: - ambient - default @@ -104,6 +104,7 @@ spec: - openshift-ambient - openshift - preview + - remote - stable type: string updateStrategy: @@ -3851,6 +3852,11 @@ spec: description: Address of the Datadog Agent. type: string type: object + enableIstioTags: + description: |- + Determines whether or not trace spans generated by Envoy will include Istio specific tags. + By default Istio specific tags are included in the trace spans. + type: boolean lightstep: description: |- Use a Lightstep tracer. @@ -4340,6 +4346,12 @@ spec: implements the Envoy ext_authz filter authorization check service using the gRPC API. properties: + clearRouteCache: + description: |- + If true, clears route cache in order to allow the external authorization service to correctly affect routing decisions. + If true, recalculate routes with the new ExtAuthZ added/removed headers. + Default is false + type: boolean failOpen: description: |- If true, the HTTP request or TCP connection will be allowed even if the communication with the authorization service has failed, @@ -4409,6 +4421,12 @@ spec: implements the Envoy ext_authz filter authorization check service using the HTTP API. properties: + clearRouteCache: + description: |- + If true, clears route cache in order to allow the external authorization service to correctly affect routing decisions. + If true, recalculate routes with the new ExtAuthZ added/removed headers. + Default is false + type: boolean failOpen: description: |- If true, the user request will be allowed even if the communication with the authorization service has failed, @@ -4978,7 +4996,7 @@ spec: initialMetadata: description: |- Optional. Additional metadata to include in streams initiated to the GrpcService. This can be used for - scenarios in which additional ad hoc authorization headers (e.g. “x-foo-bar: baz-key“) are to + scenarios in which additional ad hoc authorization headers (e.g. "x-foo-bar: baz-key") are to be injected. items: properties: @@ -9244,13 +9262,6 @@ spec: description: Identifies the revision this installation is associated with. type: string - revisionTags: - description: |- - Specifies the aliases for the Istio control plane revision. A MutatingWebhookConfiguration - is created for each alias. - items: - type: string - type: array sidecarInjectorWebhook: description: Configuration for the sidecar injector webhook. properties: diff --git a/bundle/manifests/sailoperator.io_remoteistios.yaml b/bundle/manifests/sailoperator.io_remoteistios.yaml index 7d26470e8..4a5e4a502 100644 --- a/bundle/manifests/sailoperator.io_remoteistios.yaml +++ b/bundle/manifests/sailoperator.io_remoteistios.yaml @@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.4 + controller-gen.kubebuilder.io/version: v0.16.5 creationTimestamp: null name: remoteistios.sailoperator.io spec: @@ -3842,6 +3842,11 @@ spec: description: Address of the Datadog Agent. type: string type: object + enableIstioTags: + description: |- + Determines whether or not trace spans generated by Envoy will include Istio specific tags. + By default Istio specific tags are included in the trace spans. + type: boolean lightstep: description: |- Use a Lightstep tracer. @@ -4331,6 +4336,12 @@ spec: implements the Envoy ext_authz filter authorization check service using the gRPC API. properties: + clearRouteCache: + description: |- + If true, clears route cache in order to allow the external authorization service to correctly affect routing decisions. + If true, recalculate routes with the new ExtAuthZ added/removed headers. + Default is false + type: boolean failOpen: description: |- If true, the HTTP request or TCP connection will be allowed even if the communication with the authorization service has failed, @@ -4400,6 +4411,12 @@ spec: implements the Envoy ext_authz filter authorization check service using the HTTP API. properties: + clearRouteCache: + description: |- + If true, clears route cache in order to allow the external authorization service to correctly affect routing decisions. + If true, recalculate routes with the new ExtAuthZ added/removed headers. + Default is false + type: boolean failOpen: description: |- If true, the user request will be allowed even if the communication with the authorization service has failed, @@ -4969,7 +4986,7 @@ spec: initialMetadata: description: |- Optional. Additional metadata to include in streams initiated to the GrpcService. This can be used for - scenarios in which additional ad hoc authorization headers (e.g. “x-foo-bar: baz-key“) are to + scenarios in which additional ad hoc authorization headers (e.g. "x-foo-bar: baz-key") are to be injected. items: properties: @@ -9235,13 +9252,6 @@ spec: description: Identifies the revision this installation is associated with. type: string - revisionTags: - description: |- - Specifies the aliases for the Istio control plane revision. A MutatingWebhookConfiguration - is created for each alias. - items: - type: string - type: array sidecarInjectorWebhook: description: Configuration for the sidecar injector webhook. properties: diff --git a/bundle/manifests/security.istio.io_authorizationpolicies.yaml b/bundle/manifests/security.istio.io_authorizationpolicies.yaml index bebc1b2c6..7f450f367 100644 --- a/bundle/manifests/security.istio.io_authorizationpolicies.yaml +++ b/bundle/manifests/security.istio.io_authorizationpolicies.yaml @@ -321,6 +321,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -337,8 +343,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. @@ -674,6 +678,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -690,8 +700,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. diff --git a/bundle/manifests/security.istio.io_peerauthentications.yaml b/bundle/manifests/security.istio.io_peerauthentications.yaml index 0d9ad5524..e0393c18e 100644 --- a/bundle/manifests/security.istio.io_peerauthentications.yaml +++ b/bundle/manifests/security.istio.io_peerauthentications.yaml @@ -127,6 +127,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -143,8 +149,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. @@ -286,6 +290,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -302,8 +312,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. diff --git a/bundle/manifests/security.istio.io_requestauthentications.yaml b/bundle/manifests/security.istio.io_requestauthentications.yaml index 2d1b0ba00..52135c199 100644 --- a/bundle/manifests/security.istio.io_requestauthentications.yaml +++ b/bundle/manifests/security.istio.io_requestauthentications.yaml @@ -255,6 +255,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -271,8 +277,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. @@ -542,6 +546,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -558,8 +568,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. diff --git a/bundle/manifests/servicemeshoperator3.clusterserviceversion.yaml b/bundle/manifests/servicemeshoperator3.clusterserviceversion.yaml index 0e7193635..d80fbfd17 100644 --- a/bundle/manifests/servicemeshoperator3.clusterserviceversion.yaml +++ b/bundle/manifests/servicemeshoperator3.clusterserviceversion.yaml @@ -34,7 +34,7 @@ metadata: capabilities: Seamless Upgrades categories: OpenShift Optional, Integration & Delivery, Networking, Security containerImage: quay.io/maistra-dev/sail-operator:3.0-latest - createdAt: "2024-10-23T21:09:47Z" + createdAt: "2024-11-29T18:08:50Z" description: The OpenShift Service Mesh Operator enables you to install, configure, and manage an instance of Red Hat OpenShift Service Mesh. OpenShift Service Mesh is based on the open source Istio project. @@ -50,7 +50,7 @@ metadata: features.operators.openshift.io/token-auth-gcp: "false" operators.openshift.io/valid-subscription: '["OpenShift Container Platform", "OpenShift Platform Plus"]' - operators.operatorframework.io/builder: operator-sdk-v1.37.0 + operators.operatorframework.io/builder: operator-sdk-v1.38.0 operators.operatorframework.io/internal-objects: '["wasmplugins.extensions.istio.io","destinationrules.networking.istio.io","envoyfilters.networking.istio.io","gateways.networking.istio.io","proxyconfigs.networking.istio.io","serviceentries.networking.istio.io","sidecars.networking.istio.io","virtualservices.networking.istio.io","workloadentries.networking.istio.io","workloadgroups.networking.istio.io","authorizationpolicies.security.istio.io","peerauthentications.security.istio.io","requestauthentications.security.istio.io","telemetries.telemetry.istio.io"]' operators.operatorframework.io/project_layout: go.kubebuilder.io/v4 repository: https://github.com/istio-ecosystem/sail-operator @@ -133,6 +133,9 @@ spec: - kind: WorkloadGroup name: workloadgroups.networking.istio.io version: v1beta1 + - kind: RemoteIstio + name: remoteistios.sailoperator.io + version: v1alpha1 - kind: AuthorizationPolicy name: authorizationpolicies.security.istio.io version: v1 @@ -178,7 +181,7 @@ spec: - description: |- The built-in installation configuration profile to use. The 'default' profile is always applied. On OpenShift, the 'openshift' profile is also applied on top of 'default'. - Must be one of: ambient, default, demo, empty, openshift-ambient, openshift, preview, stable. + Must be one of: ambient, default, demo, empty, openshift-ambient, openshift, preview, remote, stable. displayName: Profile path: profile x-descriptors: @@ -215,6 +218,12 @@ spec: displayName: Helm Values path: values version: v1alpha1 + - description: IstioRevisionTag references a Istio or IstioRevision object and + serves as an alias for sidecar injection. + displayName: Istio Revision Tag + kind: IstioRevisionTag + name: istiorevisiontags.sailoperator.io + version: v1alpha1 - description: |- Istio represents an Istio Service Mesh deployment consisting of one or more control plane instances (represented by one or more IstioRevision objects). @@ -276,7 +285,7 @@ spec: - description: |- The built-in installation configuration profile to use. The 'default' profile is always applied. On OpenShift, the 'openshift' profile is also applied on top of 'default'. - Must be one of: ambient, default, demo, empty, openshift-ambient, openshift, preview, stable. + Must be one of: ambient, default, demo, empty, openshift-ambient, openshift, preview, remote, stable. displayName: Profile path: profile x-descriptors: @@ -290,75 +299,6 @@ spec: displayName: Helm Values path: values version: v1alpha1 - - description: |- - RemoteIstio represents a remote Istio Service Mesh deployment consisting of one or more - remote control plane instances (represented by one or more IstioRevision objects). - displayName: Remote Istio - kind: RemoteIstio - name: remoteistios.sailoperator.io - specDescriptors: - - description: "Type of strategy to use. Can be \"InPlace\" or \"RevisionBased\". - When the \"InPlace\" strategy\nis used, the existing Istio control plane - is updated in-place. The workloads therefore\ndon't need to be moved from - one control plane instance to another. When the \"RevisionBased\"\nstrategy - is used, a new Istio control plane instance is created for every change - to the\nIstio.spec.version field. The old control plane remains in place - until all workloads have\nbeen moved to the new control plane instance.\n\n\nThe - \"InPlace\" strategy is the default.\tTODO: change default to \"RevisionBased\"" - displayName: Type - path: updateStrategy.type - x-descriptors: - - urn:alm:descriptor:com.tectonic.ui:select:InPlace - - urn:alm:descriptor:com.tectonic.ui:select:RevisionBased - - description: |- - Defines the version of Istio to install. - Must be one of: v1.23.2. - displayName: Istio Version - path: version - x-descriptors: - - urn:alm:descriptor:com.tectonic.ui:fieldGroup:General - - urn:alm:descriptor:com.tectonic.ui:select:v1.23.2 - - description: |- - Defines how many seconds the operator should wait before removing a non-active revision after all - the workloads have stopped using it. You may want to set this value on the order of minutes. - The minimum is 0 and the default value is 30. - displayName: Inactive Revision Deletion Grace Period (seconds) - path: updateStrategy.inactiveRevisionDeletionGracePeriodSeconds - x-descriptors: - - urn:alm:descriptor:com.tectonic.ui:number - - description: |- - Defines whether the workloads should be moved from one control plane instance to another - automatically. If updateWorkloads is true, the operator moves the workloads from the old - control plane instance to the new one after the new control plane is ready. - If updateWorkloads is false, the user must move the workloads manually by updating the - istio.io/rev labels on the namespace and/or the pods. - Defaults to false. - displayName: Update Workloads Automatically - path: updateStrategy.updateWorkloads - x-descriptors: - - urn:alm:descriptor:com.tectonic.ui:booleanSwitch - - description: Namespace to which the Istio components should be installed. - displayName: Namespace - path: namespace - x-descriptors: - - urn:alm:descriptor:io.kubernetes:Namespace - - description: |- - The built-in installation configuration profile to use. - The 'default' profile is always applied. On OpenShift, the 'openshift' profile is also applied on top of 'default'. - Must be one of: ambient, default, demo, empty, openshift-ambient, openshift, preview, stable. - displayName: Profile - path: profile - x-descriptors: - - urn:alm:descriptor:com.tectonic.ui:hidden - - description: Defines the update strategy to use when the version in the RemoteIstio - CR is updated. - displayName: Update Strategy - path: updateStrategy - - description: Defines the values to be passed to the Helm charts when installing - Istio. - displayName: Helm Values - path: values - version: v1alpha1 description: |- Red Hat OpenShift Service Mesh is a platform that provides behavioral insight and operational control over a service mesh, providing a uniform way to connect, secure, and monitor microservice applications. @@ -493,6 +433,32 @@ spec: - get - patch - update + - apiGroups: + - sailoperator.io + resources: + - istiorevisiontags + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - sailoperator.io + resources: + - istiorevisiontags/finalizers + verbs: + - update + - apiGroups: + - sailoperator.io + resources: + - istiorevisiontags/status + verbs: + - get + - patch + - update - apiGroups: - sailoperator.io resources: diff --git a/bundle/manifests/telemetry.istio.io_telemetries.yaml b/bundle/manifests/telemetry.istio.io_telemetries.yaml index ec3cf100a..9a897b7b9 100644 --- a/bundle/manifests/telemetry.istio.io_telemetries.yaml +++ b/bundle/manifests/telemetry.istio.io_telemetries.yaml @@ -354,6 +354,11 @@ spec: description: Controls span reporting. nullable: true type: boolean + enableIstioTags: + description: Determines whether or not trace spans generated + by Envoy will include Istio specific tags. + nullable: true + type: boolean match: description: Allows tailoring of behavior to specific conditions. properties: @@ -416,6 +421,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -432,8 +443,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. @@ -802,6 +811,11 @@ spec: description: Controls span reporting. nullable: true type: boolean + enableIstioTags: + description: Determines whether or not trace spans generated + by Envoy will include Istio specific tags. + nullable: true + type: boolean match: description: Allows tailoring of behavior to specific conditions. properties: @@ -864,6 +878,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -880,8 +900,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. diff --git a/bundle/metadata/annotations.yaml b/bundle/metadata/annotations.yaml index 3bef409f7..3a0ad9018 100644 --- a/bundle/metadata/annotations.yaml +++ b/bundle/metadata/annotations.yaml @@ -5,7 +5,7 @@ annotations: operators.operatorframework.io.bundle.metadata.v1: metadata/ operators.operatorframework.io.bundle.package.v1: servicemeshoperator3 operators.operatorframework.io.bundle.channels.v1: "candidates" - operators.operatorframework.io.metrics.builder: operator-sdk-v1.37.0 + operators.operatorframework.io.metrics.builder: operator-sdk-v1.38.0 operators.operatorframework.io.metrics.mediatype.v1: metrics+v1 operators.operatorframework.io.metrics.project_layout: go.kubebuilder.io/v4 diff --git a/bundle/tests/scorecard/config.yaml b/bundle/tests/scorecard/config.yaml index bd9bf1e03..cf5c4335e 100644 --- a/bundle/tests/scorecard/config.yaml +++ b/bundle/tests/scorecard/config.yaml @@ -8,7 +8,7 @@ stages: - entrypoint: - scorecard-test - basic-check-spec - image: quay.io/operator-framework/scorecard-test:v1.37.0 + image: quay.io/operator-framework/scorecard-test:v1.38.0 labels: suite: basic test: basic-check-spec-test @@ -18,7 +18,7 @@ stages: - entrypoint: - scorecard-test - olm-bundle-validation - image: quay.io/operator-framework/scorecard-test:v1.37.0 + image: quay.io/operator-framework/scorecard-test:v1.38.0 labels: suite: olm test: olm-bundle-validation-test @@ -28,7 +28,7 @@ stages: - entrypoint: - scorecard-test - olm-crds-have-validation - image: quay.io/operator-framework/scorecard-test:v1.37.0 + image: quay.io/operator-framework/scorecard-test:v1.38.0 labels: suite: olm test: olm-crds-have-validation-test @@ -38,7 +38,7 @@ stages: - entrypoint: - scorecard-test - olm-spec-descriptors - image: quay.io/operator-framework/scorecard-test:v1.37.0 + image: quay.io/operator-framework/scorecard-test:v1.38.0 labels: suite: olm test: olm-spec-descriptors-test @@ -48,7 +48,7 @@ stages: - entrypoint: - scorecard-test - olm-status-descriptors - image: quay.io/operator-framework/scorecard-test:v1.37.0 + image: quay.io/operator-framework/scorecard-test:v1.38.0 labels: suite: olm test: olm-status-descriptors-test diff --git a/chart/crds/extensions.istio.io_wasmplugins.yaml b/chart/crds/extensions.istio.io_wasmplugins.yaml index 1acec4b67..2067b280d 100644 --- a/chart/crds/extensions.istio.io_wasmplugins.yaml +++ b/chart/crds/extensions.istio.io_wasmplugins.yaml @@ -305,6 +305,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -321,8 +327,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. diff --git a/chart/crds/networking.istio.io_destinationrules.yaml b/chart/crds/networking.istio.io_destinationrules.yaml index b00eef64e..451f7352d 100644 --- a/chart/crds/networking.istio.io_destinationrules.yaml +++ b/chart/crds/networking.istio.io_destinationrules.yaml @@ -1883,6 +1883,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -1899,8 +1905,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. @@ -3800,6 +3804,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -3816,8 +3826,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. @@ -5717,6 +5725,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -5733,8 +5747,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. diff --git a/chart/crds/networking.istio.io_envoyfilters.yaml b/chart/crds/networking.istio.io_envoyfilters.yaml index e2be5b942..70ed2f9fb 100644 --- a/chart/crds/networking.istio.io_envoyfilters.yaml +++ b/chart/crds/networking.istio.io_envoyfilters.yaml @@ -344,6 +344,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -360,8 +366,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. diff --git a/chart/crds/networking.istio.io_gateways.yaml b/chart/crds/networking.istio.io_gateways.yaml index 0d29b562f..ece08a057 100644 --- a/chart/crds/networking.istio.io_gateways.yaml +++ b/chart/crds/networking.istio.io_gateways.yaml @@ -195,6 +195,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -211,8 +217,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. @@ -424,6 +428,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -440,8 +450,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. @@ -653,6 +661,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -669,8 +683,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. diff --git a/chart/crds/networking.istio.io_proxyconfigs.yaml b/chart/crds/networking.istio.io_proxyconfigs.yaml index 7432a7498..e7e64d508 100644 --- a/chart/crds/networking.istio.io_proxyconfigs.yaml +++ b/chart/crds/networking.istio.io_proxyconfigs.yaml @@ -88,6 +88,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -104,8 +110,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. diff --git a/chart/crds/networking.istio.io_serviceentries.yaml b/chart/crds/networking.istio.io_serviceentries.yaml index a87b1c236..de8ff0350 100644 --- a/chart/crds/networking.istio.io_serviceentries.yaml +++ b/chart/crds/networking.istio.io_serviceentries.yaml @@ -257,6 +257,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -273,8 +279,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. @@ -550,6 +554,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -566,8 +576,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. @@ -843,6 +851,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -859,8 +873,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. diff --git a/chart/crds/networking.istio.io_sidecars.yaml b/chart/crds/networking.istio.io_sidecars.yaml index 560e8fbb1..3ea0e2665 100644 --- a/chart/crds/networking.istio.io_sidecars.yaml +++ b/chart/crds/networking.istio.io_sidecars.yaml @@ -502,6 +502,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -518,8 +524,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. @@ -1040,6 +1044,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -1056,8 +1066,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. @@ -1578,6 +1586,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -1594,8 +1608,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. diff --git a/chart/crds/networking.istio.io_virtualservices.yaml b/chart/crds/networking.istio.io_virtualservices.yaml index 8a49cfad3..39e31750c 100644 --- a/chart/crds/networking.istio.io_virtualservices.yaml +++ b/chart/crds/networking.istio.io_virtualservices.yaml @@ -1002,6 +1002,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -1018,8 +1024,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. @@ -2038,6 +2042,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -2054,8 +2064,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. @@ -3074,6 +3082,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -3090,8 +3104,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. diff --git a/chart/crds/networking.istio.io_workloadentries.yaml b/chart/crds/networking.istio.io_workloadentries.yaml index cf682cd2d..f0053326f 100644 --- a/chart/crds/networking.istio.io_workloadentries.yaml +++ b/chart/crds/networking.istio.io_workloadentries.yaml @@ -120,6 +120,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -136,8 +142,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. @@ -276,6 +280,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -292,8 +302,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. @@ -432,6 +440,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -448,8 +462,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. diff --git a/chart/crds/networking.istio.io_workloadgroups.yaml b/chart/crds/networking.istio.io_workloadgroups.yaml index 9f06ab833..2758ad3ab 100644 --- a/chart/crds/networking.istio.io_workloadgroups.yaml +++ b/chart/crds/networking.istio.io_workloadgroups.yaml @@ -248,6 +248,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -264,8 +270,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. @@ -534,6 +538,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -550,8 +560,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. @@ -820,6 +828,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -836,8 +850,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. diff --git a/chart/crds/sailoperator.io_istiocnis.yaml b/chart/crds/sailoperator.io_istiocnis.yaml index 1acbea7bb..9878d656d 100644 --- a/chart/crds/sailoperator.io_istiocnis.yaml +++ b/chart/crds/sailoperator.io_istiocnis.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.4 + controller-gen.kubebuilder.io/version: v0.16.5 name: istiocnis.sailoperator.io spec: group: sailoperator.io @@ -70,7 +70,7 @@ spec: description: |- The built-in installation configuration profile to use. The 'default' profile is always applied. On OpenShift, the 'openshift' profile is also applied on top of 'default'. - Must be one of: ambient, default, demo, empty, openshift-ambient, openshift, preview, stable. + Must be one of: ambient, default, demo, empty, openshift-ambient, openshift, preview, remote, stable. enum: - ambient - default @@ -79,6 +79,7 @@ spec: - openshift-ambient - openshift - preview + - remote - stable type: string values: diff --git a/chart/crds/sailoperator.io_istiorevisions.yaml b/chart/crds/sailoperator.io_istiorevisions.yaml index ee7a4eac0..bd63f4a68 100644 --- a/chart/crds/sailoperator.io_istiorevisions.yaml +++ b/chart/crds/sailoperator.io_istiorevisions.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.4 + controller-gen.kubebuilder.io/version: v0.16.5 name: istiorevisions.sailoperator.io spec: group: sailoperator.io @@ -78,14 +78,6 @@ spec: x-kubernetes-validations: - message: Value is immutable rule: self == oldSelf - type: - default: Local - description: Type indicates whether this revision represents a local - or a remote control plane installation. - type: string - x-kubernetes-validations: - - message: Value is immutable - rule: self == oldSelf values: description: Defines the values to be passed to the Helm charts when installing Istio. @@ -3792,6 +3784,11 @@ spec: description: Address of the Datadog Agent. type: string type: object + enableIstioTags: + description: |- + Determines whether or not trace spans generated by Envoy will include Istio specific tags. + By default Istio specific tags are included in the trace spans. + type: boolean lightstep: description: |- Use a Lightstep tracer. @@ -4281,6 +4278,12 @@ spec: implements the Envoy ext_authz filter authorization check service using the gRPC API. properties: + clearRouteCache: + description: |- + If true, clears route cache in order to allow the external authorization service to correctly affect routing decisions. + If true, recalculate routes with the new ExtAuthZ added/removed headers. + Default is false + type: boolean failOpen: description: |- If true, the HTTP request or TCP connection will be allowed even if the communication with the authorization service has failed, @@ -4350,6 +4353,12 @@ spec: implements the Envoy ext_authz filter authorization check service using the HTTP API. properties: + clearRouteCache: + description: |- + If true, clears route cache in order to allow the external authorization service to correctly affect routing decisions. + If true, recalculate routes with the new ExtAuthZ added/removed headers. + Default is false + type: boolean failOpen: description: |- If true, the user request will be allowed even if the communication with the authorization service has failed, @@ -4919,7 +4928,7 @@ spec: initialMetadata: description: |- Optional. Additional metadata to include in streams initiated to the GrpcService. This can be used for - scenarios in which additional ad hoc authorization headers (e.g. “x-foo-bar: baz-key“) are to + scenarios in which additional ad hoc authorization headers (e.g. "x-foo-bar: baz-key") are to be injected. items: properties: @@ -9185,13 +9194,6 @@ spec: description: Identifies the revision this installation is associated with. type: string - revisionTags: - description: |- - Specifies the aliases for the Istio control plane revision. A MutatingWebhookConfiguration - is created for each alias. - items: - type: string - type: array sidecarInjectorWebhook: description: Configuration for the sidecar injector webhook. properties: @@ -9386,7 +9388,6 @@ spec: type: string required: - namespace - - type - version type: object x-kubernetes-validations: diff --git a/chart/crds/sailoperator.io_istiorevisiontags.yaml b/chart/crds/sailoperator.io_istiorevisiontags.yaml new file mode 100644 index 000000000..3c4866eb1 --- /dev/null +++ b/chart/crds/sailoperator.io_istiorevisiontags.yaml @@ -0,0 +1,143 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.5 + name: istiorevisiontags.sailoperator.io +spec: + group: sailoperator.io + names: + categories: + - istio-io + kind: IstioRevisionTag + listKind: IstioRevisionTagList + plural: istiorevisiontags + shortNames: + - istiorevtag + singular: istiorevisiontag + scope: Cluster + versions: + - additionalPrinterColumns: + - description: The current state of this object. + jsonPath: .status.state + name: Status + type: string + - description: Whether the tag is being used by workloads. + jsonPath: .status.conditions[?(@.type=="InUse")].status + name: In use + type: string + - description: The IstioRevision this object is referencing. + jsonPath: .status.istioRevision + name: Revision + type: string + - description: The age of the object + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: IstioRevisionTag references a Istio or IstioRevision object and + serves as an alias for sidecar injection. + 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: IstioRevisionTagSpec defines the desired state of IstioRevisionTag + properties: + targetRef: + description: IstioRevisionTagTargetReference can reference either + Istio or IstioRevision objects in the cluster. + properties: + kind: + description: Kind is the kind of the target resource. + maxLength: 253 + minLength: 1 + type: string + name: + description: Name is the name of the target resource. + maxLength: 253 + minLength: 1 + type: string + required: + - kind + - name + type: object + required: + - targetRef + type: object + status: + description: IstioRevisionStatus defines the observed state of IstioRevision + properties: + conditions: + description: Represents the latest available observations of the object's + current state. + items: + description: IstioRevisionCondition represents a specific observation + of the IstioRevision object's state. + properties: + lastTransitionTime: + description: Last time the condition transitioned from one status + to another. + format: date-time + type: string + message: + description: Human-readable message indicating details about + the last transition. + type: string + reason: + description: Unique, single-word, CamelCase reason for the condition's + last transition. + type: string + status: + description: The status of this condition. Can be True, False + or Unknown. + type: string + type: + description: The type of this condition. + type: string + type: object + type: array + istioRevision: + description: IstioRevision stores the name of the referenced IstioRevision + type: string + istiodNamespace: + description: IstiodNamespace stores the namespace of the corresponding + Istiod instance + type: string + observedGeneration: + description: |- + ObservedGeneration is the most recent generation observed for this + IstioRevisionTag object. It corresponds to the object's generation, which is + updated on mutation by the API Server. The information in the status + pertains to this particular generation of the object. + format: int64 + type: integer + state: + description: Reports the current state of the object. + type: string + required: + - istioRevision + - istiodNamespace + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/chart/crds/sailoperator.io_istios.yaml b/chart/crds/sailoperator.io_istios.yaml index 2cd9cf0a0..0cb0fdaf3 100644 --- a/chart/crds/sailoperator.io_istios.yaml +++ b/chart/crds/sailoperator.io_istios.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.4 + controller-gen.kubebuilder.io/version: v0.16.5 name: istios.sailoperator.io spec: group: sailoperator.io @@ -95,7 +95,7 @@ spec: description: |- The built-in installation configuration profile to use. The 'default' profile is always applied. On OpenShift, the 'openshift' profile is also applied on top of 'default'. - Must be one of: ambient, default, demo, empty, openshift-ambient, openshift, preview, stable. + Must be one of: ambient, default, demo, empty, openshift-ambient, openshift, preview, remote, stable. enum: - ambient - default @@ -104,6 +104,7 @@ spec: - openshift-ambient - openshift - preview + - remote - stable type: string updateStrategy: @@ -3851,6 +3852,11 @@ spec: description: Address of the Datadog Agent. type: string type: object + enableIstioTags: + description: |- + Determines whether or not trace spans generated by Envoy will include Istio specific tags. + By default Istio specific tags are included in the trace spans. + type: boolean lightstep: description: |- Use a Lightstep tracer. @@ -4340,6 +4346,12 @@ spec: implements the Envoy ext_authz filter authorization check service using the gRPC API. properties: + clearRouteCache: + description: |- + If true, clears route cache in order to allow the external authorization service to correctly affect routing decisions. + If true, recalculate routes with the new ExtAuthZ added/removed headers. + Default is false + type: boolean failOpen: description: |- If true, the HTTP request or TCP connection will be allowed even if the communication with the authorization service has failed, @@ -4409,6 +4421,12 @@ spec: implements the Envoy ext_authz filter authorization check service using the HTTP API. properties: + clearRouteCache: + description: |- + If true, clears route cache in order to allow the external authorization service to correctly affect routing decisions. + If true, recalculate routes with the new ExtAuthZ added/removed headers. + Default is false + type: boolean failOpen: description: |- If true, the user request will be allowed even if the communication with the authorization service has failed, @@ -4978,7 +4996,7 @@ spec: initialMetadata: description: |- Optional. Additional metadata to include in streams initiated to the GrpcService. This can be used for - scenarios in which additional ad hoc authorization headers (e.g. “x-foo-bar: baz-key“) are to + scenarios in which additional ad hoc authorization headers (e.g. "x-foo-bar: baz-key") are to be injected. items: properties: @@ -9244,13 +9262,6 @@ spec: description: Identifies the revision this installation is associated with. type: string - revisionTags: - description: |- - Specifies the aliases for the Istio control plane revision. A MutatingWebhookConfiguration - is created for each alias. - items: - type: string - type: array sidecarInjectorWebhook: description: Configuration for the sidecar injector webhook. properties: diff --git a/chart/crds/sailoperator.io_remoteistios.yaml b/chart/crds/sailoperator.io_remoteistios.yaml index ec9441aed..a164b7e01 100644 --- a/chart/crds/sailoperator.io_remoteistios.yaml +++ b/chart/crds/sailoperator.io_remoteistios.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.4 + controller-gen.kubebuilder.io/version: v0.16.5 name: remoteistios.sailoperator.io spec: group: sailoperator.io @@ -3842,6 +3842,11 @@ spec: description: Address of the Datadog Agent. type: string type: object + enableIstioTags: + description: |- + Determines whether or not trace spans generated by Envoy will include Istio specific tags. + By default Istio specific tags are included in the trace spans. + type: boolean lightstep: description: |- Use a Lightstep tracer. @@ -4331,6 +4336,12 @@ spec: implements the Envoy ext_authz filter authorization check service using the gRPC API. properties: + clearRouteCache: + description: |- + If true, clears route cache in order to allow the external authorization service to correctly affect routing decisions. + If true, recalculate routes with the new ExtAuthZ added/removed headers. + Default is false + type: boolean failOpen: description: |- If true, the HTTP request or TCP connection will be allowed even if the communication with the authorization service has failed, @@ -4400,6 +4411,12 @@ spec: implements the Envoy ext_authz filter authorization check service using the HTTP API. properties: + clearRouteCache: + description: |- + If true, clears route cache in order to allow the external authorization service to correctly affect routing decisions. + If true, recalculate routes with the new ExtAuthZ added/removed headers. + Default is false + type: boolean failOpen: description: |- If true, the user request will be allowed even if the communication with the authorization service has failed, @@ -4969,7 +4986,7 @@ spec: initialMetadata: description: |- Optional. Additional metadata to include in streams initiated to the GrpcService. This can be used for - scenarios in which additional ad hoc authorization headers (e.g. “x-foo-bar: baz-key“) are to + scenarios in which additional ad hoc authorization headers (e.g. "x-foo-bar: baz-key") are to be injected. items: properties: @@ -9235,13 +9252,6 @@ spec: description: Identifies the revision this installation is associated with. type: string - revisionTags: - description: |- - Specifies the aliases for the Istio control plane revision. A MutatingWebhookConfiguration - is created for each alias. - items: - type: string - type: array sidecarInjectorWebhook: description: Configuration for the sidecar injector webhook. properties: diff --git a/chart/crds/security.istio.io_authorizationpolicies.yaml b/chart/crds/security.istio.io_authorizationpolicies.yaml index 0f72f71e1..a83d6ee0c 100644 --- a/chart/crds/security.istio.io_authorizationpolicies.yaml +++ b/chart/crds/security.istio.io_authorizationpolicies.yaml @@ -320,6 +320,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -336,8 +342,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. @@ -673,6 +677,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -689,8 +699,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. diff --git a/chart/crds/security.istio.io_peerauthentications.yaml b/chart/crds/security.istio.io_peerauthentications.yaml index 3d6895db5..329efc01f 100644 --- a/chart/crds/security.istio.io_peerauthentications.yaml +++ b/chart/crds/security.istio.io_peerauthentications.yaml @@ -126,6 +126,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -142,8 +148,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. @@ -285,6 +289,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -301,8 +311,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. diff --git a/chart/crds/security.istio.io_requestauthentications.yaml b/chart/crds/security.istio.io_requestauthentications.yaml index 53fe59d32..2d6729688 100644 --- a/chart/crds/security.istio.io_requestauthentications.yaml +++ b/chart/crds/security.istio.io_requestauthentications.yaml @@ -254,6 +254,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -270,8 +276,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. @@ -541,6 +545,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -557,8 +567,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. diff --git a/chart/crds/telemetry.istio.io_telemetries.yaml b/chart/crds/telemetry.istio.io_telemetries.yaml index c858b2995..beaa1537b 100644 --- a/chart/crds/telemetry.istio.io_telemetries.yaml +++ b/chart/crds/telemetry.istio.io_telemetries.yaml @@ -353,6 +353,11 @@ spec: description: Controls span reporting. nullable: true type: boolean + enableIstioTags: + description: Determines whether or not trace spans generated + by Envoy will include Istio specific tags. + nullable: true + type: boolean match: description: Allows tailoring of behavior to specific conditions. properties: @@ -415,6 +420,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -431,8 +442,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. @@ -801,6 +810,11 @@ spec: description: Controls span reporting. nullable: true type: boolean + enableIstioTags: + description: Determines whether or not trace spans generated + by Envoy will include Istio specific tags. + nullable: true + type: boolean match: description: Allows tailoring of behavior to specific conditions. properties: @@ -863,6 +877,12 @@ spec: description: Human-readable message indicating details about last transition. type: string + observedGeneration: + anyOf: + - type: integer + - type: string + description: Resource Generation to which the Condition refers. + x-kubernetes-int-or-string: true reason: description: Unique, one-word, CamelCase reason for the condition's last transition. @@ -879,8 +899,6 @@ spec: anyOf: - type: integer - type: string - description: Resource Generation to which the Reconciled Condition - refers. x-kubernetes-int-or-string: true validationMessages: description: Includes any errors or warnings detected by Istio's analyzers. diff --git a/chart/samples/istio-sample-gw-api.yaml b/chart/samples/istio-sample-gw-api.yaml new file mode 100644 index 000000000..251977305 --- /dev/null +++ b/chart/samples/istio-sample-gw-api.yaml @@ -0,0 +1,16 @@ +apiVersion: sailoperator.io/v1alpha1 +kind: Istio +metadata: + name: gateway-controller +spec: + version: v1.23.0 + namespace: gateway-controller + updateStrategy: + type: InPlace + inactiveRevisionDeletionGracePeriodSeconds: 30 + values: + pilot: + resources: + requests: + cpu: 100m + memory: 1024Mi diff --git a/chart/samples/istio-sample-revisionbased.yaml b/chart/samples/istio-sample-revisionbased.yaml new file mode 100644 index 000000000..47fb2f055 --- /dev/null +++ b/chart/samples/istio-sample-revisionbased.yaml @@ -0,0 +1,18 @@ +apiVersion: sailoperator.io/v1alpha1 +kind: Istio +metadata: + name: default +spec: + version: v1.23.2 + namespace: istio-system + updateStrategy: + type: RevisionBased +--- +apiVersion: sailoperator.io/v1alpha1 +kind: IstioRevisionTag +metadata: + name: default +spec: + targetRef: + kind: Istio + name: default diff --git a/chart/samples/remoteistio-sample.yaml b/chart/samples/remoteistio-sample.yaml deleted file mode 100644 index 66407f939..000000000 --- a/chart/samples/remoteistio-sample.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: sailoperator.io/v1alpha1 -kind: RemoteIstio -metadata: - name: default -spec: - version: latest - namespace: istio-system - updateStrategy: - type: InPlace - inactiveRevisionDeletionGracePeriodSeconds: 30 - values: - istiodRemote: - injectionPath: /inject/cluster/cluster2/net/network1 - global: - remotePilotAddress: 1.2.3.4 \ No newline at end of file diff --git a/chart/templates/olm/scorecard.yaml b/chart/templates/olm/scorecard.yaml index feebc2ada..82c508161 100644 --- a/chart/templates/olm/scorecard.yaml +++ b/chart/templates/olm/scorecard.yaml @@ -9,7 +9,7 @@ stages: - entrypoint: - scorecard-test - basic-check-spec - image: quay.io/operator-framework/scorecard-test:v1.37.0 + image: quay.io/operator-framework/scorecard-test:v1.38.0 labels: suite: basic test: basic-check-spec-test @@ -19,7 +19,7 @@ stages: - entrypoint: - scorecard-test - olm-bundle-validation - image: quay.io/operator-framework/scorecard-test:v1.37.0 + image: quay.io/operator-framework/scorecard-test:v1.38.0 labels: suite: olm test: olm-bundle-validation-test @@ -29,7 +29,7 @@ stages: - entrypoint: - scorecard-test - olm-crds-have-validation - image: quay.io/operator-framework/scorecard-test:v1.37.0 + image: quay.io/operator-framework/scorecard-test:v1.38.0 labels: suite: olm test: olm-crds-have-validation-test @@ -39,7 +39,7 @@ stages: - entrypoint: - scorecard-test - olm-spec-descriptors - image: quay.io/operator-framework/scorecard-test:v1.37.0 + image: quay.io/operator-framework/scorecard-test:v1.38.0 labels: suite: olm test: olm-spec-descriptors-test @@ -49,7 +49,7 @@ stages: - entrypoint: - scorecard-test - olm-status-descriptors - image: quay.io/operator-framework/scorecard-test:v1.37.0 + image: quay.io/operator-framework/scorecard-test:v1.38.0 labels: suite: olm test: olm-status-descriptors-test diff --git a/chart/templates/rbac/role.yaml b/chart/templates/rbac/role.yaml index b5165226e..28c2a0bb6 100644 --- a/chart/templates/rbac/role.yaml +++ b/chart/templates/rbac/role.yaml @@ -82,6 +82,32 @@ rules: - get - patch - update +- apiGroups: + - sailoperator.io + resources: + - istiorevisiontags + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - sailoperator.io + resources: + - istiorevisiontags/finalizers + verbs: + - update +- apiGroups: + - sailoperator.io + resources: + - istiorevisiontags/status + verbs: + - get + - patch + - update - apiGroups: - sailoperator.io resources: diff --git a/chart/values.yaml b/chart/values.yaml index c4f5ff6a4..c4a424808 100644 --- a/chart/values.yaml +++ b/chart/values.yaml @@ -18,10 +18,14 @@ csv: This version of the operator supports the following Istio versions: + - v1.24.1 + - v1.24.0 + - v1.23.3 - v1.23.2 + - v1.22.6 - v1.22.5 - v1.21.6 - - latest (bb972b54) + - latest (4b76e917) [See this page](https://github.com/istio-ecosystem/sail-operator/blob/main/bundle/README.md) for instructions on how to use it. support: Community based diff --git a/cmd/main.go b/cmd/main.go index d713a8131..873648e9e 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -23,7 +23,7 @@ import ( "github.com/istio-ecosystem/sail-operator/controllers/istio" "github.com/istio-ecosystem/sail-operator/controllers/istiocni" "github.com/istio-ecosystem/sail-operator/controllers/istiorevision" - "github.com/istio-ecosystem/sail-operator/controllers/remoteistio" + "github.com/istio-ecosystem/sail-operator/controllers/istiorevisiontag" "github.com/istio-ecosystem/sail-operator/controllers/webhook" "github.com/istio-ecosystem/sail-operator/pkg/config" "github.com/istio-ecosystem/sail-operator/pkg/enqueuelogger" @@ -144,17 +144,17 @@ func main() { os.Exit(1) } - err = remoteistio.NewReconciler(reconcilerCfg, mgr.GetClient(), mgr.GetScheme()). + err = istiorevision.NewReconciler(reconcilerCfg, mgr.GetClient(), mgr.GetScheme(), chartManager). SetupWithManager(mgr) if err != nil { - setupLog.Error(err, "unable to create controller", "controller", "RemoteIstio") + setupLog.Error(err, "unable to create controller", "controller", "IstioRevision") os.Exit(1) } - err = istiorevision.NewReconciler(reconcilerCfg, mgr.GetClient(), mgr.GetScheme(), chartManager). + err = istiorevisiontag.NewReconciler(reconcilerCfg, mgr.GetClient(), mgr.GetScheme(), chartManager). SetupWithManager(mgr) if err != nil { - setupLog.Error(err, "unable to create controller", "controller", "IstioRevision") + setupLog.Error(err, "unable to create controller", "controller", "IstioRevisionTag") os.Exit(1) } diff --git a/common/.commonfiles.sha b/common/.commonfiles.sha index aebb24f3e..feef7bcc2 100644 --- a/common/.commonfiles.sha +++ b/common/.commonfiles.sha @@ -1 +1 @@ -82dc68a737b72d394c344d4fd71ff9e9ebf01852 +e955cacd8ba890189a80551485d35a0e228276ec diff --git a/common/config/.golangci.yml b/common/config/.golangci.yml index 2c0565492..b5a0349b9 100644 --- a/common/config/.golangci.yml +++ b/common/config/.golangci.yml @@ -18,7 +18,7 @@ linters: disable-all: true enable: - errcheck - - exportloopref + - copyloopvar - depguard - gocritic - gofumpt diff --git a/common/scripts/setup_env.sh b/common/scripts/setup_env.sh index c63f1fa33..746d900ed 100755 --- a/common/scripts/setup_env.sh +++ b/common/scripts/setup_env.sh @@ -75,7 +75,7 @@ fi TOOLS_REGISTRY_PROVIDER=${TOOLS_REGISTRY_PROVIDER:-gcr.io} PROJECT_ID=${PROJECT_ID:-istio-testing} if [[ "${IMAGE_VERSION:-}" == "" ]]; then - IMAGE_VERSION=master-4759bf88d40172234fc6a0b9e11a4c5f1ea58a90 + IMAGE_VERSION=master-12939d7be6baee95d63b1a9d7c4e194f1b241257 fi if [[ "${IMAGE_NAME:-}" == "" ]]; then IMAGE_NAME=build-tools diff --git a/controllers/istio/istio_controller.go b/controllers/istio/istio_controller.go index 24043a2ec..b492f39e4 100644 --- a/controllers/istio/istio_controller.go +++ b/controllers/istio/istio_controller.go @@ -115,7 +115,6 @@ func (r *Reconciler) reconcileActiveRevision(ctx context.Context, istio *v1alpha return revision.CreateOrUpdate(ctx, r.Client, getActiveRevisionName(istio), - v1alpha1.IstioRevisionTypeLocal, istio.Spec.Version, istio.Spec.Namespace, values, metav1.OwnerReference{ APIVersion: v1alpha1.GroupVersion.String(), @@ -141,14 +140,14 @@ func getPruningGracePeriod(istio *v1alpha1.Istio) time.Duration { func (r *Reconciler) getActiveRevision(ctx context.Context, istio *v1alpha1.Istio) (v1alpha1.IstioRevision, error) { rev := v1alpha1.IstioRevision{} - err := r.Client.Get(ctx, getActiveRevisionKey(istio), &rev) + err := r.Client.Get(ctx, GetActiveRevisionKey(istio), &rev) if err != nil { return rev, fmt.Errorf("get failed: %w", err) } return rev, nil } -func getActiveRevisionKey(istio *v1alpha1.Istio) types.NamespacedName { +func GetActiveRevisionKey(istio *v1alpha1.Istio) types.NamespacedName { return types.NamespacedName{ Name: getActiveRevisionName(istio), } @@ -324,6 +323,8 @@ func convertConditionReason(reason v1alpha1.IstioRevisionConditionReason) v1alph return v1alpha1.IstioReasonReadinessCheckFailed case v1alpha1.IstioRevisionReasonReconcileError: return v1alpha1.IstioReasonReconcileError + case v1alpha1.IstioRevisionReasonRemoteIstiodNotReady: + return v1alpha1.IstioReasonRemoteIstiodNotReady default: panic(fmt.Sprintf("can't convert IstioRevisionConditionReason: %s", reason)) } diff --git a/controllers/istio/istio_controller_test.go b/controllers/istio/istio_controller_test.go index f91a9db54..129acf0ea 100644 --- a/controllers/istio/istio_controller_test.go +++ b/controllers/istio/istio_controller_test.go @@ -527,7 +527,6 @@ func TestDetermineStatus(t *testing.T) { initObjs := []client.Object{istio} for _, rev := range tc.revisions { - rev := rev initObjs = append(initObjs, &rev) } @@ -728,7 +727,6 @@ func TestUpdateStatus(t *testing.T) { initObjs := []client.Object{istio} for _, rev := range tc.revisions { - rev := rev initObjs = append(initObjs, &rev) } diff --git a/controllers/istiocni/istiocni_controller.go b/controllers/istiocni/istiocni_controller.go index d8b031b1f..23f558083 100644 --- a/controllers/istiocni/istiocni_controller.go +++ b/controllers/istiocni/istiocni_controller.go @@ -30,6 +30,7 @@ import ( "github.com/istio-ecosystem/sail-operator/pkg/helm" "github.com/istio-ecosystem/sail-operator/pkg/istiovalues" "github.com/istio-ecosystem/sail-operator/pkg/kube" + "github.com/istio-ecosystem/sail-operator/pkg/predicate" "github.com/istio-ecosystem/sail-operator/pkg/reconciler" "github.com/istio-ecosystem/sail-operator/pkg/validation" appsv1 "k8s.io/api/apps/v1" @@ -40,6 +41,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" 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" @@ -225,7 +227,11 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error { Watches(&corev1.ConfigMap{}, ownedResourceHandler). Watches(&appsv1.DaemonSet{}, ownedResourceHandler). Watches(&corev1.ResourceQuota{}, ownedResourceHandler). - Watches(&corev1.ServiceAccount{}, ownedResourceHandler). + + // We use predicate.IgnoreUpdate() so that we skip the reconciliation when a pull secret is added to the ServiceAccount. + // This is necessary so that we don't remove the newly-added secret. + // TODO: this is a temporary hack until we implement the correct solution on the Helm-render side + Watches(&corev1.ServiceAccount{}, ownedResourceHandler, builder.WithPredicates(predicate.IgnoreUpdate())). // TODO: only register NetAttachDef if the CRD is installed (may also need to watch for CRD creation) // Owns(&multusv1.NetworkAttachmentDefinition{}). diff --git a/controllers/istiorevision/istiorevision_controller.go b/controllers/istiorevision/istiorevision_controller.go index 13d162684..716fa530c 100644 --- a/controllers/istiorevision/istiorevision_controller.go +++ b/controllers/istiorevision/istiorevision_controller.go @@ -30,7 +30,9 @@ import ( "github.com/istio-ecosystem/sail-operator/pkg/errlist" "github.com/istio-ecosystem/sail-operator/pkg/helm" "github.com/istio-ecosystem/sail-operator/pkg/kube" + predicate2 "github.com/istio-ecosystem/sail-operator/pkg/predicate" "github.com/istio-ecosystem/sail-operator/pkg/reconciler" + "github.com/istio-ecosystem/sail-operator/pkg/revision" "github.com/istio-ecosystem/sail-operator/pkg/validation" admissionv1 "k8s.io/api/admissionregistration/v1" appsv1 "k8s.io/api/apps/v1" @@ -55,16 +57,6 @@ import ( "istio.io/istio/pkg/ptr" ) -const ( - IstioInjectionLabel = "istio-injection" - IstioInjectionEnabledValue = "enabled" - IstioRevLabel = "istio.io/rev" - IstioSidecarInjectLabel = "sidecar.istio.io/inject" - - istiodChartName = "istiod" - istiodRemoteChartName = "istiod-remote" -) - // Reconciler reconciles an IstioRevision object type Reconciler struct { client.Client @@ -128,9 +120,6 @@ func (r *Reconciler) Finalize(ctx context.Context, rev *v1alpha1.IstioRevision) } func (r *Reconciler) validate(ctx context.Context, rev *v1alpha1.IstioRevision) error { - if rev.Spec.Type == "" { - return reconciler.NewValidationError("spec.type not set") - } if rev.Spec.Version == "" { return reconciler.NewValidationError("spec.version not set") } @@ -155,6 +144,11 @@ func (r *Reconciler) validate(ctx context.Context, rev *v1alpha1.IstioRevision) if rev.Spec.Values.Global == nil || rev.Spec.Values.Global.IstioNamespace == nil || *rev.Spec.Values.Global.IstioNamespace != rev.Spec.Namespace { return reconciler.NewValidationError("spec.values.global.istioNamespace does not match spec.namespace") } + + if tagExists, err := validation.IstioRevisionTagExists(ctx, r.Client, rev.Name); tagExists || err != nil { + return reconciler.NewValidationError("an IstioRevisionTag exists with this name") + } + return nil } @@ -172,33 +166,22 @@ func (r *Reconciler) installHelmCharts(ctx context.Context, rev *v1alpha1.IstioR _, err := r.ChartManager.UpgradeOrInstallChart(ctx, r.getChartDir(rev), values, rev.Spec.Namespace, getReleaseName(rev), ownerReference) if err != nil { - return fmt.Errorf("failed to install/update Helm chart %q: %w", getChartName(rev), err) + return fmt.Errorf("failed to install/update Helm chart %q: %w", constants.IstiodChartName, err) } return nil } func getReleaseName(rev *v1alpha1.IstioRevision) string { - return fmt.Sprintf("%s-%s", rev.Name, getChartName(rev)) + return fmt.Sprintf("%s-%s", rev.Name, constants.IstiodChartName) } func (r *Reconciler) getChartDir(rev *v1alpha1.IstioRevision) string { - return path.Join(r.Config.ResourceDirectory, rev.Spec.Version, "charts", getChartName(rev)) -} - -func getChartName(rev *v1alpha1.IstioRevision) string { - switch rev.Spec.Type { - case v1alpha1.IstioRevisionTypeLocal: - return istiodChartName - case v1alpha1.IstioRevisionTypeRemote: - return istiodRemoteChartName - default: - panic(badIstioRevisionType(rev)) - } + return path.Join(r.Config.ResourceDirectory, rev.Spec.Version, "charts", constants.IstiodChartName) } func (r *Reconciler) uninstallHelmCharts(ctx context.Context, rev *v1alpha1.IstioRevision) error { if _, err := r.ChartManager.UninstallChart(ctx, getReleaseName(rev), rev.Spec.Namespace); err != nil { - return fmt.Errorf("failed to uninstall Helm chart %q: %w", getChartName(rev), err) + return fmt.Errorf("failed to uninstall Helm chart %q: %w", constants.IstiodChartName, err) } return nil } @@ -226,6 +209,10 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error { // The handler triggers the reconciliation of the referenced IstioRevision CR so that its InUse condition is updated. podHandler := wrapEventHandler(logger, handler.EnqueueRequestsFromMapFunc(r.mapPodToReconcileRequest)) + // revisionTagHandler handles IstioRevisionTags that reference the IstioRevision CR via their targetRef. + // The handler triggers the reconciliation of the referenced IstioRevision CR so that its InUse condition is updated. + revisionTagHandler := wrapEventHandler(logger, handler.EnqueueRequestsFromMapFunc(r.mapRevisionTagToReconcileRequest)) + return ctrl.NewControllerManagedBy(mgr). WithOptions(controller.Options{ LogConstructor: func(req *reconcile.Request) logr.Logger { @@ -246,7 +233,11 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error { Watches(&appsv1.Deployment{}, ownedResourceHandler). // we don't ignore the status here because we use it to calculate the IstioRevision status Watches(&corev1.Endpoints{}, ownedResourceHandler). Watches(&corev1.Service{}, ownedResourceHandler, builder.WithPredicates(ignoreStatusChange())). - Watches(&corev1.ServiceAccount{}, ownedResourceHandler). + + // We use predicate.IgnoreUpdate() so that we skip the reconciliation when a pull secret is added to the ServiceAccount. + // This is necessary so that we don't remove the newly-added secret. + // TODO: this is a temporary hack until we implement the correct solution on the Helm-render side + Watches(&corev1.ServiceAccount{}, ownedResourceHandler, builder.WithPredicates(predicate2.IgnoreUpdate())). Watches(&rbacv1.Role{}, ownedResourceHandler). Watches(&rbacv1.RoleBinding{}, ownedResourceHandler). Watches(&policyv1.PodDisruptionBudget{}, ownedResourceHandler, builder.WithPredicates(ignoreStatusChange())). @@ -258,6 +249,9 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error { // +lint-watches:ignore: Pod (not found in charts, but must be watched to reconcile IstioRevision when a pod references it) Watches(&corev1.Pod{}, podHandler, builder.WithPredicates(ignoreStatusChange())). + // +lint-watches:ignore: IstioRevisionTag (not found in charts, but must be watched to reconcile IstioRevision when a pod references it) + Watches(&v1alpha1.IstioRevisionTag{}, revisionTagHandler). + // cluster-scoped resources Watches(&rbacv1.ClusterRole{}, ownedResourceHandler). Watches(&rbacv1.ClusterRoleBinding{}, ownedResourceHandler). @@ -332,8 +326,7 @@ func (r *Reconciler) determineReadyCondition(ctx context.Context, rev *v1alpha1. Status: metav1.ConditionFalse, } - switch rev.Spec.Type { - case v1alpha1.IstioRevisionTypeLocal: + if !revision.IsUsingRemoteControlPlane(rev) { istiod := appsv1.Deployment{} if err := r.Client.Get(ctx, istiodDeploymentKey(rev), &istiod); err == nil { if istiod.Status.Replicas == 0 { @@ -354,7 +347,7 @@ func (r *Reconciler) determineReadyCondition(ctx context.Context, rev *v1alpha1. c.Message = fmt.Sprintf("failed to get readiness: %v", err) return c, fmt.Errorf("get failed: %w", err) } - case v1alpha1.IstioRevisionTypeRemote: + } else { webhook := admissionv1.MutatingWebhookConfiguration{} webhookKey := injectionWebhookKey(rev) if err := r.Client.Get(ctx, webhookKey, &webhook); err == nil { @@ -378,8 +371,6 @@ func (r *Reconciler) determineReadyCondition(ctx context.Context, rev *v1alpha1. c.Message = fmt.Sprintf("failed to get readiness: %v", err) return c, fmt.Errorf("get failed: %w", err) } - default: - panic(badIstioRevisionType(rev)) } return c, nil } @@ -387,7 +378,7 @@ func (r *Reconciler) determineReadyCondition(ctx context.Context, rev *v1alpha1. func (r *Reconciler) determineInUseCondition(ctx context.Context, rev *v1alpha1.IstioRevision) (v1alpha1.IstioRevisionCondition, error) { c := v1alpha1.IstioRevisionCondition{Type: v1alpha1.IstioRevisionConditionInUse} - isReferenced, err := r.isRevisionReferencedByWorkloads(ctx, rev) + isReferenced, err := r.isRevisionReferenced(ctx, rev) if err == nil { if isReferenced { c.Status = metav1.ConditionTrue @@ -406,10 +397,22 @@ func (r *Reconciler) determineInUseCondition(ctx context.Context, rev *v1alpha1. return c, fmt.Errorf("failed to determine if IstioRevision is in use: %w", err) } -func (r *Reconciler) isRevisionReferencedByWorkloads(ctx context.Context, rev *v1alpha1.IstioRevision) (bool, error) { +func (r *Reconciler) isRevisionReferenced(ctx context.Context, rev *v1alpha1.IstioRevision) (bool, error) { log := logf.FromContext(ctx) nsList := corev1.NamespaceList{} nsMap := map[string]corev1.Namespace{} + // if an IstioRevision is referenced by a revisionTag, it's considered as InUse + revisionTagList := v1alpha1.IstioRevisionTagList{} + if err := r.Client.List(ctx, &revisionTagList); err != nil { + return false, fmt.Errorf("failed to list IstioRevisionTags: %w", err) + } + for _, tag := range revisionTagList.Items { + if tag.Status.IstioRevision == rev.Name { + log.V(2).Info("Revision is referenced by IstioRevisionTag", "IstioRevisionTag", tag.Name) + return true, nil + } + } + if err := r.Client.List(ctx, &nsList); err != nil { // TODO: can we optimize this by specifying a label selector return false, fmt.Errorf("failed to list namespaces: %w", err) } @@ -452,10 +455,10 @@ func podReferencesRevision(pod corev1.Pod, ns corev1.Namespace, rev *v1alpha1.Is } func getReferencedRevisionFromNamespace(labels map[string]string) string { - if labels[IstioInjectionLabel] == IstioInjectionEnabledValue { + if labels[constants.IstioInjectionLabel] == constants.IstioInjectionEnabledValue { return v1alpha1.DefaultRevision } - revision := labels[IstioRevLabel] + revision := labels[constants.IstioRevLabel] if revision != "" { return revision } @@ -466,21 +469,21 @@ func getReferencedRevisionFromNamespace(labels map[string]string) string { func getReferencedRevisionFromPod(podLabels, podAnnotations, nsLabels map[string]string) string { // if pod was already injected, the revision that did the injection is specified in the istio.io/rev annotation - revision := podAnnotations[IstioRevLabel] + revision := podAnnotations[constants.IstioRevLabel] if revision != "" { return revision } // pod is marked for injection by a specific revision, but wasn't injected (e.g. because it was created before the revision was applied) revisionFromNamespace := getReferencedRevisionFromNamespace(nsLabels) - if podLabels[IstioSidecarInjectLabel] != "false" { + if podLabels[constants.IstioSidecarInjectLabel] != "false" { if revisionFromNamespace != "" { return revisionFromNamespace } - revisionFromPod := podLabels[IstioRevLabel] + revisionFromPod := podLabels[constants.IstioRevLabel] if revisionFromPod != "" { return revisionFromPod - } else if podLabels[IstioSidecarInjectLabel] == "true" { + } else if podLabels[constants.IstioSidecarInjectLabel] == "true" { return v1alpha1.DefaultRevision } } @@ -551,6 +554,14 @@ func (r *Reconciler) mapPodToReconcileRequest(ctx context.Context, pod client.Ob return nil } +func (r *Reconciler) mapRevisionTagToReconcileRequest(ctx context.Context, revisionTag client.Object) []reconcile.Request { + tag, ok := revisionTag.(*v1alpha1.IstioRevisionTag) + if ok && tag.Status.IstioRevision != "" { + return []reconcile.Request{{NamespacedName: types.NamespacedName{Name: tag.Status.IstioRevision}}} + } + return nil +} + // ignoreStatusChange returns a predicate that ignores watch events where only the resource status changes; if // there are any other changes to the resource, the event is not ignored. // This ensures that the controller doesn't reconcile the entire IstioRevision every time the status of an owned @@ -610,10 +621,6 @@ func clearIgnoredFields(obj client.Object) { } } -func badIstioRevisionType(rev *v1alpha1.IstioRevision) string { - return fmt.Sprintf("unknown IstioRevisionType: %s", rev.Spec.Type) -} - func wrapEventHandler(logger logr.Logger, handler handler.EventHandler) handler.EventHandler { return enqueuelogger.WrapIfNecessary(v1alpha1.IstioRevisionKind, logger, handler) } diff --git a/controllers/istiorevision/istiorevision_controller_test.go b/controllers/istiorevision/istiorevision_controller_test.go index c27b76129..7c2f21b97 100644 --- a/controllers/istiorevision/istiorevision_controller_test.go +++ b/controllers/istiorevision/istiorevision_controller_test.go @@ -60,7 +60,6 @@ func TestValidate(t *testing.T) { Name: "default", }, Spec: v1alpha1.IstioRevisionSpec{ - Type: v1alpha1.IstioRevisionTypeLocal, Version: supportedversion.Default, Namespace: "istio-system", Values: &v1alpha1.Values{ @@ -73,25 +72,6 @@ func TestValidate(t *testing.T) { objects: []client.Object{ns}, expectErr: "", }, - { - name: "no type", - rev: &v1alpha1.IstioRevision{ - ObjectMeta: metav1.ObjectMeta{ - Name: "default", - }, - Spec: v1alpha1.IstioRevisionSpec{ - Version: supportedversion.Default, - Namespace: "istio-system", - Values: &v1alpha1.Values{ - Global: &v1alpha1.GlobalConfig{ - IstioNamespace: ptr.Of("istio-system"), - }, - }, - }, - }, - objects: []client.Object{ns}, - expectErr: `spec.type not set`, - }, { name: "no version", rev: &v1alpha1.IstioRevision{ @@ -99,7 +79,6 @@ func TestValidate(t *testing.T) { Name: "default", }, Spec: v1alpha1.IstioRevisionSpec{ - Type: v1alpha1.IstioRevisionTypeLocal, Namespace: "istio-system", }, }, @@ -113,7 +92,6 @@ func TestValidate(t *testing.T) { Name: "default", }, Spec: v1alpha1.IstioRevisionSpec{ - Type: v1alpha1.IstioRevisionTypeLocal, Version: supportedversion.Default, }, }, @@ -127,7 +105,6 @@ func TestValidate(t *testing.T) { Name: "default", }, Spec: v1alpha1.IstioRevisionSpec{ - Type: v1alpha1.IstioRevisionTypeLocal, Version: supportedversion.Default, Namespace: "istio-system", }, @@ -142,7 +119,6 @@ func TestValidate(t *testing.T) { Name: "default", }, Spec: v1alpha1.IstioRevisionSpec{ - Type: v1alpha1.IstioRevisionTypeLocal, Version: supportedversion.Default, Namespace: "istio-system", }, @@ -157,7 +133,6 @@ func TestValidate(t *testing.T) { Name: "default", }, Spec: v1alpha1.IstioRevisionSpec{ - Type: v1alpha1.IstioRevisionTypeLocal, Version: supportedversion.Default, Namespace: "istio-system", Values: &v1alpha1.Values{ @@ -177,7 +152,6 @@ func TestValidate(t *testing.T) { Name: "default", }, Spec: v1alpha1.IstioRevisionSpec{ - Type: v1alpha1.IstioRevisionTypeLocal, Version: supportedversion.Default, Namespace: "istio-system", Values: &v1alpha1.Values{ @@ -198,7 +172,6 @@ func TestValidate(t *testing.T) { Name: "my-revision", }, Spec: v1alpha1.IstioRevisionSpec{ - Type: v1alpha1.IstioRevisionTypeLocal, Version: supportedversion.Default, Namespace: "istio-system", Values: &v1alpha1.Values{ @@ -293,7 +266,6 @@ func TestDetermineReadyCondition(t *testing.T) { testCases := []struct { name string - revType v1alpha1.IstioRevisionType values *v1alpha1.Values clientObjects []client.Object interceptors interceptor.Funcs @@ -418,9 +390,8 @@ func TestDetermineReadyCondition(t *testing.T) { expectErr: true, }, { - name: "Istiod-remote ready", - revType: v1alpha1.IstioRevisionTypeRemote, - values: nil, + name: "Istiod-remote ready", + values: &v1alpha1.Values{Profile: ptr.Of("remote")}, clientObjects: []client.Object{ &admissionv1.MutatingWebhookConfiguration{ ObjectMeta: metav1.ObjectMeta{ @@ -437,9 +408,8 @@ func TestDetermineReadyCondition(t *testing.T) { }, }, { - name: "Istiod-remote not ready", - revType: v1alpha1.IstioRevisionTypeRemote, - values: nil, + name: "Istiod-remote not ready", + values: &v1alpha1.Values{Profile: ptr.Of("remote")}, clientObjects: []client.Object{ &admissionv1.MutatingWebhookConfiguration{ ObjectMeta: metav1.ObjectMeta{ @@ -458,9 +428,8 @@ func TestDetermineReadyCondition(t *testing.T) { }, }, { - name: "Istiod-remote no readiness probe status annotation", - revType: v1alpha1.IstioRevisionTypeRemote, - values: nil, + name: "Istiod-remote no readiness probe status annotation", + values: &v1alpha1.Values{Profile: ptr.Of("remote")}, clientObjects: []client.Object{ &admissionv1.MutatingWebhookConfiguration{ ObjectMeta: metav1.ObjectMeta{ @@ -478,8 +447,7 @@ func TestDetermineReadyCondition(t *testing.T) { }, { name: "Istiod-remote webhook config not found", - revType: v1alpha1.IstioRevisionTypeRemote, - values: nil, + values: &v1alpha1.Values{Profile: ptr.Of("remote")}, clientObjects: []client.Object{}, expected: v1alpha1.IstioRevisionCondition{ Type: v1alpha1.IstioRevisionConditionReady, @@ -490,7 +458,7 @@ func TestDetermineReadyCondition(t *testing.T) { }, { name: "Istiod-remote client error on get", - revType: v1alpha1.IstioRevisionTypeRemote, + values: &v1alpha1.Values{Profile: ptr.Of("remote")}, clientObjects: []client.Object{}, interceptors: interceptor.Funcs{ Get: func(_ context.Context, _ client.WithWatch, _ client.ObjectKey, obj client.Object, _ ...client.GetOption) error { @@ -515,16 +483,12 @@ func TestDetermineReadyCondition(t *testing.T) { r := NewReconciler(cfg, cl, scheme.Scheme, nil) - if tt.revType == "" { - tt.revType = v1alpha1.IstioRevisionTypeLocal - } rev := &v1alpha1.IstioRevision{ ObjectMeta: metav1.ObjectMeta{ Name: "my-istio", }, Spec: v1alpha1.IstioRevisionSpec{ Namespace: "istio-system", - Type: tt.revType, Values: tt.values, }, } diff --git a/controllers/istiorevisiontag/istiorevisiontag_controller.go b/controllers/istiorevisiontag/istiorevisiontag_controller.go new file mode 100644 index 000000000..d42c8cf71 --- /dev/null +++ b/controllers/istiorevisiontag/istiorevisiontag_controller.go @@ -0,0 +1,547 @@ +// Copyright Istio Authors +// +// 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 istiorevisiontag + +import ( + "context" + "errors" + "fmt" + "path" + "reflect" + + "github.com/go-logr/logr" + "github.com/istio-ecosystem/sail-operator/api/v1alpha1" + "github.com/istio-ecosystem/sail-operator/pkg/config" + "github.com/istio-ecosystem/sail-operator/pkg/constants" + "github.com/istio-ecosystem/sail-operator/pkg/enqueuelogger" + "github.com/istio-ecosystem/sail-operator/pkg/errlist" + "github.com/istio-ecosystem/sail-operator/pkg/helm" + "github.com/istio-ecosystem/sail-operator/pkg/kube" + "github.com/istio-ecosystem/sail-operator/pkg/reconciler" + admissionv1 "k8s.io/api/admissionregistration/v1" + autoscalingv2 "k8s.io/api/autoscaling/v2" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + 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/event" + "sigs.k8s.io/controller-runtime/pkg/handler" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/predicate" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + "istio.io/istio/pkg/ptr" +) + +const ( + revisionTagsChartName = "revisiontags" +) + +// Reconciler reconciles an IstioRevisionTag object +type Reconciler struct { + client.Client + Scheme *runtime.Scheme + Config config.ReconcilerConfig + ChartManager *helm.ChartManager +} + +func NewReconciler(reconcilerCfg config.ReconcilerConfig, client client.Client, scheme *runtime.Scheme, chartManager *helm.ChartManager) *Reconciler { + return &Reconciler{ + Client: client, + Scheme: scheme, + Config: reconcilerCfg, + ChartManager: chartManager, + } +} + +// +kubebuilder:rbac:groups=sailoperator.io,resources=istiorevisiontags,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=sailoperator.io,resources=istiorevisiontags/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=sailoperator.io,resources=istiorevisiontags/finalizers,verbs=update +// +kubebuilder:rbac:groups="admissionregistration.k8s.io",resources=mutatingwebhookconfigurations,verbs="*" +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.14.1/pkg/reconcile +func (r *Reconciler) Reconcile(ctx context.Context, tag *v1alpha1.IstioRevisionTag) (ctrl.Result, error) { + log := logf.FromContext(ctx).WithValues("IstioRevisionTag", tag.Name) + + rev, reconcileErr := r.doReconcile(ctx, tag) + + log.Info("Reconciliation done. Updating status.") + statusErr := r.updateStatus(ctx, tag, rev, reconcileErr) + + reconcileErr = errors.Unwrap(reconcileErr) + + return ctrl.Result{}, errors.Join(reconcileErr, statusErr) +} + +func (r *Reconciler) doReconcile(ctx context.Context, tag *v1alpha1.IstioRevisionTag) (*v1alpha1.IstioRevision, error) { + log := logf.FromContext(ctx).WithValues("IstioRevisionTag", tag.Name) + if err := r.validate(ctx, tag); err != nil { + return nil, err + } + + log.Info("Retrieving referenced IstioRevision for IstioRevisionTag") + rev, err := r.getIstioRevision(ctx, tag.Spec.TargetRef) + if rev == nil || err != nil { + return nil, err + } + + log.Info("Installing Helm chart") + return rev, r.installHelmCharts(ctx, tag, rev) +} + +func (r *Reconciler) Finalize(ctx context.Context, tag *v1alpha1.IstioRevisionTag) error { + return r.uninstallHelmCharts(ctx, tag) +} + +func (r *Reconciler) validate(ctx context.Context, tag *v1alpha1.IstioRevisionTag) error { + if tag.Spec.TargetRef.Kind == "" || tag.Spec.TargetRef.Name == "" { + return reconciler.NewValidationError("spec.targetRef not set") + } + rev := v1alpha1.IstioRevision{} + if err := r.Client.Get(ctx, types.NamespacedName{Name: tag.Name}, &rev); err == nil { + return NewNameAlreadyExistsError("there is an IstioRevision with this name", err) + } else if !apierrors.IsNotFound(err) { + return err + } + if tag.Spec.TargetRef.Kind == v1alpha1.IstioKind { + i := v1alpha1.Istio{} + if err := r.Client.Get(ctx, types.NamespacedName{Name: tag.Spec.TargetRef.Name}, &i); err != nil { + if apierrors.IsNotFound(err) { + return NewReferenceNotFoundError("referenced Istio resource does not exist", err) + } + return reconciler.NewValidationError("failed to get referenced Istio resource: " + err.Error()) + } + } else if tag.Spec.TargetRef.Kind == v1alpha1.IstioRevisionKind { + if err := r.Client.Get(ctx, types.NamespacedName{Name: tag.Spec.TargetRef.Name}, &rev); err != nil { + if apierrors.IsNotFound(err) { + return NewReferenceNotFoundError("referenced IstioRevision resource does not exist", err) + } + return reconciler.NewValidationError("failed to get referenced IstioRevision resource: " + err.Error()) + } + } + return nil +} + +func (r *Reconciler) getIstioRevision(ctx context.Context, ref v1alpha1.IstioRevisionTagTargetReference) (*v1alpha1.IstioRevision, error) { + var revisionName string + if ref.Kind == v1alpha1.IstioRevisionKind { + revisionName = ref.Name + } else if ref.Kind == v1alpha1.IstioKind { + i := v1alpha1.Istio{} + err := r.Client.Get(ctx, types.NamespacedName{Name: ref.Name}, &i) + if err != nil { + return nil, err + } + if i.Status.ActiveRevisionName == "" { + return nil, reconciler.NewTransientError("referenced Istio has no active revision") + } + revisionName = i.Status.ActiveRevisionName + } else { + return nil, reconciler.NewValidationError("unknown targetRef.kind") + } + + rev := v1alpha1.IstioRevision{} + err := r.Client.Get(ctx, types.NamespacedName{Name: revisionName}, &rev) + if err != nil { + return nil, err + } + return &rev, nil +} + +func (r *Reconciler) installHelmCharts(ctx context.Context, tag *v1alpha1.IstioRevisionTag, rev *v1alpha1.IstioRevision) error { + ownerReference := metav1.OwnerReference{ + APIVersion: v1alpha1.GroupVersion.String(), + Kind: v1alpha1.IstioRevisionTagKind, + Name: tag.Name, + UID: tag.UID, + Controller: ptr.Of(true), + BlockOwnerDeletion: ptr.Of(true), + } + + values := helm.FromValues(rev.Spec.Values) + if err := values.SetStringSlice("revisionTags", []string{tag.Name}); err != nil { + return err + } + + _, err := r.ChartManager.UpgradeOrInstallChart(ctx, r.getChartDir(rev), + values, rev.Spec.Namespace, getReleaseName(tag), ownerReference) + if err != nil { + return fmt.Errorf("failed to install/update Helm chart %q: %w", revisionTagsChartName, err) + } + return nil +} + +func getReleaseName(tag *v1alpha1.IstioRevisionTag) string { + return fmt.Sprintf("%s-%s", tag.Name, revisionTagsChartName) +} + +func (r *Reconciler) getChartDir(tag *v1alpha1.IstioRevision) string { + return path.Join(r.Config.ResourceDirectory, tag.Spec.Version, "charts", revisionTagsChartName) +} + +func (r *Reconciler) uninstallHelmCharts(ctx context.Context, tag *v1alpha1.IstioRevisionTag) error { + if _, err := r.ChartManager.UninstallChart(ctx, getReleaseName(tag), tag.Status.IstiodNamespace); err != nil { + return fmt.Errorf("failed to uninstall Helm chart %q: %w", revisionTagsChartName, err) + } + return nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error { + logger := mgr.GetLogger().WithName("ctrlr").WithName("revtag") + + // mainObjectHandler handles the IstioRevisionTag watch events + mainObjectHandler := wrapEventHandler(logger, &handler.EnqueueRequestForObject{}) + + // ownedResourceHandler handles resources that are owned by the IstioRevisionTag CR + ownedResourceHandler := wrapEventHandler( + logger, handler.EnqueueRequestForOwner(r.Scheme, r.RESTMapper(), &v1alpha1.IstioRevisionTag{}, handler.OnlyControllerOwner())) + + // operatorResourcesHandler handles watch events from operator CRDs Istio and IstioRevision + operatorResourcesHandler := wrapEventHandler(logger, handler.EnqueueRequestsFromMapFunc(r.mapOperatorResourceToReconcileRequest)) + // nsHandler triggers reconciliation in two cases: + // - when a namespace that references the IstioRevisionTag CR via the istio.io/rev + // or istio-injection labels is updated, so that the InUse condition of + // the IstioRevisionTag CR is updated. + nsHandler := wrapEventHandler(logger, handler.EnqueueRequestsFromMapFunc(r.mapNamespaceToReconcileRequest)) + + // podHandler handles pods that reference the IstioRevisionTag CR via the istio.io/rev or sidecar.istio.io/inject labels. + // The handler triggers the reconciliation of the referenced IstioRevision CR so that its InUse condition is updated. + podHandler := wrapEventHandler(logger, handler.EnqueueRequestsFromMapFunc(r.mapPodToReconcileRequest)) + + return ctrl.NewControllerManagedBy(mgr). + WithOptions(controller.Options{ + LogConstructor: func(req *reconcile.Request) logr.Logger { + log := logger + if req != nil { + log = log.WithValues("IstioRevisionTag", req.Name) + } + return log + }, + }). + // we use the Watches function instead of For(), so that we can wrap the handler so that events that cause the object to be enqueued are logged + Watches(&v1alpha1.IstioRevisionTag{}, mainObjectHandler). + Named("istiorevisiontag"). + // watches related to in-use detection + Watches(&corev1.Namespace{}, nsHandler, builder.WithPredicates(ignoreStatusChange())). + Watches(&corev1.Pod{}, podHandler, builder.WithPredicates(ignoreStatusChange())). + + // cluster-scoped resources + Watches(&v1alpha1.Istio{}, operatorResourcesHandler). + Watches(&v1alpha1.IstioRevision{}, operatorResourcesHandler). + Watches(&admissionv1.MutatingWebhookConfiguration{}, ownedResourceHandler). + Complete(reconciler.NewStandardReconcilerWithFinalizer[*v1alpha1.IstioRevisionTag](r.Client, r.Reconcile, r.Finalize, constants.FinalizerName)) +} + +func (r *Reconciler) determineStatus(ctx context.Context, tag *v1alpha1.IstioRevisionTag, + rev *v1alpha1.IstioRevision, reconcileErr error, +) (v1alpha1.IstioRevisionTagStatus, error) { + var errs errlist.Builder + reconciledCondition := r.determineReconciledCondition(reconcileErr) + + inUseCondition, err := r.determineInUseCondition(ctx, tag) + errs.Add(err) + + status := *tag.Status.DeepCopy() + status.ObservedGeneration = tag.Generation + if reconciledCondition.Status == metav1.ConditionTrue && rev != nil { + status.IstiodNamespace = rev.Spec.Namespace + status.IstioRevision = rev.Name + } + status.SetCondition(reconciledCondition) + status.SetCondition(inUseCondition) + status.State = deriveState(reconciledCondition, inUseCondition) + return status, errs.Error() +} + +func (r *Reconciler) updateStatus(ctx context.Context, tag *v1alpha1.IstioRevisionTag, rev *v1alpha1.IstioRevision, reconcileErr error) error { + var errs errlist.Builder + + status, err := r.determineStatus(ctx, tag, rev, reconcileErr) + if err != nil { + errs.Add(fmt.Errorf("failed to determine status: %w", err)) + } + + if !reflect.DeepEqual(tag.Status, status) { + if err := r.Client.Status().Patch(ctx, tag, kube.NewStatusPatch(status)); err != nil { + errs.Add(fmt.Errorf("failed to patch status: %w", err)) + } + } + return errs.Error() +} + +func deriveState(reconciledCondition, inUseCondition v1alpha1.IstioRevisionTagCondition) v1alpha1.IstioRevisionTagConditionReason { + if reconciledCondition.Status != metav1.ConditionTrue { + return reconciledCondition.Reason + } + if inUseCondition.Status != metav1.ConditionTrue { + return inUseCondition.Reason + } + return v1alpha1.IstioRevisionTagReasonHealthy +} + +func (r *Reconciler) determineReconciledCondition(err error) v1alpha1.IstioRevisionTagCondition { + c := v1alpha1.IstioRevisionTagCondition{Type: v1alpha1.IstioRevisionTagConditionReconciled} + + if err == nil { + c.Status = metav1.ConditionTrue + } else if IsNameAlreadyExistsError(err) { + c.Status = metav1.ConditionFalse + c.Reason = v1alpha1.IstioRevisionTagReasonNameAlreadyExists + c.Message = err.Error() + } else if IsReferenceNotFoundError(err) { + c.Status = metav1.ConditionFalse + c.Reason = v1alpha1.IstioRevisionTagReasonReferenceNotFound + c.Message = err.Error() + } else { + c.Status = metav1.ConditionFalse + c.Reason = v1alpha1.IstioRevisionTagReasonReconcileError + c.Message = fmt.Sprintf("error reconciling resource: %v", err) + } + return c +} + +func (r *Reconciler) determineInUseCondition(ctx context.Context, tag *v1alpha1.IstioRevisionTag) (v1alpha1.IstioRevisionTagCondition, error) { + c := v1alpha1.IstioRevisionTagCondition{Type: v1alpha1.IstioRevisionTagConditionInUse} + + isReferenced, err := r.isRevisionTagReferencedByWorkloads(ctx, tag) + if err == nil { + if isReferenced { + c.Status = metav1.ConditionTrue + c.Reason = v1alpha1.IstioRevisionTagReasonReferencedByWorkloads + c.Message = "Referenced by at least one pod or namespace" + } else { + c.Status = metav1.ConditionFalse + c.Reason = v1alpha1.IstioRevisionTagReasonNotReferenced + c.Message = "Not referenced by any pod or namespace" + } + return c, nil + } + c.Status = metav1.ConditionUnknown + c.Reason = v1alpha1.IstioRevisionTagReasonUsageCheckFailed + c.Message = fmt.Sprintf("failed to determine if revision tag is in use: %v", err) + return c, fmt.Errorf("failed to determine if IstioRevisionTag is in use: %w", err) +} + +func (r *Reconciler) isRevisionTagReferencedByWorkloads(ctx context.Context, tag *v1alpha1.IstioRevisionTag) (bool, error) { + log := logf.FromContext(ctx) + nsList := corev1.NamespaceList{} + nsMap := map[string]corev1.Namespace{} + if err := r.Client.List(ctx, &nsList); err != nil { // TODO: can we optimize this by specifying a label selector + return false, fmt.Errorf("failed to list namespaces: %w", err) + } + for _, ns := range nsList.Items { + if namespaceReferencesRevisionTag(ns, tag) { + log.V(2).Info("RevisionTag is referenced by Namespace", "Namespace", ns.Name) + return true, nil + } + nsMap[ns.Name] = ns + } + + podList := corev1.PodList{} + if err := r.Client.List(ctx, &podList); err != nil { // TODO: can we optimize this by specifying a label selector + return false, fmt.Errorf("failed to list pods: %w", err) + } + for _, pod := range podList.Items { + if podReferencesRevisionTag(pod, tag) { + log.V(2).Info("RevisionTag is referenced by Pod", "Pod", client.ObjectKeyFromObject(&pod)) + return true, nil + } + } + + rev, err := r.getIstioRevision(ctx, tag.Spec.TargetRef) + if err != nil { + return false, err + } + + if tag.Name == v1alpha1.DefaultRevision && rev.Spec.Values != nil && + rev.Spec.Values.SidecarInjectorWebhook != nil && + rev.Spec.Values.SidecarInjectorWebhook.EnableNamespacesByDefault != nil && + *rev.Spec.Values.SidecarInjectorWebhook.EnableNamespacesByDefault { + return true, nil + } + + log.V(2).Info("RevisionTag is not referenced by any Pod or Namespace") + return false, nil +} + +func namespaceReferencesRevisionTag(ns corev1.Namespace, tag *v1alpha1.IstioRevisionTag) bool { + return tag.Name == getReferencedRevisionFromNamespace(ns.Labels) +} + +func podReferencesRevisionTag(pod corev1.Pod, tag *v1alpha1.IstioRevisionTag) bool { + return tag.Name == getReferencedRevisionTagFromPod(pod.GetLabels()) +} + +func getReferencedRevisionFromNamespace(labels map[string]string) string { + if labels[constants.IstioInjectionLabel] == constants.IstioInjectionEnabledValue { + return v1alpha1.DefaultRevision + } + revision := labels[constants.IstioRevLabel] + if revision != "" { + return revision + } + // TODO: if .Values.sidecarInjectorWebhook.enableNamespacesByDefault is true, then all namespaces except system namespaces should use the "default" revision + + return "" +} + +func getReferencedRevisionTagFromPod(podLabels map[string]string) string { + // we only look at pod labels to identify injection intent, as the annotation only references the real revision name instead of the tag + if podLabels[constants.IstioInjectionLabel] == constants.IstioInjectionEnabledValue { + return v1alpha1.DefaultRevision + } else if podLabels[constants.IstioSidecarInjectLabel] != "false" && podLabels[constants.IstioRevLabel] != "" { + return podLabels[constants.IstioRevLabel] + } + + return "" +} + +func (r *Reconciler) mapNamespaceToReconcileRequest(ctx context.Context, ns client.Object) []reconcile.Request { + var requests []reconcile.Request + + // Check if the namespace references an IstioRevisionTag in its labels + revision := getReferencedRevisionFromNamespace(ns.GetLabels()) + if revision != "" { + requests = append(requests, reconcile.Request{NamespacedName: types.NamespacedName{Name: revision}}) + } + return requests +} + +func (r *Reconciler) mapPodToReconcileRequest(ctx context.Context, pod client.Object) []reconcile.Request { + revision := getReferencedRevisionTagFromPod(pod.GetLabels()) + if revision != "" { + return []reconcile.Request{{NamespacedName: types.NamespacedName{Name: revision}}} + } + return nil +} + +func (r *Reconciler) mapOperatorResourceToReconcileRequest(ctx context.Context, obj client.Object) []reconcile.Request { + var revisionName string + if i, ok := obj.(*v1alpha1.Istio); ok && i.Status.ActiveRevisionName != "" { + revisionName = i.Status.ActiveRevisionName + } else if rev, ok := obj.(*v1alpha1.IstioRevision); ok { + revisionName = rev.Name + } else { + return nil + } + tags := v1alpha1.IstioRevisionTagList{} + err := r.Client.List(ctx, &tags, &client.ListOptions{}) + if err != nil { + return nil + } + requests := []reconcile.Request{} + for _, tag := range tags.Items { + if tag.Status.IstioRevision == revisionName { + requests = append(requests, reconcile.Request{NamespacedName: types.NamespacedName{Name: tag.Name}}) + } + } + return requests +} + +// ignoreStatusChange returns a predicate that ignores watch events where only the resource status changes; if +// there are any other changes to the resource, the event is not ignored. +// This ensures that the controller doesn't reconcile the entire IstioRevisionTag every time the status of an owned +// resource is updated. Without this predicate, the controller would continuously reconcile the IstioRevisionTag +// because the status.currentMetrics of the HorizontalPodAutoscaler object was updated. +func ignoreStatusChange() predicate.Funcs { + return predicate.Funcs{ + UpdateFunc: func(e event.UpdateEvent) bool { + return specWasUpdated(e.ObjectOld, e.ObjectNew) || + !reflect.DeepEqual(e.ObjectNew.GetLabels(), e.ObjectOld.GetLabels()) || + !reflect.DeepEqual(e.ObjectNew.GetAnnotations(), e.ObjectOld.GetAnnotations()) || + !reflect.DeepEqual(e.ObjectNew.GetOwnerReferences(), e.ObjectOld.GetOwnerReferences()) || + !reflect.DeepEqual(e.ObjectNew.GetFinalizers(), e.ObjectOld.GetFinalizers()) + }, + } +} + +func specWasUpdated(oldObject client.Object, newObject client.Object) bool { + // for HPAs, k8s doesn't set metadata.generation, so we actually have to check whether the spec was updated + if oldHpa, ok := oldObject.(*autoscalingv2.HorizontalPodAutoscaler); ok { + if newHpa, ok := newObject.(*autoscalingv2.HorizontalPodAutoscaler); ok { + return !reflect.DeepEqual(oldHpa.Spec, newHpa.Spec) + } + } + + // for other resources, comparing the metadata.generation suffices + return oldObject.GetGeneration() != newObject.GetGeneration() +} + +func wrapEventHandler(logger logr.Logger, handler handler.EventHandler) handler.EventHandler { + return enqueuelogger.WrapIfNecessary(v1alpha1.IstioRevisionTagKind, logger, handler) +} + +type NameAlreadyExistsError struct { + Message string + originalError error +} + +func (err NameAlreadyExistsError) Error() string { + return err.Message +} + +func (err NameAlreadyExistsError) Unwrap() error { + return err.originalError +} + +func NewNameAlreadyExistsError(message string, originalError error) NameAlreadyExistsError { + return NameAlreadyExistsError{ + Message: message, + originalError: originalError, + } +} + +func IsNameAlreadyExistsError(err error) bool { + if _, ok := err.(NameAlreadyExistsError); ok { + return true + } + return false +} + +type ReferenceNotFoundError struct { + Message string + originalError error +} + +func (err ReferenceNotFoundError) Error() string { + return err.Message +} + +func (err ReferenceNotFoundError) Unwrap() error { + return err.originalError +} + +func NewReferenceNotFoundError(message string, originalError error) ReferenceNotFoundError { + return ReferenceNotFoundError{ + Message: message, + originalError: originalError, + } +} + +func IsReferenceNotFoundError(err error) bool { + if _, ok := err.(ReferenceNotFoundError); ok { + return true + } + return false +} diff --git a/controllers/remoteistio/remoteistio_controller.go b/controllers/remoteistio/remoteistio_controller.go deleted file mode 100644 index fe92ebd00..000000000 --- a/controllers/remoteistio/remoteistio_controller.go +++ /dev/null @@ -1,315 +0,0 @@ -// Copyright Istio Authors -// -// 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 remoteistio - -import ( - "context" - "errors" - "fmt" - "reflect" - "strings" - "time" - - "github.com/go-logr/logr" - "github.com/istio-ecosystem/sail-operator/api/v1alpha1" - "github.com/istio-ecosystem/sail-operator/pkg/config" - "github.com/istio-ecosystem/sail-operator/pkg/errlist" - "github.com/istio-ecosystem/sail-operator/pkg/kube" - "github.com/istio-ecosystem/sail-operator/pkg/reconciler" - "github.com/istio-ecosystem/sail-operator/pkg/revision" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - - "istio.io/istio/pkg/ptr" -) - -// Reconciler reconciles a RemoteIstio object -type Reconciler struct { - Config config.ReconcilerConfig - client.Client - Scheme *runtime.Scheme -} - -func NewReconciler(cfg config.ReconcilerConfig, client client.Client, scheme *runtime.Scheme) *Reconciler { - return &Reconciler{ - Config: cfg, - Client: client, - Scheme: scheme, - } -} - -// +kubebuilder:rbac:groups=sailoperator.io,resources=remoteistios,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=sailoperator.io,resources=remoteistios/status,verbs=get;update;patch -// +kubebuilder:rbac:groups=sailoperator.io,resources=remoteistios/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. -// -// For more details, check Reconcile and its Result here: -// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.14.1/pkg/reconcile -func (r *Reconciler) Reconcile(ctx context.Context, istio *v1alpha1.RemoteIstio) (ctrl.Result, error) { - log := logf.FromContext(ctx) - - log.Info("Reconciling") - result, reconcileErr := r.doReconcile(ctx, istio) - - log.Info("Reconciliation done. Updating status.") - statusErr := r.updateStatus(ctx, istio, reconcileErr) - - return result, errors.Join(reconcileErr, statusErr) -} - -// doReconcile is the function that actually reconciles the Istio object. Any error reported by this -// function should get reported in the status of the Istio object by the caller. -func (r *Reconciler) doReconcile(ctx context.Context, istio *v1alpha1.RemoteIstio) (result ctrl.Result, err error) { - if err := validate(istio); err != nil { - return ctrl.Result{}, err - } - - if err = r.reconcileActiveRevision(ctx, istio); err != nil { - return ctrl.Result{}, err - } - - return revision.PruneInactive(ctx, r.Client, istio.UID, getActiveRevisionName(istio), getPruningGracePeriod(istio)) -} - -func validate(istio *v1alpha1.RemoteIstio) error { - if istio.Spec.Version == "" { - return reconciler.NewValidationError("spec.version not set") - } - if istio.Spec.Namespace == "" { - return reconciler.NewValidationError("spec.namespace not set") - } - return nil -} - -func (r *Reconciler) reconcileActiveRevision(ctx context.Context, istio *v1alpha1.RemoteIstio) error { - values, err := revision.ComputeValues( - istio.Spec.Values, istio.Spec.Namespace, istio.Spec.Version, - r.Config.Platform, r.Config.DefaultProfile, istio.Spec.Profile, - r.Config.ResourceDirectory, getActiveRevisionName(istio)) - if err != nil { - return err - } - - return revision.CreateOrUpdate(ctx, r.Client, - getActiveRevisionName(istio), - v1alpha1.IstioRevisionTypeRemote, - istio.Spec.Version, istio.Spec.Namespace, values, - metav1.OwnerReference{ - APIVersion: v1alpha1.GroupVersion.String(), - Kind: v1alpha1.RemoteIstioKind, - Name: istio.Name, - UID: istio.UID, - Controller: ptr.Of(true), - BlockOwnerDeletion: ptr.Of(true), - }) -} - -func getPruningGracePeriod(istio *v1alpha1.RemoteIstio) time.Duration { - strategy := istio.Spec.UpdateStrategy - period := int64(v1alpha1.DefaultRevisionDeletionGracePeriodSeconds) - if strategy != nil && strategy.InactiveRevisionDeletionGracePeriodSeconds != nil { - period = *strategy.InactiveRevisionDeletionGracePeriodSeconds - } - if period < v1alpha1.MinRevisionDeletionGracePeriodSeconds { - period = v1alpha1.MinRevisionDeletionGracePeriodSeconds - } - return time.Duration(period) * time.Second -} - -func (r *Reconciler) getActiveRevision(ctx context.Context, istio *v1alpha1.RemoteIstio) (v1alpha1.IstioRevision, error) { - rev := v1alpha1.IstioRevision{} - err := r.Client.Get(ctx, getActiveRevisionKey(istio), &rev) - if err != nil { - return rev, fmt.Errorf("get failed: %w", err) - } - return rev, nil -} - -func getActiveRevisionKey(istio *v1alpha1.RemoteIstio) types.NamespacedName { - return types.NamespacedName{ - Name: getActiveRevisionName(istio), - } -} - -func getActiveRevisionName(istio *v1alpha1.RemoteIstio) string { - var strategy v1alpha1.UpdateStrategyType - if istio.Spec.UpdateStrategy != nil { - strategy = istio.Spec.UpdateStrategy.Type - } - - switch strategy { - default: - fallthrough - case v1alpha1.UpdateStrategyTypeInPlace: - return istio.Name - case v1alpha1.UpdateStrategyTypeRevisionBased: - return istio.Name + "-" + strings.ReplaceAll(istio.Spec.Version, ".", "-") - } -} - -func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - WithOptions(controller.Options{ - LogConstructor: func(req *reconcile.Request) logr.Logger { - log := mgr.GetLogger().WithName("ctrlr").WithName("remoteistio") - if req != nil { - log = log.WithValues("RemoteIstio", req.Name) - } - return log - }, - }). - For(&v1alpha1.RemoteIstio{}). - Owns(&v1alpha1.IstioRevision{}). - Complete(reconciler.NewStandardReconciler[*v1alpha1.RemoteIstio](r.Client, r.Reconcile)) -} - -func (r *Reconciler) determineStatus(ctx context.Context, istio *v1alpha1.RemoteIstio, reconcileErr error) (v1alpha1.RemoteIstioStatus, error) { - var errs errlist.Builder - status := *istio.Status.DeepCopy() - status.ObservedGeneration = istio.Generation - - // set Reconciled and Ready conditions - if reconcileErr != nil { - status.SetCondition(v1alpha1.RemoteIstioCondition{ - Type: v1alpha1.RemoteIstioConditionReconciled, - Status: metav1.ConditionFalse, - Reason: v1alpha1.RemoteIstioReasonReconcileError, - Message: reconcileErr.Error(), - }) - status.SetCondition(v1alpha1.RemoteIstioCondition{ - Type: v1alpha1.RemoteIstioConditionReady, - Status: metav1.ConditionUnknown, - Reason: v1alpha1.RemoteIstioReasonReconcileError, - Message: "cannot determine readiness due to reconciliation error", - }) - status.State = v1alpha1.RemoteIstioReasonReconcileError - } else { - status.ActiveRevisionName = getActiveRevisionName(istio) - rev, err := r.getActiveRevision(ctx, istio) - if apierrors.IsNotFound(err) { - revisionNotFound := func(conditionType v1alpha1.RemoteIstioConditionType) v1alpha1.RemoteIstioCondition { - return v1alpha1.RemoteIstioCondition{ - Type: conditionType, - Status: metav1.ConditionFalse, - Reason: v1alpha1.RemoteIstioReasonRevisionNotFound, - Message: "active IstioRevision not found", - } - } - - status.SetCondition(revisionNotFound(v1alpha1.RemoteIstioConditionReconciled)) - status.SetCondition(revisionNotFound(v1alpha1.RemoteIstioConditionReady)) - status.State = v1alpha1.RemoteIstioReasonRevisionNotFound - } else if err == nil { - status.SetCondition(convertCondition(rev.Status.GetCondition(v1alpha1.IstioRevisionConditionReconciled))) - status.SetCondition(convertCondition(rev.Status.GetCondition(v1alpha1.IstioRevisionConditionReady))) - status.State = convertConditionReason(rev.Status.State) - } else { - activeRevisionGetFailed := func(conditionType v1alpha1.RemoteIstioConditionType) v1alpha1.RemoteIstioCondition { - return v1alpha1.RemoteIstioCondition{ - Type: conditionType, - Status: metav1.ConditionUnknown, - Reason: v1alpha1.RemoteIstioReasonFailedToGetActiveRevision, - Message: fmt.Sprintf("failed to get active IstioRevision: %s", err), - } - } - status.SetCondition(activeRevisionGetFailed(v1alpha1.RemoteIstioConditionReconciled)) - status.SetCondition(activeRevisionGetFailed(v1alpha1.RemoteIstioConditionReady)) - status.State = v1alpha1.RemoteIstioReasonFailedToGetActiveRevision - errs.Add(fmt.Errorf("failed to get active IstioRevision: %w", err)) - } - } - - // count the ready, in-use, and total revisions - if revs, err := revision.ListOwned(ctx, r.Client, istio.UID); err == nil { - status.Revisions.Total = int32(len(revs)) - status.Revisions.Ready = 0 - status.Revisions.InUse = 0 - for _, rev := range revs { - if rev.Status.GetCondition(v1alpha1.IstioRevisionConditionReady).Status == metav1.ConditionTrue { - status.Revisions.Ready++ - } - if rev.Status.GetCondition(v1alpha1.IstioRevisionConditionInUse).Status == metav1.ConditionTrue { - status.Revisions.InUse++ - } - } - } else { - status.Revisions.Total = -1 - status.Revisions.Ready = -1 - status.Revisions.InUse = -1 - errs.Add(err) - } - return status, errs.Error() -} - -func (r *Reconciler) updateStatus(ctx context.Context, istio *v1alpha1.RemoteIstio, reconcileErr error) error { - var errs errlist.Builder - status, err := r.determineStatus(ctx, istio, reconcileErr) - if err != nil { - errs.Add(fmt.Errorf("failed to determine status: %w", err)) - } - - if !reflect.DeepEqual(istio.Status, status) { - if err := r.Client.Status().Patch(ctx, istio, kube.NewStatusPatch(status)); err != nil { - errs.Add(fmt.Errorf("failed to patch status: %w", err)) - } - } - return errs.Error() -} - -func convertCondition(condition v1alpha1.IstioRevisionCondition) v1alpha1.RemoteIstioCondition { - return v1alpha1.RemoteIstioCondition{ - Type: convertConditionType(condition), - Status: condition.Status, - Reason: convertConditionReason(condition.Reason), - Message: condition.Message, - } -} - -func convertConditionType(condition v1alpha1.IstioRevisionCondition) v1alpha1.RemoteIstioConditionType { - switch condition.Type { - case v1alpha1.IstioRevisionConditionReconciled: - return v1alpha1.RemoteIstioConditionReconciled - case v1alpha1.IstioRevisionConditionReady: - return v1alpha1.RemoteIstioConditionReady - default: - panic(fmt.Sprintf("can't convert IstioRevisionConditionType: %s", condition.Type)) - } -} - -func convertConditionReason(reason v1alpha1.IstioRevisionConditionReason) v1alpha1.RemoteIstioConditionReason { - switch reason { - case "": - return "" - case v1alpha1.IstioRevisionReasonRemoteIstiodNotReady: - return v1alpha1.RemoteIstioReasonIstiodNotReady - case v1alpha1.IstioRevisionReasonHealthy: - return v1alpha1.RemoteIstioReasonHealthy - case v1alpha1.IstioRevisionReasonReadinessCheckFailed: - return v1alpha1.RemoteIstioReasonReadinessCheckFailed - case v1alpha1.IstioRevisionReasonReconcileError: - return v1alpha1.RemoteIstioReasonReconcileError - default: - panic(fmt.Sprintf("can't convert IstioRevisionConditionReason: %s", reason)) - } -} diff --git a/controllers/remoteistio/remoteistio_controller_test.go b/controllers/remoteistio/remoteistio_controller_test.go deleted file mode 100644 index 8b74879f2..000000000 --- a/controllers/remoteistio/remoteistio_controller_test.go +++ /dev/null @@ -1,927 +0,0 @@ -// Copyright Istio Authors -// -// 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 remoteistio - -import ( - "context" - "fmt" - "runtime/debug" - "testing" - "time" - - "github.com/google/go-cmp/cmp" - "github.com/istio-ecosystem/sail-operator/api/v1alpha1" - "github.com/istio-ecosystem/sail-operator/pkg/config" - "github.com/istio-ecosystem/sail-operator/pkg/scheme" - "github.com/istio-ecosystem/sail-operator/pkg/test/testtime" - "github.com/istio-ecosystem/sail-operator/pkg/test/util/supportedversion" - . "github.com/onsi/gomega" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/fake" - "sigs.k8s.io/controller-runtime/pkg/client/interceptor" - - "istio.io/istio/pkg/ptr" -) - -var ( - ctx = context.Background() - istioNamespace = "my-istio-namespace" - istioName = "my-istio" - istioKey = types.NamespacedName{ - Name: istioName, - } - istioUID = types.UID("my-istio-uid") - objectMeta = metav1.ObjectMeta{ - Name: istioKey.Name, - } -) - -func TestReconcile(t *testing.T) { - cfg := newReconcilerTestConfig(t) - - t.Run("returns error when Istio version not set", func(t *testing.T) { - istio := &v1alpha1.RemoteIstio{ - ObjectMeta: objectMeta, - } - - cl := newFakeClientBuilder(). - WithObjects(istio). - Build() - reconciler := NewReconciler(cfg, cl, scheme.Scheme) - - _, err := reconciler.Reconcile(ctx, istio) - if err == nil { - t.Errorf("Expected an error, but got nil") - } - - Must(t, cl.Get(ctx, istioKey, istio)) - - if istio.Status.State != v1alpha1.RemoteIstioReasonReconcileError { - t.Errorf("Expected status.state to be %q, but got %q", v1alpha1.RemoteIstioReasonReconcileError, istio.Status.State) - } - - reconciledCond := istio.Status.GetCondition(v1alpha1.RemoteIstioConditionReconciled) - if reconciledCond.Status != metav1.ConditionFalse { - t.Errorf("Expected Reconciled condition status to be %q, but got %q", metav1.ConditionFalse, reconciledCond.Status) - } - - readyCond := istio.Status.GetCondition(v1alpha1.RemoteIstioConditionReady) - if readyCond.Status != metav1.ConditionUnknown { - t.Errorf("Expected Reconciled condition status to be %q, but got %q", metav1.ConditionUnknown, readyCond.Status) - } - }) - - t.Run("returns error when computeIstioRevisionValues fails", func(t *testing.T) { - istio := &v1alpha1.RemoteIstio{ - ObjectMeta: objectMeta, - Spec: v1alpha1.RemoteIstioSpec{ - Version: "my-version", - }, - } - - cl := newFakeClientBuilder(). - WithStatusSubresource(&v1alpha1.RemoteIstio{}). - WithObjects(istio). - Build() - cfg := newReconcilerTestConfig(t) - cfg.DefaultProfile = "invalid-profile" - reconciler := NewReconciler(cfg, cl, scheme.Scheme) - - _, err := reconciler.Reconcile(ctx, istio) - if err == nil { - t.Errorf("Expected an error, but got nil") - } - - Must(t, cl.Get(ctx, istioKey, istio)) - - if istio.Status.State != v1alpha1.RemoteIstioReasonReconcileError { - t.Errorf("Expected status.state to be %q, but got %q", v1alpha1.RemoteIstioReasonReconcileError, istio.Status.State) - } - - reconciledCond := istio.Status.GetCondition(v1alpha1.RemoteIstioConditionReconciled) - if reconciledCond.Status != metav1.ConditionFalse { - t.Errorf("Expected Reconciled condition status to be %q, but got %q", metav1.ConditionFalse, reconciledCond.Status) - } - - readyCond := istio.Status.GetCondition(v1alpha1.RemoteIstioConditionReady) - if readyCond.Status != metav1.ConditionUnknown { - t.Errorf("Expected Reconciled condition status to be %q, but got %q", metav1.ConditionUnknown, readyCond.Status) - } - }) - - t.Run("returns error when reconcileActiveRevision fails", func(t *testing.T) { - istio := &v1alpha1.RemoteIstio{ - ObjectMeta: objectMeta, - Spec: v1alpha1.RemoteIstioSpec{ - Version: "my-version", - }, - } - - cl := newFakeClientBuilder(). - WithObjects(istio). - WithInterceptorFuncs(interceptor.Funcs{ - Create: func(_ context.Context, _ client.WithWatch, _ client.Object, _ ...client.CreateOption) error { - return fmt.Errorf("internal error") - }, - }). - Build() - reconciler := NewReconciler(cfg, cl, scheme.Scheme) - - _, err := reconciler.Reconcile(ctx, istio) - if err == nil { - t.Errorf("Expected an error, but got nil") - } - - Must(t, cl.Get(ctx, istioKey, istio)) - - if istio.Status.State != v1alpha1.RemoteIstioReasonReconcileError { - t.Errorf("Expected status.state to be %q, but got %q", v1alpha1.RemoteIstioReasonReconcileError, istio.Status.State) - } - - reconciledCond := istio.Status.GetCondition(v1alpha1.RemoteIstioConditionReconciled) - if reconciledCond.Status != metav1.ConditionFalse { - t.Errorf("Expected Reconciled condition status to be %q, but got %q", metav1.ConditionFalse, reconciledCond.Status) - } - - readyCond := istio.Status.GetCondition(v1alpha1.RemoteIstioConditionReady) - if readyCond.Status != metav1.ConditionUnknown { - t.Errorf("Expected Reconciled condition status to be %q, but got %q", metav1.ConditionUnknown, readyCond.Status) - } - }) -} - -func TestValidate(t *testing.T) { - testCases := []struct { - name string - istio *v1alpha1.RemoteIstio - expectErr string - }{ - { - name: "success", - istio: &v1alpha1.RemoteIstio{ - ObjectMeta: metav1.ObjectMeta{ - Name: "default", - }, - Spec: v1alpha1.RemoteIstioSpec{ - Version: supportedversion.Default, - Namespace: "istio-system", - }, - }, - expectErr: "", - }, - { - name: "no version", - istio: &v1alpha1.RemoteIstio{ - ObjectMeta: metav1.ObjectMeta{ - Name: "default", - }, - Spec: v1alpha1.RemoteIstioSpec{ - Namespace: "istio-system", - }, - }, - expectErr: "spec.version not set", - }, - { - name: "no namespace", - istio: &v1alpha1.RemoteIstio{ - ObjectMeta: metav1.ObjectMeta{ - Name: "default", - }, - Spec: v1alpha1.RemoteIstioSpec{ - Version: supportedversion.Default, - }, - }, - expectErr: "spec.namespace not set", - }, - } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - g := NewWithT(t) - - err := validate(tc.istio) - if tc.expectErr == "" { - g.Expect(err).ToNot(HaveOccurred()) - } else { - g.Expect(err).To(HaveOccurred()) - g.Expect(err.Error()).To(ContainSubstring(tc.expectErr)) - } - }) - } -} - -func TestDetermineStatus(t *testing.T) { - cfg := newReconcilerTestConfig(t) - - generation := int64(100) - - ownedByIstio := metav1.OwnerReference{ - APIVersion: v1alpha1.GroupVersion.String(), - Kind: v1alpha1.RemoteIstioKind, - Name: istioName, - UID: istioUID, - Controller: ptr.Of(true), - BlockOwnerDeletion: ptr.Of(true), - } - - ownedByAnotherIstio := metav1.OwnerReference{ - APIVersion: v1alpha1.GroupVersion.String(), - Kind: v1alpha1.RemoteIstioKind, - Name: "some-other-Istio", - UID: "some-other-uid", - Controller: ptr.Of(true), - BlockOwnerDeletion: ptr.Of(true), - } - - revision := func(name string, ownerRef metav1.OwnerReference, reconciled, ready, inUse bool) v1alpha1.IstioRevision { - return v1alpha1.IstioRevision{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - OwnerReferences: []metav1.OwnerReference{ownerRef}, - }, - Spec: v1alpha1.IstioRevisionSpec{Namespace: istioNamespace}, - Status: v1alpha1.IstioRevisionStatus{ - State: v1alpha1.IstioRevisionReasonHealthy, - Conditions: []v1alpha1.IstioRevisionCondition{ - {Type: v1alpha1.IstioRevisionConditionReconciled, Status: toConditionStatus(reconciled)}, - {Type: v1alpha1.IstioRevisionConditionReady, Status: toConditionStatus(ready)}, - {Type: v1alpha1.IstioRevisionConditionInUse, Status: toConditionStatus(inUse)}, - }, - }, - } - } - - testCases := []struct { - name string - reconciliationErr error - istio *v1alpha1.RemoteIstio - revisions []v1alpha1.IstioRevision - interceptorFuncs *interceptor.Funcs - wantErr bool - expectedStatus v1alpha1.RemoteIstioStatus - }{ - { - name: "reconciliation error", - reconciliationErr: fmt.Errorf("reconciliation error"), - wantErr: false, - expectedStatus: v1alpha1.RemoteIstioStatus{ - State: v1alpha1.RemoteIstioReasonReconcileError, - ObservedGeneration: generation, - Conditions: []v1alpha1.RemoteIstioCondition{ - { - Type: v1alpha1.RemoteIstioConditionReconciled, - Status: metav1.ConditionFalse, - Reason: v1alpha1.RemoteIstioReasonReconcileError, - Message: "reconciliation error", - }, - { - Type: v1alpha1.RemoteIstioConditionReady, - Status: metav1.ConditionUnknown, - Reason: v1alpha1.RemoteIstioReasonReconcileError, - Message: "cannot determine readiness due to reconciliation error", - }, - }, - }, - }, - { - name: "mirrors status of active revision", - wantErr: false, - revisions: []v1alpha1.IstioRevision{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: istioKey.Name, - OwnerReferences: []metav1.OwnerReference{ownedByIstio}, - }, - Spec: v1alpha1.IstioRevisionSpec{ - Namespace: istioNamespace, - }, - Status: v1alpha1.IstioRevisionStatus{ - State: v1alpha1.IstioRevisionReasonHealthy, - Conditions: []v1alpha1.IstioRevisionCondition{ - { - Type: v1alpha1.IstioRevisionConditionReconciled, - Status: metav1.ConditionTrue, - Reason: v1alpha1.IstioRevisionReasonHealthy, - Message: "reconciled message", - }, - { - Type: v1alpha1.IstioRevisionConditionReady, - Status: metav1.ConditionTrue, - Reason: v1alpha1.IstioRevisionReasonHealthy, - Message: "ready message", - }, - }, - }, - }, - { - ObjectMeta: metav1.ObjectMeta{ - Name: istioKey.Name + "-not-active", - OwnerReferences: []metav1.OwnerReference{ownedByIstio}, - }, - Spec: v1alpha1.IstioRevisionSpec{ - Namespace: istioNamespace, - }, - Status: v1alpha1.IstioRevisionStatus{ - State: v1alpha1.IstioRevisionReasonHealthy, - Conditions: []v1alpha1.IstioRevisionCondition{ - { - Type: v1alpha1.IstioRevisionConditionReconciled, - Status: metav1.ConditionFalse, - Reason: v1alpha1.IstioRevisionReasonHealthy, - Message: "shouldn't mirror this revision", - }, - { - Type: v1alpha1.IstioRevisionConditionReady, - Status: metav1.ConditionFalse, - Reason: v1alpha1.IstioRevisionReasonHealthy, - Message: "shouldn't mirror this revision", - }, - }, - }, - }, - }, - expectedStatus: v1alpha1.RemoteIstioStatus{ - State: v1alpha1.RemoteIstioReasonHealthy, - ObservedGeneration: generation, - Conditions: []v1alpha1.RemoteIstioCondition{ - { - Type: v1alpha1.RemoteIstioConditionReconciled, - Status: metav1.ConditionTrue, - Reason: v1alpha1.RemoteIstioReasonHealthy, - Message: "reconciled message", - }, - { - Type: v1alpha1.RemoteIstioConditionReady, - Status: metav1.ConditionTrue, - Reason: v1alpha1.RemoteIstioReasonHealthy, - Message: "ready message", - }, - }, - ActiveRevisionName: istioKey.Name, - Revisions: v1alpha1.RevisionSummary{ - Total: 2, - Ready: 1, - InUse: 0, - }, - }, - }, - { - name: "shows correct revision counts", - wantErr: false, - revisions: []v1alpha1.IstioRevision{ - // owned by the Istio under test; 3 todal, 2 ready, 1 in use - revision(istioKey.Name, ownedByIstio, true, true, true), - revision(istioKey.Name+"-old1", ownedByIstio, true, true, false), - revision(istioKey.Name+"-old2", ownedByIstio, true, false, false), - // not owned by the Istio being tested; shouldn't affect counts - revision("some-other-istio", ownedByAnotherIstio, true, true, true), - }, - expectedStatus: v1alpha1.RemoteIstioStatus{ - State: v1alpha1.RemoteIstioReasonHealthy, - ObservedGeneration: generation, - Conditions: []v1alpha1.RemoteIstioCondition{ - { - Type: v1alpha1.RemoteIstioConditionReconciled, - Status: metav1.ConditionTrue, - }, - { - Type: v1alpha1.RemoteIstioConditionReady, - Status: metav1.ConditionTrue, - }, - }, - ActiveRevisionName: istioKey.Name, - Revisions: v1alpha1.RevisionSummary{ - Total: 3, - Ready: 2, - InUse: 1, - }, - }, - }, - { - name: "active revision not found", - wantErr: false, - expectedStatus: v1alpha1.RemoteIstioStatus{ - State: v1alpha1.RemoteIstioReasonRevisionNotFound, - ObservedGeneration: generation, - Conditions: []v1alpha1.RemoteIstioCondition{ - { - Type: v1alpha1.RemoteIstioConditionReconciled, - Status: metav1.ConditionFalse, - Reason: v1alpha1.RemoteIstioReasonRevisionNotFound, - Message: "active IstioRevision not found", - }, - { - Type: v1alpha1.RemoteIstioConditionReady, - Status: metav1.ConditionFalse, - Reason: v1alpha1.RemoteIstioReasonRevisionNotFound, - Message: "active IstioRevision not found", - }, - }, - ActiveRevisionName: istioKey.Name, - }, - }, - { - name: "get active revision error", - interceptorFuncs: &interceptor.Funcs{ - Get: func(_ context.Context, _ client.WithWatch, _ client.ObjectKey, obj client.Object, _ ...client.GetOption) error { - if _, ok := obj.(*v1alpha1.IstioRevision); ok { - return fmt.Errorf("simulated error") - } - return nil - }, - }, - wantErr: true, - expectedStatus: v1alpha1.RemoteIstioStatus{ - State: v1alpha1.RemoteIstioReasonFailedToGetActiveRevision, - ObservedGeneration: generation, - Conditions: []v1alpha1.RemoteIstioCondition{ - { - Type: v1alpha1.RemoteIstioConditionReconciled, - Status: metav1.ConditionUnknown, - Reason: v1alpha1.RemoteIstioReasonFailedToGetActiveRevision, - Message: "failed to get active IstioRevision: get failed: simulated error", - }, - { - Type: v1alpha1.RemoteIstioConditionReady, - Status: metav1.ConditionUnknown, - Reason: v1alpha1.RemoteIstioReasonFailedToGetActiveRevision, - Message: "failed to get active IstioRevision: get failed: simulated error", - }, - }, - ActiveRevisionName: istioKey.Name, - Revisions: v1alpha1.RevisionSummary{}, - }, - }, - { - name: "list revisions error", - interceptorFuncs: &interceptor.Funcs{ - List: func(_ context.Context, _ client.WithWatch, list client.ObjectList, _ ...client.ListOption) error { - if _, ok := list.(*v1alpha1.IstioRevisionList); ok { - return fmt.Errorf("simulated error") - } - return nil - }, - }, - wantErr: true, - expectedStatus: v1alpha1.RemoteIstioStatus{ - State: v1alpha1.RemoteIstioReasonRevisionNotFound, - ObservedGeneration: generation, - Conditions: []v1alpha1.RemoteIstioCondition{ - { - Type: v1alpha1.RemoteIstioConditionReconciled, - Status: metav1.ConditionFalse, - Reason: v1alpha1.RemoteIstioReasonRevisionNotFound, - Message: "active IstioRevision not found", - }, - { - Type: v1alpha1.RemoteIstioConditionReady, - Status: metav1.ConditionFalse, - Reason: v1alpha1.RemoteIstioReasonRevisionNotFound, - Message: "active IstioRevision not found", - }, - }, - ActiveRevisionName: istioKey.Name, - Revisions: v1alpha1.RevisionSummary{ - Total: -1, - Ready: -1, - InUse: -1, - }, - }, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - var interceptorFuncs interceptor.Funcs - if tc.interceptorFuncs != nil { - interceptorFuncs = *tc.interceptorFuncs - } - - istio := tc.istio - if istio == nil { - istio = &v1alpha1.RemoteIstio{ - ObjectMeta: metav1.ObjectMeta{ - Name: istioKey.Name, - UID: istioUID, - Generation: 100, - }, - Spec: v1alpha1.RemoteIstioSpec{ - Version: "my-version", - Namespace: istioNamespace, - }, - } - } - - initObjs := []client.Object{istio} - for _, rev := range tc.revisions { - rev := rev - initObjs = append(initObjs, &rev) - } - - cl := newFakeClientBuilder(). - WithObjects(initObjs...). - WithInterceptorFuncs(interceptorFuncs). - Build() - reconciler := NewReconciler(cfg, cl, scheme.Scheme) - - status, err := reconciler.determineStatus(ctx, istio, tc.reconciliationErr) - if (err != nil) != tc.wantErr { - t.Errorf("determineStatus() error = %v, wantErr %v", err, tc.wantErr) - } - - if diff := cmp.Diff(tc.expectedStatus, clearTimestamps(status)); diff != "" { - t.Errorf("returned status wasn't as expected; diff (-expected, +actual):\n%v", diff) - } - }) - } -} - -func TestUpdateStatus(t *testing.T) { - cfg := newReconcilerTestConfig(t) - - generation := int64(100) - oneMinuteAgo := testtime.OneMinuteAgo() - - testCases := []struct { - name string - reconciliationErr error - istio *v1alpha1.RemoteIstio - revisions []v1alpha1.IstioRevision - interceptorFuncs *interceptor.Funcs - disallowWrites bool - wantErr bool - expectedStatus v1alpha1.RemoteIstioStatus - - skipInterceptors bool // used internally by test implementation when it wants to get around the interceptor - }{ - { - name: "updates status even when determineStatus returns error", - interceptorFuncs: &interceptor.Funcs{ - List: func(_ context.Context, _ client.WithWatch, list client.ObjectList, _ ...client.ListOption) error { - if _, ok := list.(*v1alpha1.IstioRevisionList); ok { - return fmt.Errorf("simulated error") - } - return nil - }, - }, - wantErr: true, - expectedStatus: v1alpha1.RemoteIstioStatus{ - State: v1alpha1.RemoteIstioReasonRevisionNotFound, - ObservedGeneration: generation, - Conditions: []v1alpha1.RemoteIstioCondition{ - { - Type: v1alpha1.RemoteIstioConditionReconciled, - Status: metav1.ConditionFalse, - Reason: v1alpha1.RemoteIstioReasonRevisionNotFound, - Message: "active IstioRevision not found", - }, - { - Type: v1alpha1.RemoteIstioConditionReady, - Status: metav1.ConditionFalse, - Reason: v1alpha1.RemoteIstioReasonRevisionNotFound, - Message: "active IstioRevision not found", - }, - }, - ActiveRevisionName: istioKey.Name, - Revisions: v1alpha1.RevisionSummary{ - Total: -1, - Ready: -1, - InUse: -1, - }, - }, - }, - { - name: "skips update when status unchanged", - istio: &v1alpha1.RemoteIstio{ - ObjectMeta: metav1.ObjectMeta{ - Name: istioKey.Name, - UID: istioUID, - Generation: 100, - }, - Spec: v1alpha1.RemoteIstioSpec{ - Version: "my-version", - Namespace: istioNamespace, - }, - Status: v1alpha1.RemoteIstioStatus{ - ObservedGeneration: 100, - State: v1alpha1.RemoteIstioReasonHealthy, - Conditions: []v1alpha1.RemoteIstioCondition{ - { - Type: v1alpha1.RemoteIstioConditionReconciled, - Status: metav1.ConditionTrue, - Reason: v1alpha1.RemoteIstioReasonHealthy, - Message: "reconciled message", - LastTransitionTime: *oneMinuteAgo, - }, - { - Type: v1alpha1.RemoteIstioConditionReady, - Status: metav1.ConditionTrue, - Reason: v1alpha1.RemoteIstioReasonHealthy, - Message: "ready message", - LastTransitionTime: *oneMinuteAgo, - }, - }, - ActiveRevisionName: istioKey.Name, - }, - }, - revisions: []v1alpha1.IstioRevision{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: istioKey.Name, - }, - Spec: v1alpha1.IstioRevisionSpec{ - Namespace: istioNamespace, - }, - Status: v1alpha1.IstioRevisionStatus{ - State: v1alpha1.IstioRevisionReasonHealthy, - Conditions: []v1alpha1.IstioRevisionCondition{ - { - Type: v1alpha1.IstioRevisionConditionReconciled, - Status: metav1.ConditionTrue, - Reason: v1alpha1.IstioRevisionReasonHealthy, - Message: "reconciled message", - LastTransitionTime: *oneMinuteAgo, - }, - { - Type: v1alpha1.IstioRevisionConditionReady, - Status: metav1.ConditionTrue, - Reason: v1alpha1.IstioRevisionReasonHealthy, - Message: "ready message", - LastTransitionTime: *oneMinuteAgo, - }, - }, - }, - }, - }, - expectedStatus: v1alpha1.RemoteIstioStatus{ - State: v1alpha1.RemoteIstioReasonHealthy, - ObservedGeneration: generation, - Conditions: []v1alpha1.RemoteIstioCondition{ - { - Type: v1alpha1.RemoteIstioConditionReconciled, - Status: metav1.ConditionTrue, - Reason: v1alpha1.RemoteIstioReasonHealthy, - Message: "reconciled message", - }, - { - Type: v1alpha1.RemoteIstioConditionReady, - Status: metav1.ConditionTrue, - Reason: v1alpha1.RemoteIstioReasonHealthy, - Message: "ready message", - }, - }, - ActiveRevisionName: istioKey.Name, - }, - disallowWrites: true, - wantErr: false, - }, - { - name: "returns status update error", - interceptorFuncs: &interceptor.Funcs{ - SubResourcePatch: func(_ context.Context, _ client.Client, _ string, _ client.Object, _ client.Patch, _ ...client.SubResourcePatchOption) error { - return fmt.Errorf("patch status error") - }, - }, - wantErr: true, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - var interceptorFuncs interceptor.Funcs - if tc.disallowWrites { - if tc.interceptorFuncs != nil { - panic("can't use disallowWrites and interceptorFuncs at the same time") - } - interceptorFuncs = noWrites(t) - } else if tc.interceptorFuncs != nil { - interceptorFuncs = *tc.interceptorFuncs - } - - istio := tc.istio - if istio == nil { - istio = &v1alpha1.RemoteIstio{ - ObjectMeta: metav1.ObjectMeta{ - Name: istioKey.Name, - UID: istioUID, - Generation: 100, - }, - Spec: v1alpha1.RemoteIstioSpec{ - Version: "my-version", - Namespace: istioNamespace, - }, - } - } - - initObjs := []client.Object{istio} - for _, rev := range tc.revisions { - rev := rev - initObjs = append(initObjs, &rev) - } - - cl := newFakeClientBuilder(). - WithObjects(initObjs...). - WithInterceptorFuncs(interceptorFuncs). - Build() - reconciler := NewReconciler(cfg, cl, scheme.Scheme) - - err := reconciler.updateStatus(ctx, istio, tc.reconciliationErr) - if (err != nil) != tc.wantErr { - t.Errorf("updateStatus() error = %v, wantErr %v", err, tc.wantErr) - } - - Must(t, cl.Get(ctx, istioKey, istio)) - if diff := cmp.Diff(tc.expectedStatus, clearTimestamps(istio.Status)); diff != "" { - t.Errorf("returned status wasn't as expected; diff (-expected, +actual):\n%v", diff) - } - }) - } -} - -func clearTimestamps(status v1alpha1.RemoteIstioStatus) v1alpha1.RemoteIstioStatus { - for i := range status.Conditions { - status.Conditions[i].LastTransitionTime = metav1.Time{} - } - return status -} - -func toConditionStatus(b bool) metav1.ConditionStatus { - if b { - return metav1.ConditionTrue - } - return metav1.ConditionFalse -} - -func TestGetActiveRevisionName(t *testing.T) { - tests := []struct { - name string - version string - updateStrategyType *v1alpha1.UpdateStrategyType - expectedRevisionName string - }{ - { - name: "No update strategy specified", - version: "1.0.0", - updateStrategyType: nil, - expectedRevisionName: "test-istio", - }, - { - name: "InPlace", - version: "1.0.0", - updateStrategyType: ptr.Of(v1alpha1.UpdateStrategyTypeInPlace), - expectedRevisionName: "test-istio", - }, - { - name: "RevisionBased v1.0.0", - version: "1.0.0", - updateStrategyType: ptr.Of(v1alpha1.UpdateStrategyTypeRevisionBased), - expectedRevisionName: "test-istio-1-0-0", - }, - { - name: "RevisionBased v2.0.0", - version: "2.0.0", - updateStrategyType: ptr.Of(v1alpha1.UpdateStrategyTypeRevisionBased), - expectedRevisionName: "test-istio-2-0-0", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - istio := &v1alpha1.RemoteIstio{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-istio", - }, - Spec: v1alpha1.RemoteIstioSpec{ - Version: tt.version, - }, - } - if tt.updateStrategyType != nil { - istio.Spec.UpdateStrategy = &v1alpha1.IstioUpdateStrategy{ - Type: *tt.updateStrategyType, - } - } - actual := getActiveRevisionName(istio) - if actual != tt.expectedRevisionName { - t.Errorf("getActiveRevisionName() = %v, want %v", actual, tt.expectedRevisionName) - } - }) - } -} - -func newFakeClientBuilder() *fake.ClientBuilder { - return fake.NewClientBuilder(). - WithScheme(scheme.Scheme). - WithStatusSubresource(&v1alpha1.RemoteIstio{}) -} - -func TestGetPruningGracePeriod(t *testing.T) { - tests := []struct { - name string - updateStrategy *v1alpha1.IstioUpdateStrategy - expected time.Duration - }{ - { - name: "Nil update strategy", - updateStrategy: nil, - expected: v1alpha1.DefaultRevisionDeletionGracePeriodSeconds * time.Second, - }, - { - name: "Nil grace period", - updateStrategy: &v1alpha1.IstioUpdateStrategy{}, - expected: v1alpha1.DefaultRevisionDeletionGracePeriodSeconds * time.Second, - }, - { - name: "Grace period less than minimum", - updateStrategy: &v1alpha1.IstioUpdateStrategy{ - InactiveRevisionDeletionGracePeriodSeconds: ptr.Of(int64(v1alpha1.MinRevisionDeletionGracePeriodSeconds - 10)), - }, - expected: v1alpha1.MinRevisionDeletionGracePeriodSeconds * time.Second, - }, - { - name: "Grace period more than minimum", - updateStrategy: &v1alpha1.IstioUpdateStrategy{ - InactiveRevisionDeletionGracePeriodSeconds: ptr.Of(int64(v1alpha1.MinRevisionDeletionGracePeriodSeconds + 10)), - }, - expected: (v1alpha1.MinRevisionDeletionGracePeriodSeconds + 10) * time.Second, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - istio := &v1alpha1.RemoteIstio{ - Spec: v1alpha1.RemoteIstioSpec{ - UpdateStrategy: tt.updateStrategy, - }, - } - got := getPruningGracePeriod(istio) - if got != tt.expected { - t.Errorf("getPruningGracePeriod() = %v, want %v", got, tt.expected) - } - }) - } -} - -func Must(t *testing.T, err error) { - t.Helper() - if err != nil { - t.Fatal(err) - } -} - -func noWrites(t *testing.T) interceptor.Funcs { - return interceptor.Funcs{ - Create: func(_ context.Context, _ client.WithWatch, _ client.Object, _ ...client.CreateOption) error { - t.Fatal("unexpected call to Create in", string(debug.Stack())) - return nil - }, - Update: func(_ context.Context, _ client.WithWatch, _ client.Object, _ ...client.UpdateOption) error { - t.Fatal("unexpected call to Update in", string(debug.Stack())) - return nil - }, - Delete: func(_ context.Context, _ client.WithWatch, _ client.Object, _ ...client.DeleteOption) error { - t.Fatal("unexpected call to Delete in", string(debug.Stack())) - return nil - }, - Patch: func(_ context.Context, _ client.WithWatch, _ client.Object, _ client.Patch, _ ...client.PatchOption) error { - t.Fatal("unexpected call to Patch in", string(debug.Stack())) - return nil - }, - DeleteAllOf: func(_ context.Context, _ client.WithWatch, _ client.Object, _ ...client.DeleteAllOfOption) error { - t.Fatal("unexpected call to DeleteAllOf in", string(debug.Stack())) - return nil - }, - SubResourceCreate: func(_ context.Context, _ client.Client, _ string, _ client.Object, _ client.Object, _ ...client.SubResourceCreateOption) error { - t.Fatal("unexpected call to SubResourceCreate in", string(debug.Stack())) - return nil - }, - SubResourceUpdate: func(_ context.Context, _ client.Client, _ string, _ client.Object, _ ...client.SubResourceUpdateOption) error { - t.Fatal("unexpected call to SubResourceUpdate in", string(debug.Stack())) - return nil - }, - SubResourcePatch: func(_ context.Context, _ client.Client, _ string, obj client.Object, _ client.Patch, _ ...client.SubResourcePatchOption) error { - t.Fatalf("unexpected call to SubResourcePatch with the object %+v: %v", obj, string(debug.Stack())) - return nil - }, - } -} - -func newReconcilerTestConfig(t *testing.T) config.ReconcilerConfig { - return config.ReconcilerConfig{ - ResourceDirectory: t.TempDir(), - Platform: config.PlatformKubernetes, - DefaultProfile: "", - } -} diff --git a/controllers/webhook/webhook_controller.go b/controllers/webhook/webhook_controller.go index fe6ef714b..142a60b44 100644 --- a/controllers/webhook/webhook_controller.go +++ b/controllers/webhook/webhook_controller.go @@ -30,6 +30,7 @@ import ( "github.com/istio-ecosystem/sail-operator/pkg/constants" "github.com/istio-ecosystem/sail-operator/pkg/enqueuelogger" "github.com/istio-ecosystem/sail-operator/pkg/reconciler" + "github.com/istio-ecosystem/sail-operator/pkg/revision" admissionv1 "k8s.io/api/admissionregistration/v1" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" @@ -74,15 +75,21 @@ func NewReconciler(client client.Client, scheme *runtime.Scheme) *Reconciler { // For more details, check Reconcile and its Result here: // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.14.1/pkg/reconcile func (r *Reconciler) Reconcile(ctx context.Context, webhook *admissionv1.MutatingWebhookConfiguration) (ctrl.Result, error) { + log := logf.FromContext(ctx) + isReady, err := r.probe(ctx, webhook) + reason := "" if err != nil { - isReady = false + log.V(3).Error(err, "Probe failed") + reason = err.Error() } if webhook.Annotations == nil { webhook.Annotations = make(map[string]string) } webhook.Annotations[constants.WebhookReadinessProbeStatusAnnotationKey] = strconv.FormatBool(isReady) + webhook.Annotations[constants.WebhookReadinessProbeStatusReasonAnnotationKey] = reason + err = r.Client.Update(ctx, webhook) if err != nil { return ctrl.Result{}, err @@ -91,7 +98,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, webhook *admissionv1.Mutatin } func doProbe(ctx context.Context, webhook *admissionv1.MutatingWebhookConfiguration) (bool, error) { - log := logf.FromContext(ctx).V(3) + log := logf.FromContext(ctx) if len(webhook.Webhooks) == 0 { return false, errors.New("mutatingwebhookconfiguration contains no webhooks") } @@ -129,13 +136,13 @@ func doProbe(ctx context.Context, webhook *admissionv1.MutatingWebhookConfigurat return false, err } - log.Info("Executing readiness probe on remote control plane", "url", req.URL.String()) + log.V(3).Info("Executing readiness probe on remote control plane", "url", req.URL.String()) resp, err := httpClient.Do(req) if err != nil { - log.Info("Probe failed", "error", err) + log.V(3).Info("Probe failed", "error", err) return false, err } - log.Info("Probe response", "response", resp.StatusCode) + log.V(3).Info("Probe response", "response", resp.StatusCode) return resp.StatusCode == http.StatusOK, nil } @@ -178,36 +185,37 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error { // we use the Watches function instead of For(), so that we can wrap the handler so that events that cause the object to be enqueued are logged // +lint-watches:ignore: IstioRevision (not found in charts, but this is the main resource watched by this controller) - Watches(&admissionv1.MutatingWebhookConfiguration{}, objectHandler, builder.WithPredicates(ownedByRemoteIstioPredicate(mgr.GetClient()))). + Watches(&admissionv1.MutatingWebhookConfiguration{}, objectHandler, builder.WithPredicates(ownedByRemoteIstioRevisionPredicate(mgr.GetClient()))). Named("mutatingwebhookconfiguration"). Complete(reconciler.NewStandardReconciler[*admissionv1.MutatingWebhookConfiguration](r.Client, r.Reconcile)) } -func ownedByRemoteIstioPredicate(cl client.Client) predicate.Predicate { +func ownedByRemoteIstioRevisionPredicate(cl client.Client) predicate.Predicate { return predicate.Funcs{ CreateFunc: func(e event.CreateEvent) bool { - return isOwnedByRemoteIstio(cl, e.Object) + return IsOwnedByRevisionWithRemoteControlPlane(cl, e.Object) }, UpdateFunc: func(e event.UpdateEvent) bool { - return isOwnedByRemoteIstio(cl, e.ObjectNew) + return IsOwnedByRevisionWithRemoteControlPlane(cl, e.ObjectNew) }, DeleteFunc: func(e event.DeleteEvent) bool { - return isOwnedByRemoteIstio(cl, e.Object) + return IsOwnedByRevisionWithRemoteControlPlane(cl, e.Object) }, GenericFunc: func(e event.GenericEvent) bool { - return isOwnedByRemoteIstio(cl, e.Object) + return IsOwnedByRevisionWithRemoteControlPlane(cl, e.Object) }, } } -func isOwnedByRemoteIstio(cl client.Client, obj client.Object) bool { +func IsOwnedByRevisionWithRemoteControlPlane(cl client.Client, obj client.Object) bool { for _, ownerRef := range obj.GetOwnerReferences() { if ownerRef.APIVersion == v1alpha1.GroupVersion.String() && ownerRef.Kind == v1alpha1.IstioRevisionKind { rev := &v1alpha1.IstioRevision{} err := cl.Get(context.Background(), client.ObjectKey{Name: ownerRef.Name}, rev) if err != nil { - // TODO log error - } else if rev.Spec.Type == v1alpha1.IstioRevisionTypeRemote { + return false + } + if revision.IsUsingRemoteControlPlane(rev) { return true } } diff --git a/controllers/webhook/webhook_controller_test.go b/controllers/webhook/webhook_controller_test.go index 2e2b98813..054c316cf 100644 --- a/controllers/webhook/webhook_controller_test.go +++ b/controllers/webhook/webhook_controller_test.go @@ -380,7 +380,7 @@ func TestDoProbe(t *testing.T) { } } -func TestIsOwnedByRemoteIstio(t *testing.T) { +func TestIsOwnedByRevisionWithRemoteControlPlane(t *testing.T) { tests := []struct { name string ownerRefs []metav1.OwnerReference @@ -431,7 +431,7 @@ func TestIsOwnedByRemoteIstio(t *testing.T) { expected: false, }, { - name: "IstioRevision type not remote", + name: "IstioRevision not using remote profile", ownerRefs: []metav1.OwnerReference{ { APIVersion: v1alpha1.GroupVersion.String(), @@ -444,15 +444,13 @@ func TestIsOwnedByRemoteIstio(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "revision1", }, - Spec: v1alpha1.IstioRevisionSpec{ - Type: v1alpha1.IstioRevisionTypeLocal, - }, + Spec: v1alpha1.IstioRevisionSpec{}, }, }, expected: false, }, { - name: "IstioRevision type is remote", + name: "IstioRevision uses remote profile", ownerRefs: []metav1.OwnerReference{ { APIVersion: v1alpha1.GroupVersion.String(), @@ -466,7 +464,9 @@ func TestIsOwnedByRemoteIstio(t *testing.T) { Name: "revision1", }, Spec: v1alpha1.IstioRevisionSpec{ - Type: v1alpha1.IstioRevisionTypeRemote, + Values: &v1alpha1.Values{ + Profile: ptr.Of("remote"), + }, }, }, }, @@ -489,7 +489,7 @@ func TestIsOwnedByRemoteIstio(t *testing.T) { }, } - result := isOwnedByRemoteIstio(cl, obj) + result := IsOwnedByRevisionWithRemoteControlPlane(cl, obj) g.Expect(result).To(Equal(tt.expected)) }) } diff --git a/docs/README.md b/docs/README.md index 828976cf5..136954d3c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -7,7 +7,6 @@ - [Istio resource](#istio-resource) - [IstioRevision resource](#istiorevision-resource) - [IstioCNI resource](#istiocni-resource) - - [RemoteIstio resource](#remoteistio-resource) - [API Reference documentation](#api-reference-documentation) - [Getting Started](#getting-started) - [Installation on OpenShift](#installation-on-openshift) @@ -64,7 +63,7 @@ kind: Istio metadata: name: default spec: - version: v1.22.3 + version: v1.23.2 namespace: istio-system updateStrategy: type: InPlace @@ -98,7 +97,7 @@ kind: IstioCNI metadata: name: default spec: - version: v1.22.3 + version: v1.23.2 namespace: istio-cni values: cni: @@ -107,32 +106,6 @@ spec: - kube-system ``` -### RemoteIstio resource -The `RemoteIstio` resource is used to connect the local cluster to an external Istio control plane. -When you create a `RemoteIstio` resource, the operator deploys the `istiod-remote` Helm chart. -Instead of deploying the entire Istio control plane, this chart deploys only the sidecar injector webhook, allowing you to inject the Istio proxy into your workloads and have this proxy managed by the Istio control plane running outside the cluster (typically in another Kubernetes cluster). - -The `RemoteIstio` resource is very similar to the `Istio` resource, with the most notable difference being the `istiodRemote` field in the `values` section, which allows you to configure the address of the remote Istio control plane: - -```yaml -apiVersion: sailoperator.io/v1alpha1 -kind: RemoteIstio -metadata: - name: default -spec: - version: v1.22.3 - namespace: istio-system - updateStrategy: - type: InPlace - values: - istiodRemote: - injectionPath: /inject/cluster/cluster2/net/network1 - global: - remotePilotAddress: 1.2.3.4 -``` - -For more information on how to use the `RemoteIstio` resource, refer to the [multi-cluster](#multi-cluster) section. - ## API Reference documentation The Sail Operator API reference documentation can be found [here](https://github.com/istio-ecosystem/sail-operator/tree/main/docs/api-reference/sailoperator.io.md). @@ -231,7 +204,7 @@ spec: values: pilot: traceSampling: 0.1 - version: v1.23.0 + version: v1.23.2 ``` Note that the only field that was added is the `spec.version` field. There are a few situations however where the APIs are different and require different approaches to achieve the same outcome. @@ -244,10 +217,6 @@ Sail Operator's Istio resource does not have a `spec.components` field. Instead, The CNI plugin's lifecycle is managed separately from the control plane. You will have to create a [IstioCNI resource](#istiocni-resource) to use CNI. -### istiod-remote - -The functionality of the istiod-remote chart is exposed through the [RemoteIstio resource](#remoteistio-resource). - ## Gateways [Gateways in Istio](https://istio.io/latest/docs/concepts/traffic-management/#gateways) are used to manage inbound and outbound traffic for the mesh. The Sail Operator does not deploy or manage Gateways. You can deploy a gateway either through [gateway-api](https://istio.io/latest/docs/tasks/traffic-management/ingress/gateway-api/) or through [gateway injection](https://istio.io/latest/docs/setup/additional-setup/gateway/#deploying-a-gateway). As you are following the gateway installation instructions, skip the step to install Istio since this is handled by the Sail Operator. @@ -288,7 +257,7 @@ Steps: namespace: istio-system updateStrategy: type: InPlace - version: v1.21.0 + version: v1.22.5 EOF ``` @@ -296,9 +265,10 @@ Steps: ```console $ kubectl get istio -n istio-system - NAME READY STATUS IN USE VERSION AGE - default True Healthy True v1.21.0 2m + NAME REVISIONS READY IN USE ACTIVE REVISION STATUS VERSION AGE + default 1 1 0 default Healthy v1.22.5 23s ``` + Note: `IN USE` field shows as 0, as `Istio` is yet installed. 4. Create namespace `bookinfo` and deploy bookinfo application. @@ -309,27 +279,36 @@ Steps: ``` Note: if the `Istio` resource name is other than `default`, you need to set the `istio.io/rev` label to the name of the `Istio` resource instead of adding the `istio-injection=enabled` label. -5. Perform the update of the control plane by changing the version in the Istio resource. +5. Review the `Istio` resource after application deployment. + + ```console + $ kubectl get istio -n istio-system + NAME REVISIONS READY IN USE ACTIVE REVISION STATUS VERSION AGE + default 1 1 1 default Healthy v1.22.5 115s + ``` + Note: `IN USE` field shows as 1, after application being deployed. + +6. Perform the update of the control plane by changing the version in the Istio resource. ```bash - kubectl patch istio default -n istio-system --type='merge' -p '{"spec":{"version":"v1.21.2"}}' + kubectl patch istio default -n istio-system --type='merge' -p '{"spec":{"version":"v1.23.2"}}' ``` -6. Confirm the `Istio` resource version was updated. +7. Confirm the `Istio` resource version was updated. ```console $ kubectl get istio -n istio-system - NAME REVISIONS READY IN USE ACTIVE REVISION VERSION AGE - default 1 1 1 Healthy v1.21.2 12m + NAME REVISIONS READY IN USE ACTIVE REVISION STATUS VERSION AGE + default 1 1 1 default Healthy v1.23.2 4m50s ``` -7. Delete `bookinfo` pods to trigger sidecar injection with the new version. +8. Delete `bookinfo` pods to trigger sidecar injection with the new version. ```bash kubectl rollout restart deployment -n bookinfo ``` -8. Confirm that the new version is used in the sidecar. +9. Confirm that the new version is used in the sidecar. ```bash istioctl proxy-status @@ -366,7 +345,7 @@ Steps: updateStrategy: type: RevisionBased inactiveRevisionDeletionGracePeriodSeconds: 30 - version: v1.21.0 + version: v1.22.5 EOF ``` @@ -374,16 +353,17 @@ Steps: ```console $ kubectl get istio -n istio-system - NAME READY STATUS IN USE VERSION AGE - default True Healthy True v1.21.0 2m + NAME REVISIONS READY IN USE ACTIVE REVISION STATUS VERSION AGE + default 1 1 0 default-v1-22-5 Healthy v1.22.5 52s ``` + Note: `IN USE` field shows as 0, as `Istio` is yet installed. 4. Get the `IstioRevision` name. ```console $ kubectl get istiorevision -n istio-system - NAME READY STATUS IN USE VERSION AGE - default-v1-21-0 True Healthy False v1.21.0 114s + NAME TYPE READY STATUS IN USE VERSION AGE + default-v1-22-5 Local True Healthy False v1.22.5 3m4s ``` Note: `IstioRevision` name is in the format `-`. @@ -391,7 +371,7 @@ Steps: ```bash kubectl create namespace bookinfo - kubectl label namespace bookinfo istio.io/rev=default-v1-21-0 + kubectl label namespace bookinfo istio.io/rev=default-v1-22-5 ``` 6. Deploy bookinfo application. @@ -400,78 +380,91 @@ Steps: kubectl apply -n bookinfo -f https://raw.githubusercontent.com/istio/istio/release-1.22/samples/bookinfo/platform/kube/bookinfo.yaml ``` -7. Confirm that the proxy version matches the control plane version. +7. Review the `Istio` resource after application deployment. + + ```console + $ kubectl get istio -n istio-system + NAME REVISIONS READY IN USE ACTIVE REVISION STATUS VERSION AGE + default 1 1 1 default-v1-22-5 Healthy v1.22.5 5m13s + ``` + Note: `IN USE` field shows as 1, after application being deployed. + +8. Confirm that the proxy version matches the control plane version. ```bash istioctl proxy-status ``` The column `VERSION` should match the control plane version. -8. Update the control plane to a new version. +9. Update the control plane to a new version. ```bash - kubectl patch istio default -n istio-system --type='merge' -p '{"spec":{"version":"v1.21.2"}}' + kubectl patch istio default -n istio-system --type='merge' -p '{"spec":{"version":"v1.23.2"}}' ``` -9. Verify the `Istio` and `IstioRevision` resources. There will be a new revision created with the new version. +10. Verify the `Istio` and `IstioRevision` resources. There will be a new revision created with the new version. ```console $ kubectl get istio -n istio-system - NAME REVISIONS READY IN USE ACTIVE REVISION VERSION AGE - default 2 2 1 Healthy v1.21.2 23m + NAME REVISIONS READY IN USE ACTIVE REVISION STATUS VERSION AGE + default 2 2 1 default-v1-23-2 Healthy v1.23.2 9m23s $ kubectl get istiorevision -n istio-system - NAME READY STATUS IN USE VERSION AGE - default-v1-21-0 True Healthy True v1.21.0 27m - default-v1-21-2 True Healthy False v1.21.2 4m45s + NAME TYPE READY STATUS IN USE VERSION AGE + default-v1-22-5 Local True Healthy True v1.22.5 10m + default-v1-23-2 Local True Healthy False v1.23.2 66s ``` -10. Confirm there are two control plane pods running, one for each revision. +11. Confirm there are two control plane pods running, one for each revision. ```console $ kubectl get pods -n istio-system NAME READY STATUS RESTARTS AGE - istiod-default-v1-21-0-69d6df7f9c-grm24 1/1 Running 0 28m - istiod-default-v1-21-2-7c4f4674c5-4g7n7 1/1 Running 0 6m9s + istiod-default-v1-22-5-c98fd9675-r7bfw 1/1 Running 0 10m + istiod-default-v1-23-2-7495cdc7bf-v8t4g 1/1 Running 0 113s ``` -11. Confirm the proxy sidecar version remains the same: +12. Confirm the proxy sidecar version remains the same: ```bash istioctl proxy-status ``` The column `VERSION` should still match the old control plane version. -12. Change the label of the `bookinfo` namespace to use the new revision. +13. Change the label of the `bookinfo` namespace to use the new revision. ```bash - kubectl label namespace bookinfo istio.io/rev=default-v1-21-2 --overwrite + kubectl label namespace bookinfo istio.io/rev=default-v1-23-2 --overwrite ``` The existing workload sidecars will continue to run and will remain connected to the old control plane instance. They will not be replaced with a new version until the pods are deleted and recreated. -13. Delete all the pods in the `bookinfo` namespace. +14. Delete all the pods in the `bookinfo` namespace. ```bash kubectl rollout restart deployment -n bookinfo ``` -14. Confirm the new version is used in the sidecars. +15. Confirm the new version is used in the sidecars. ```bash istioctl proxy-status ``` The column `VERSION` should match the updated control plane version. -15. Confirm the old control plane and revision deletion. +16. Confirm the old control plane and revision deletion. ```console $ kubectl get pods -n istio-system NAME READY STATUS RESTARTS AGE - istiod-default-v1-21-2-7c4f4674c5-4g7n7 1/1 Running 0 94m + istiod-default-v1-23-2-7495cdc7bf-v8t4g 1/1 Running 0 4m40s + + $ kubectl get istio -n istio-system + NAME REVISIONS READY IN USE ACTIVE REVISION STATUS VERSION AGE + default 1 1 1 default-v1-23-2 Healthy v1.23.2 5m $ kubectl get istiorevision -n istio-system - NAME READY STATUS IN USE VERSION AGE - default-v1-21-2 True Healthy True v1.21.2 94m + NAME TYPE READY STATUS IN USE VERSION AGE + default-v1-23-2 Local True Healthy True v1.23.2 5m31s ``` The old `IstioRevision` resource and the old control plane will be deleted when the grace period specified in the `Istio` resource field `spec.updateStrategy.inactiveRevisionDeletionGracePeriodSeconds` expires. @@ -853,17 +846,18 @@ In this setup there is a Primary cluster (`cluster1`) and a Remote cluster (`clu kubectl --context "${CTX_CLUSTER1}" apply -n istio-system -f https://raw.githubusercontent.com/istio-ecosystem/sail-operator/main/docs/multicluster/expose-services.yaml ``` -5. Create `RemoteIstio` resource on `cluster2`. +5. Create an `Istio` on `cluster2` with the `remote` profile. ```sh kubectl apply --context "${CTX_CLUSTER2}" -f - < ingress-gateway.yaml + $ oc apply -f ingress-gateway.yaml ``` 2. Configure the `bookinfo` application with the new gateway: diff --git a/enhancements/SEP2-revision-tags.md b/enhancements/SEP2-revision-tags.md new file mode 100644 index 000000000..3ef946596 --- /dev/null +++ b/enhancements/SEP2-revision-tags.md @@ -0,0 +1,136 @@ +|Status | Authors | Created | +|---------------------------------------------------|--------------|------------| +|Implementation | @dgn | 2024-07-17 | + +# Revision Tag Support + +## Overview +Upstream Istio supports the use of [stable revision tags](https://istio.io/latest/blog/2021/revision-tags/) for multi-revision deployments and canary upgrades of Istio control planes. These tags serve as aliases for revisions and allow users to use stable revision names (e.g. `prod` or `default`), so they don't have to change their namespace and pod labels (in this case `istio.io/rev=prod` or `istio-injection=enabled`) when switching to a new version. Instead, by tagging a new revision with the correct tag and restarting their workloads, they can perform an Istio update without having to change their labels. This is especially useful in situations where the team managing the Istio control plane is separate from the teams managing the workloads. + +Revision tags can have any name, there is only one special case: revisions tagged `default` are treated as if they had an empty revision name, thereby allowing the use of the standard namespace injection label `istio-injection=enabled`. + +Each revision tag only ever points to exactly one Istio revision. Upstream, revision tags are created manually using `istioctl` and- as they only affect injection- are represented in the cluster by a MutatingWebhookConfiguration. + +Here's an example how to create a `default` revision tag that points to the `1-21-1` revision using `istioctl`: + +```bash +istioctl tag set default --revision 1-21-1 +``` + +## Goals +* Provide revision tag support in Sail Operator APIs so users don't have to use istioctl for basic revision tag operations + +## Non-goals +* Compatibility with manual revision tag creation using istioctl. There might be a way to disable the operator functionality to avoid conflicts when creating revision tags manually, but that's it - you either do it yourself or let the operator do it + +## Design + +### User Stories +1. As a user of Sail Operator's RevisionBased update strategy, I want to be able to use the `istio-injection=enabled` label on my namespaces. +1. As a platform engineer, I want my application teams to be able to use a fixed label for proxy injection without having to know which version of Istio is running in the cluster, so that I can perform upgrades in the background without the application teams having to make changes to use the new version. + +### API Changes +We will add a new CRD called `IstioRevisionTag` that consistly most of a `spec.targetRef` field and a `status` subresource. + +#### IstioRevisionTag resource +Here's an example YAML for the new resource: +```yaml +apiGroup: sailoperator.io/v1alpha1 +kind: IstioRevisionTag +metadata: + name: default +spec: + targetRef: + kind: Istio # can also be IstioRevision + name: default +status: + observedGeneration: 1 + conditions: [] + state: Healthy + istiodNamespace: istio-system + istioRevision: default-v1.24.0 +``` + +In the `spec.targetRef` field, users can specify the `IstioRevision` or `Istio` resource that the `IstioRevisionTag` references. In case of referencing a `IstioRevision` resource, the created tag will point to the exact Istio control plane revision that is represented by the `IstioRevision` resource and any update of the tag will have to be made manually by changing the `spec.targetRef` to point to another `IstioRevision`. As long as a `IstioRevisionTag` exists that references a `IstioRevision`, that `IstioRevision` will be considered "in-use" by the Sail Operator, preventing its automatic deletion during a control plane update (see details below under [InUse detection](#inuse-detection)) + +If the `spec.targetRef` is used to reference an `Istio` resource, the Sail Operator will automatically update the revision tag when a new `IstioRevision` is created as part of a version update of the `Istio` resource. In this case, the `IstioRevisionTag` resource behaves like a floating tag, always referencing the active `IstioRevision` of an `Istio` resource. When it comes to InUse detection, the existence of a floating tag will also cause the active `IstioRevision` of the `Istio` resource to be considered InUse. However, it will not prevent automatic deletion, because the reference is updated immediately when the active revision changes during an update. + +#### IstioRevisionTag Status + +The `status.state` field gives a quick hint as to whether a tag has been reconciled and is InUse ("Healthy") or if there are any problems with it. + +The `status.istiodNamespace` and `status.istioRevision` fields are used by the Sail Operator controllers to store information about the Istio control plane that is referenced by this `IstioRevisionTag`. This is especially useful when it is referencing an `Istio` resource, to see which underlying `IstioRevision` is considered referenced by the operator. + +Possible conditions for `status.conditions` are: + +##### Reconciled +`true` when the tag's helm chart has been installed successfully. Possible error reasons are: +__RefNotFound__: the resource referenced by the `spec.targetRef` field was not found +__NameAlreadyExists__: there already is an `IstioRevision` with this name +__ReconcileError__: there was an error installing the chart + +##### InUse +`true` when the `IstioRevisionTag` is referenced by a namespace or pod. Possible reasons when `false` are: +__NotReferencedByAnything__: no namespace or pod is referencing the tag +__UsageCheckFailed__: there was a problem during InUse detection + +#### InUse detection + +An `IstioRevisionTag` is considered InUse when + +* there's a pod or namespace that explicitly references the `IstioRevisionTag` in an `istio.io/rev` label +* the name of the `IstioRevisionTag` name is 'default' and there's a pod or namespace with the `istio-injection: enabled` label + +Note that a pod's `istio.io/rev` annotation will not be considered as that will always have the name of the referenced `IstioRevision` rather than the name of the `IstioRevisionTag`! The labels however are added by users and reflect usage intent, ie the user will use the name of the `IstioRevisionTag`. + +Even if the referenced `IstioRevision` of an `IstioRevisionTag` is considered InUse, that does not suffice to make the `IstioRevisionTag` considered InUse by the operator! It is considered an unused alias for an InUse `IstioRevision`. + +Additionally, the introduction of `IstioRevisionTag` also adds another condition to the InUse detection of `IstioRevision`: being referenced by an `IstioRevisionTag` will now always lead to an `IstioRevision` being considered InUse! For this, it does not matter if the `IstioRevisionTag` is itself considered InUse. + +For completeness' sake, here's an overview of the conditions for an `IstioRevision` to be considered InUse (new condition in bold): + +* there's a pod that explicitly references the `IstioRevision` in an `istio.io/rev` annotation or label +* there's a namespace that explicitly references the `IstioRevision` in an `istio.io/rev` label +* the name of the `IstioRevision` is 'default' and there's a pod or namespace with the `istio-injection: enabled` label +* __there's an `IstioRevisionTag` referencing this `IstioRevision`__ + +#### Changes to existing APIs +We will need to remove the `values.revisionTags` field from our API, which is how the upstream charts expose this feature. + +### Architecture +We will need to update the mechanism to detect revisions that are being used. Today, we only look at the `istio.io/rev` annotation's value to check which revisions are in use. But when revision tags are used, those values will point to the referenced revision instead of the tags, so we have to improve our detection mechanism. The most correct way is probably to look at the revision label on the pods and namespace that is set to configure injection. + +Revision tags and revision names can be used interchangably, so they must never overlap. Therefore, we'll need a `status` on the `IstioRevisionTag` resource that can show the user an error if the name they chose is already taken by a `IstioRevision`. Another case that needs to be covered is when an `IstioRevision` is being reconciled and it would be assigned the same name as an existing tag. In this case, reconciliation of the `IstioRevision` should fail, with an error message that tells the users why this happened, ie "the name is already used by an `IstioRevisionTag`". + +## Alternatives Considered + +### Reuse `IstioRevision`'s type field for revision tags +We could add a `type` of `Tag` to the `IstioRevision` CRD and use that to manage tags. It would have the benefit that the user could list all revisions and tags using `kubectl get istiorevisions` and name-uniqueness would be handled by Kubernetes. The disadvantage though is that revision tags share no other fields with `IstioRevision` and it would be quite confusing for users that 99% of the CRD's fields are not to be used in this case, whereas there would be one new field that is only to be used for `IstioRevisions` with type=Tag. + +Note that the `type` field has since been removed with the removal of `RemoteIstio`. + +### managing tags in Istio resource +In a previous iteration of this SEP, the tags that point to an Istio control plane would have been managed in the `Istio` revision itself, in a `spec.updateStrategy.revisionTags` field. That would have meant that they are always referencing a `Istio` resource while at the same time being copied to every underlying `IstioRevision` resource. + +### values.revisionTags +Istio has a `values.revisionTags` field that we even currently expose in our APIs. The problem is that we copy all values from the `Istio` resource to every `IstioRevision` and that means we would be facing duplicate revision tags when we create additional revisions in the Sail Operator - so, some logic would be required to work around this problem. As it is a similar amount of effort, I prefer the explicit version of adding the field to the `Istio` CRD. + +### Automatic creation of default revision tag +Previously, we had this paragraph in the Architecture section: +> When the very first `IstioRevision` is created in a cluster from a `RevisionBased` Istio resource, the Sail Operator will create a `IstioRevisionTag` with the name `default`, referencing that `IstioRevision`. This way, the standard namespace injection label `istio-injection=enabled` will work out of the box for RevisionBased deployments (see second paragraph of the [Overview](#overview)). + +We have since dropped this from the design as we faced some problems with this approach. Most importantly, it is very hard to detect whether the user is not creating a default IstioRevisionTag or we have simply not seen its creation event yet. We have discussed multiple possible solutions to this, among them usage of a 'virtual' revision tag that is not created on the API server but only exists in-memory, leading to the creation of the respective Kubernetes resources only. This would avoid a race between the operator and the user trying to create tags with the same name simultaneously. + +Due to the complexity of the task we have moved it into a separate ticket: [#439 Create default revision tag automatically](https://github.com/istio-ecosystem/sail-operator/issues/439). + +## Implementation Plan +v1alpha1 +- [ ] Initial implementation & tests (https://github.com/istio-ecosystem/sail-operator/pull/413) +- [ ] Documentation + +v1beta1 +- [ ] [#439 Create default revision tag automatically](https://github.com/istio-ecosystem/sail-operator/issues/439) +- [ ] [#471 Support revision tags in multicluster topologies](https://github.com/istio-ecosystem/sail-operator/issues/471) + +## Test Plan +Functionality will be tested in integration tests. diff --git a/go.mod b/go.mod index 78bb6deca..2f8fc59cd 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,8 @@ module github.com/istio-ecosystem/sail-operator -go 1.22.0 +go 1.23 + +toolchain go1.23.2 // Client-go does not handle different versions of mergo due to some breaking changes - use the matching version // This replacement is aligned with istio/istio's go.mod @@ -13,24 +15,24 @@ require ( github.com/google/go-cmp v0.6.0 github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.4.0 github.com/magiconair/properties v1.8.7 - github.com/onsi/ginkgo/v2 v2.20.1 - github.com/onsi/gomega v1.34.2 - github.com/prometheus/common v0.60.0 + github.com/onsi/ginkgo/v2 v2.20.2 + github.com/onsi/gomega v1.35.1 + github.com/prometheus/common v0.60.1 github.com/stretchr/testify v1.9.0 - golang.org/x/mod v0.20.0 - golang.org/x/text v0.18.0 - golang.org/x/tools v0.24.0 + golang.org/x/mod v0.21.0 + golang.org/x/text v0.20.0 + golang.org/x/tools v0.26.0 gomodules.xyz/jsonpatch/v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 - helm.sh/helm/v3 v3.16.1 - istio.io/client-go v1.24.0-alpha.0.0.20241018201953-b3ca3b2a6ef6 - istio.io/istio v0.0.0-20241022213331-bb972b546125 - k8s.io/api v0.31.1 - k8s.io/apiextensions-apiserver v0.31.1 - k8s.io/apimachinery v0.31.1 - k8s.io/cli-runtime v0.31.1 - k8s.io/client-go v0.31.1 - sigs.k8s.io/controller-runtime v0.19.0 + helm.sh/helm/v3 v3.16.3 + istio.io/client-go v1.24.0-alpha.0.0.20241123091016-df47c87e86db + istio.io/istio v0.0.0-20241129034931-4b76e91786ce + k8s.io/api v0.31.2 + k8s.io/apiextensions-apiserver v0.31.2 + k8s.io/apimachinery v0.31.2 + k8s.io/cli-runtime v0.31.2 + k8s.io/client-go v0.31.2 + sigs.k8s.io/controller-runtime v0.19.1 ) require ( @@ -44,20 +46,21 @@ require ( github.com/Masterminds/sprig v2.22.0+incompatible // indirect github.com/Masterminds/sprig/v3 v3.3.0 // indirect github.com/Masterminds/squirrel v1.5.4 // indirect - github.com/Microsoft/hcsshim v0.11.4 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect - github.com/containerd/containerd v1.7.12 // indirect + github.com/containerd/containerd v1.7.23 // indirect + github.com/containerd/errdefs v0.3.0 // indirect github.com/containerd/log v0.1.0 // indirect - github.com/cyphar/filepath-securejoin v0.3.1 // indirect + github.com/containerd/platforms v0.2.1 // indirect + github.com/cyphar/filepath-securejoin v0.3.4 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/distribution/reference v0.5.0 // indirect + github.com/distribution/reference v0.6.0 // indirect github.com/docker/cli v27.3.1+incompatible // indirect github.com/docker/distribution v2.8.3+incompatible // indirect - github.com/docker/docker v26.0.2+incompatible // indirect + github.com/docker/docker v26.1.5+incompatible // indirect github.com/docker/docker-credential-helpers v0.8.2 // indirect github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-metrics v0.0.1 // indirect @@ -65,9 +68,9 @@ require ( github.com/evanphx/json-patch v5.9.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect - github.com/fatih/color v1.17.0 // indirect + github.com/fatih/color v1.18.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-errors/errors v1.5.1 // indirect github.com/go-gorp/gorp/v3 v3.1.0 // indirect @@ -101,7 +104,7 @@ require ( github.com/jmoiron/sqlx v1.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.17.9 // indirect + github.com/klauspost/compress v1.17.11 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/lib/pq v1.10.9 // indirect @@ -126,7 +129,7 @@ require ( github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_golang v1.20.4 // indirect + github.com/prometheus/client_golang v1.20.5 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/rivo/uniseg v0.4.6 // indirect @@ -142,37 +145,37 @@ require ( github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xlab/treeprint v1.2.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect - go.opentelemetry.io/otel v1.30.0 // indirect - go.opentelemetry.io/otel/metric v1.30.0 // indirect - go.opentelemetry.io/otel/trace v1.30.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 // indirect + go.opentelemetry.io/otel v1.32.0 // indirect + go.opentelemetry.io/otel/metric v1.32.0 // indirect + go.opentelemetry.io/otel/trace v1.32.0 // indirect go.starlark.net v0.0.0-20231121155337-90ade8b19d09 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/crypto v0.27.0 // indirect - golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect - golang.org/x/net v0.29.0 // indirect - golang.org/x/oauth2 v0.23.0 // indirect - golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.25.0 // indirect - golang.org/x/term v0.24.0 // indirect - golang.org/x/time v0.6.0 // indirect + golang.org/x/crypto v0.29.0 // indirect + golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect + golang.org/x/net v0.31.0 // indirect + golang.org/x/oauth2 v0.24.0 // indirect + golang.org/x/sync v0.9.0 // indirect + golang.org/x/sys v0.27.0 // indirect + golang.org/x/term v0.26.0 // indirect + golang.org/x/time v0.8.0 // indirect golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240930140551-af27646dc61f // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240930140551-af27646dc61f // indirect - google.golang.org/grpc v1.67.1 // indirect - google.golang.org/protobuf v1.34.2 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20241113202542-65e8d215514f // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241113202542-65e8d215514f // indirect + google.golang.org/grpc v1.68.0 // indirect + google.golang.org/protobuf v1.35.2 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gotest.tools/v3 v3.5.1 // indirect - istio.io/api v1.24.0-alpha.0.0.20241018201654-7c8ec5b5ab72 // indirect - k8s.io/apiserver v0.31.1 // indirect - k8s.io/component-base v0.31.1 // indirect + istio.io/api v1.24.0-alpha.0.0.20241123090716-918717d1a2a5 // indirect + k8s.io/apiserver v0.31.2 // indirect + k8s.io/component-base v0.31.2 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20240423202451-8948a665c108 // indirect - k8s.io/kubectl v0.31.1 // indirect - k8s.io/utils v0.0.0-20240921022957-49e7df575cb6 // indirect + k8s.io/kubectl v0.31.2 // indirect + k8s.io/utils v0.0.0-20241104163129-6fe5fd82f078 // indirect oras.land/oras-go v1.2.5 // indirect sigs.k8s.io/controller-tools v0.15.0 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect diff --git a/go.sum b/go.sum index 98405b31d..259942ae9 100644 --- a/go.sum +++ b/go.sum @@ -24,10 +24,10 @@ github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM= github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= -github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= -github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8= -github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/Microsoft/hcsshim v0.11.7 h1:vl/nj3Bar/CvJSYo7gIQPyRWc9f3c6IeSNavBTSZNZQ= +github.com/Microsoft/hcsshim v0.11.7/go.mod h1:MV8xMfmECjl5HdO7U/3/hFVnkmSBjAjmA09d4bExKcU= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -56,31 +56,35 @@ github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNS github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= -github.com/containerd/containerd v1.7.12 h1:+KQsnv4VnzyxWcfO9mlxxELaoztsDEjOuCMPAuPqgU0= -github.com/containerd/containerd v1.7.12/go.mod h1:/5OMpE1p0ylxtEUGY8kuCYkDRzJm9NO1TFMWjUpdevk= +github.com/containerd/containerd v1.7.23 h1:H2CClyUkmpKAGlhQp95g2WXHfLYc7whAuvZGBNYOOwQ= +github.com/containerd/containerd v1.7.23/go.mod h1:7QUzfURqZWCZV7RLNEn1XjUCQLEf0bkaK4GjUaZehxw= github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= +github.com/containerd/errdefs v0.3.0 h1:FSZgGOeK4yuT/+DnF07/Olde/q4KBoMsaamhXxIMDp4= +github.com/containerd/errdefs v0.3.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= +github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= -github.com/cyphar/filepath-securejoin v0.3.1 h1:1V7cHiaW+C+39wEfpH6XlLBQo3j/PciWFrgfCLS8XrE= -github.com/cyphar/filepath-securejoin v0.3.1/go.mod h1:F7i41x/9cBF7lzCrVsYs9fuzwRZm4NQsGTBdpp6mETc= +github.com/cyphar/filepath-securejoin v0.3.4 h1:VBWugsJh2ZxJmLFSM06/0qzQyiQX2Qs0ViKrUAcqdZ8= +github.com/cyphar/filepath-securejoin v0.3.4/go.mod h1:8s/MCNJREmFK0H02MF6Ihv1nakJe4L/w3WZLHNkvlYM= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2 h1:aBfCb7iqHmDEIp6fBvC/hQUddQfg+3qdYjwzaiP9Hnc= github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2/go.mod h1:WHNsWjnIn2V1LYOrME7e8KxSeKunYHsxEm4am0BUtcI= -github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= -github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/cli v27.3.1+incompatible h1:qEGdFBF3Xu6SCvCYhc7CzaQTlBmqDuzxPDpigSyeKQQ= github.com/docker/cli v27.3.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v26.0.2+incompatible h1:yGVmKUFGgcxA6PXWAokO0sQL22BrQ67cgVjko8tGdXE= -github.com/docker/docker v26.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v26.1.5+incompatible h1:NEAxTwEjxV6VbBMBoGG3zPqbiJosIApZjxlbrG9q3/g= +github.com/docker/docker v26.1.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo= github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= @@ -101,16 +105,16 @@ 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/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f h1:Wl78ApPPB2Wvf/TIe2xdyJxTlb6obmF18d8QdkxNDu4= github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f/go.mod h1:OSYXu++VVOHnXeitef/D8n/6y4QV8uLHSFXX4NeXMGc= -github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= -github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/foxcpp/go-mockdns v1.1.0 h1:jI0rD8M0wuYAxL7r/ynTrCQQq0BVqfB99Vgk7DlmewI= github.com/foxcpp/go-mockdns v1.1.0/go.mod h1:IhLeSFGed3mJIAXPH2aiRQB+kqz7oqu8ld2qVbOu7Wk= 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/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= +github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= 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-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk= @@ -215,8 +219,8 @@ github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.4.0 h1:V github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.4.0/go.mod h1:nqCI7aelBJU61wiBeeZWJ6oi4bJy5nrjkM6lWIMA4j0= 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.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= -github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/konsorten/go-windows-terminal-sequences v1.0.1/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.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -263,6 +267,8 @@ github.com/moby/spdystream v0.4.0 h1:Vy79D6mHeJJjiPdFEL2yku1kl0chZpJfZcPpb16BRl8 github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78= github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= +github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= +github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -283,10 +289,10 @@ 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.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.20.1 h1:YlVIbqct+ZmnEph770q9Q7NVAz4wwIiVNahee6JyUzo= -github.com/onsi/ginkgo/v2 v2.20.1/go.mod h1:lG9ey2Z29hR41WMVthyJBGUBcBhGOtoPF2VFMvBXFCI= -github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8= -github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc= +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.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= +github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= @@ -306,16 +312,16 @@ github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjz 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.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= -github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI= -github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= 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.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.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJNllA= -github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= +github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc= +github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= 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.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= @@ -379,14 +385,14 @@ github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1 github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -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.30.0 h1:F2t8sK4qf1fAmY9ua4ohFS/K+FUuOPemHUIXHtktrts= -go.opentelemetry.io/otel v1.30.0/go.mod h1:tFw4Br9b7fOS+uEao81PJjVMjW/5fvNCbpsDIXqP0pc= -go.opentelemetry.io/otel/metric v1.30.0 h1:4xNulvn9gjzo4hjg+wzIKG7iNFEaBMX00Qd4QIZs7+w= -go.opentelemetry.io/otel/metric v1.30.0/go.mod h1:aXTfST94tswhWEb+5QjlSqG+cZlmyXy/u8jFpor3WqQ= -go.opentelemetry.io/otel/trace v1.30.0 h1:7UBkkYzeg3C7kQX8VAidWh2biiQbtAKjyIML8dQ9wmc= -go.opentelemetry.io/otel/trace v1.30.0/go.mod h1:5EyKqTzzmyqB9bwtCCq6pDLktPK6fmGf/Dph+8VI02o= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM= +go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= +go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= +go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= +go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= +go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= +go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= go.starlark.net v0.0.0-20231121155337-90ade8b19d09 h1:hzy3LFnSN8kuQK8h9tHl4ndF6UruMj47OqwqsS+/Ai4= go.starlark.net v0.0.0-20231121155337-90ade8b19d09/go.mod h1:LcLNIzVOMp4oV+uusnpk+VU+SzXaJakUuBjoCSWH5dM= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= @@ -401,31 +407,31 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 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.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= -golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= -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/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= +golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= +golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= +golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= 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.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= -golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= +golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 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-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= -golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= -golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= -golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= +golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= +golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= +golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= 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-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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= +golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 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= @@ -436,22 +442,22 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= -golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= +golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= -golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= +golang.org/x/time v0.8.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-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= -golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= +golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= 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= @@ -460,14 +466,14 @@ golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 h1:LLhsEBxRTBLuKlQxFBYUO golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= 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/genproto/googleapis/api v0.0.0-20240930140551-af27646dc61f h1:jTm13A2itBi3La6yTGqn8bVSrc3ZZ1r8ENHlIXBfnRA= -google.golang.org/genproto/googleapis/api v0.0.0-20240930140551-af27646dc61f/go.mod h1:CLGoBuH1VHxAUXVPP8FfPwPEVJB6lz3URE5mY2SuayE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240930140551-af27646dc61f h1:cUMEy+8oS78BWIH9OWazBkzbr090Od9tWBNtZHkOhf0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240930140551-af27646dc61f/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= -google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= -google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/genproto/googleapis/api v0.0.0-20241113202542-65e8d215514f h1:M65LEviCfuZTfrfzwwEoxVtgvfkFkBUbFnRbxCXuXhU= +google.golang.org/genproto/googleapis/api v0.0.0-20241113202542-65e8d215514f/go.mod h1:Yo94eF2nj7igQt+TiJ49KxjIH8ndLYPZMIRSiRcEbg0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241113202542-65e8d215514f h1:C1QccEa9kUwvMgEUORqQD9S17QesQijxjZ84sO82mfo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241113202542-65e8d215514f/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= +google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0= +google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA= +google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= +google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= 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-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= @@ -487,40 +493,40 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= -helm.sh/helm/v3 v3.16.1 h1:cER6tI/8PgUAsaJaQCVBUg3VI9KN4oVaZJgY60RIc0c= -helm.sh/helm/v3 v3.16.1/go.mod h1:r+xBHHP20qJeEqtvBXMf7W35QDJnzY/eiEBzt+TfHps= -istio.io/api v1.24.0-alpha.0.0.20241018201654-7c8ec5b5ab72 h1:AVg/4p5sVhZT6JwBczgvAy9idbVYiCqZFE/QVXNKy/k= -istio.io/api v1.24.0-alpha.0.0.20241018201654-7c8ec5b5ab72/go.mod h1:MQnRok7RZ20/PE56v0LxmoWH0xVxnCQPNuf9O7PAN1I= -istio.io/client-go v1.24.0-alpha.0.0.20241018201953-b3ca3b2a6ef6 h1:qVjgBbqg19vZCpeTMQR0QM8SRfLZTtaSXgWbnWRb0fo= -istio.io/client-go v1.24.0-alpha.0.0.20241018201953-b3ca3b2a6ef6/go.mod h1:usBQZ/vvpGAUA6yGiz6x9ufG50gRC9v0332MesA/lNw= -istio.io/istio v0.0.0-20241022213331-bb972b546125 h1:mliFVPGvuQ9XCZZIG9fSQsI1AVJqz6MUIWQVAlM3MJI= -istio.io/istio v0.0.0-20241022213331-bb972b546125/go.mod h1:27Ub5DbzERxmDSo6hWl+/eaSKOmO3UctQ7c0TPtKUFU= -k8s.io/api v0.31.1 h1:Xe1hX/fPW3PXYYv8BlozYqw63ytA92snr96zMW9gWTU= -k8s.io/api v0.31.1/go.mod h1:sbN1g6eY6XVLeqNsZGLnI5FwVseTrZX7Fv3O26rhAaI= -k8s.io/apiextensions-apiserver v0.31.1 h1:L+hwULvXx+nvTYX/MKM3kKMZyei+UiSXQWciX/N6E40= -k8s.io/apiextensions-apiserver v0.31.1/go.mod h1:tWMPR3sgW+jsl2xm9v7lAyRF1rYEK71i9G5dRtkknoQ= -k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U= -k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= -k8s.io/apiserver v0.31.1 h1:Sars5ejQDCRBY5f7R3QFHdqN3s61nhkpaX8/k1iEw1c= -k8s.io/apiserver v0.31.1/go.mod h1:lzDhpeToamVZJmmFlaLwdYZwd7zB+WYRYIboqA1kGxM= -k8s.io/cli-runtime v0.31.1 h1:/ZmKhmZ6hNqDM+yf9s3Y4KEYakNXUn5sod2LWGGwCuk= -k8s.io/cli-runtime v0.31.1/go.mod h1:pKv1cDIaq7ehWGuXQ+A//1OIF+7DI+xudXtExMCbe9U= -k8s.io/client-go v0.31.1 h1:f0ugtWSbWpxHR7sjVpQwuvw9a3ZKLXX0u0itkFXufb0= -k8s.io/client-go v0.31.1/go.mod h1:sKI8871MJN2OyeqRlmA4W4KM9KBdBUpDLu/43eGemCg= -k8s.io/component-base v0.31.1 h1:UpOepcrX3rQ3ab5NB6g5iP0tvsgJWzxTyAo20sgYSy8= -k8s.io/component-base v0.31.1/go.mod h1:WGeaw7t/kTsqpVTaCoVEtillbqAhF2/JgvO0LDOMa0w= +helm.sh/helm/v3 v3.16.3 h1:kb8bSxMeRJ+knsK/ovvlaVPfdis0X3/ZhYCSFRP+YmY= +helm.sh/helm/v3 v3.16.3/go.mod h1:zeVWGDR4JJgiRbT3AnNsjYaX8OTJlIE9zC+Q7F7iUSU= +istio.io/api v1.24.0-alpha.0.0.20241123090716-918717d1a2a5 h1:YkSovYqcLmX7W1grfnyRfqsRuxenxi1UOFOksQZcq+M= +istio.io/api v1.24.0-alpha.0.0.20241123090716-918717d1a2a5/go.mod h1:MQnRok7RZ20/PE56v0LxmoWH0xVxnCQPNuf9O7PAN1I= +istio.io/client-go v1.24.0-alpha.0.0.20241123091016-df47c87e86db h1:aVs0RM/sBTjEF6aOuiB6Vdu8oBZWf8dX60FyOX2a0ug= +istio.io/client-go v1.24.0-alpha.0.0.20241123091016-df47c87e86db/go.mod h1:Sjf49Th3Q1mo9jtKinGWR05KZMxv1iS20z/ow4DST88= +istio.io/istio v0.0.0-20241129034931-4b76e91786ce h1:+k7YWqvlP6eYB7d0+tSzx5lfQMdAJlNlCjQ5/oAkFEU= +istio.io/istio v0.0.0-20241129034931-4b76e91786ce/go.mod h1:lJKVrMqVspY39QrL9cxHe9qQRVJ+2lN1wlx9AqRJBG4= +k8s.io/api v0.31.2 h1:3wLBbL5Uom/8Zy98GRPXpJ254nEFpl+hwndmk9RwmL0= +k8s.io/api v0.31.2/go.mod h1:bWmGvrGPssSK1ljmLzd3pwCQ9MgoTsRCuK35u6SygUk= +k8s.io/apiextensions-apiserver v0.31.2 h1:W8EwUb8+WXBLu56ser5IudT2cOho0gAKeTOnywBLxd0= +k8s.io/apiextensions-apiserver v0.31.2/go.mod h1:i+Geh+nGCJEGiCGR3MlBDkS7koHIIKWVfWeRFiOsUcM= +k8s.io/apimachinery v0.31.2 h1:i4vUt2hPK56W6mlT7Ry+AO8eEsyxMD1U44NR22CLTYw= +k8s.io/apimachinery v0.31.2/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/apiserver v0.31.2 h1:VUzOEUGRCDi6kX1OyQ801m4A7AUPglpsmGvdsekmcI4= +k8s.io/apiserver v0.31.2/go.mod h1:o3nKZR7lPlJqkU5I3Ove+Zx3JuoFjQobGX1Gctw6XuE= +k8s.io/cli-runtime v0.31.2 h1:7FQt4C4Xnqx8V1GJqymInK0FFsoC+fAZtbLqgXYVOLQ= +k8s.io/cli-runtime v0.31.2/go.mod h1:XROyicf+G7rQ6FQJMbeDV9jqxzkWXTYD6Uxd15noe0Q= +k8s.io/client-go v0.31.2 h1:Y2F4dxU5d3AQj+ybwSMqQnpZH9F30//1ObxOKlTI9yc= +k8s.io/client-go v0.31.2/go.mod h1:NPa74jSVR/+eez2dFsEIHNa+3o09vtNaWwWwb1qSxSs= +k8s.io/component-base v0.31.2 h1:Z1J1LIaC0AV+nzcPRFqfK09af6bZ4D1nAOpWsy9owlA= +k8s.io/component-base v0.31.2/go.mod h1:9PeyyFN/drHjtJZMCTkSpQJS3U9OXORnHQqMLDz0sUQ= 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-20240423202451-8948a665c108 h1:Q8Z7VlGhcJgBHJHYugJ/K/7iB8a2eSxCyxdVjJp+lLY= k8s.io/kube-openapi v0.0.0-20240423202451-8948a665c108/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= -k8s.io/kubectl v0.31.1 h1:ih4JQJHxsEggFqDJEHSOdJ69ZxZftgeZvYo7M/cpp24= -k8s.io/kubectl v0.31.1/go.mod h1:aNuQoR43W6MLAtXQ/Bu4GDmoHlbhHKuyD49lmTC8eJM= -k8s.io/utils v0.0.0-20240921022957-49e7df575cb6 h1:MDF6h2H/h4tbzmtIKTuctcwZmY0tY9mD9fNT47QO6HI= -k8s.io/utils v0.0.0-20240921022957-49e7df575cb6/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/kubectl v0.31.2 h1:gTxbvRkMBwvTSAlobiTVqsH6S8Aa1aGyBcu5xYLsn8M= +k8s.io/kubectl v0.31.2/go.mod h1:EyASYVU6PY+032RrTh5ahtSOMgoDRIux9V1JLKtG5xM= +k8s.io/utils v0.0.0-20241104163129-6fe5fd82f078 h1:jGnCPejIetjiy2gqaJ5V0NLwTpF4wbQ6cZIItJCSHno= +k8s.io/utils v0.0.0-20241104163129-6fe5fd82f078/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= oras.land/oras-go v1.2.5 h1:XpYuAwAb0DfQsunIyMfeET92emK8km3W4yEzZvUbsTo= oras.land/oras-go v1.2.5/go.mod h1:PuAwRShRZCsZb7g8Ar3jKKQR/2A/qN+pkYxIOd/FAoo= -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/controller-runtime v0.19.1 h1:Son+Q40+Be3QWb+niBXAg2vFiYWolDjjRfO8hn/cxOk= +sigs.k8s.io/controller-runtime v0.19.1/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= sigs.k8s.io/controller-tools v0.15.0 h1:4dxdABXGDhIa68Fiwaif0vcu32xfwmgQ+w8p+5CxoAI= sigs.k8s.io/controller-tools v0.15.0/go.mod h1:8zUSS2T8Hx0APCNRhJWbS3CAQEbIxLa07khzh7pZmXM= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= diff --git a/hack/api_transformer/transform.yaml b/hack/api_transformer/transform.yaml index 6b77497ae..7266c1440 100644 --- a/hack/api_transformer/transform.yaml +++ b/hack/api_transformer/transform.yaml @@ -69,6 +69,7 @@ inputFiles: - Values.Gateways # operator doesn't support deployment of ingress/egress gateways - Values.OwnerName # operator sets this internally - Values.Ztunnel # ambient is not yet supported + - Values.RevisionTags # Revision Tags are only supported through IstioRevisionTag CRD - SidecarInjectorConfig.ObjectSelector # appears to be unused - CNIConfig.Enabled # CNI is enabled by the mere presence of the IstioCNI resource - CNIConfig.LogLevel # deprecated and replaced with CNIConfig.logging.level diff --git a/hack/download-charts.sh b/hack/download-charts.sh index 799cc719c..c674cd93e 100755 --- a/hack/download-charts.sh +++ b/hack/download-charts.sh @@ -85,6 +85,9 @@ function patchIstioCharts() { echo "patching istio charts ${CHARTS_DIR}/cni/templates/clusterrole.yaml " # NOTE: everything in the patchIstioCharts should be here only temporarily, # until we push the required changes upstream + + # add permissions for CNI to use the privileged SCC. This has been added upstream in 1.23.0, + # so this can be removed once we remove support for versions <1.23 sed -i '0,/rules:/s//rules:\ - apiGroups: ["security.openshift.io"] \ resources: ["securitycontextconstraints"] \ @@ -96,9 +99,6 @@ function patchIstioCharts() { } function convertIstioProfiles() { - # delete the remote profile, because it isn't needed (we have the RemoteIstio resource instead) - [ -f "${PROFILES_DIR}"/remote.yaml ] && rm "${PROFILES_DIR}"/remote.yaml - # delete the minimal profile, because it ends up being empty after the conversion [ -f "${PROFILES_DIR}"/minimal.yaml ] && rm "${PROFILES_DIR}"/minimal.yaml @@ -118,6 +118,22 @@ function convertIstioProfiles() { done } +function createRevisionTagChart() { + mkdir -p "${CHARTS_DIR}/revisiontags/templates" + echo "apiVersion: v2 +appVersion: ${ISTIO_VERSION} +description: Helm chart for istio revision tags +name: revisiontags +sources: +- https://github.com/istio-ecosystem/sail-operator +version: 0.1.0 +" > "${CHARTS_DIR}/revisiontags/Chart.yaml" + cp "${CHARTS_DIR}/istiod/values.yaml" "${CHARTS_DIR}/revisiontags/values.yaml" + cp "${CHARTS_DIR}/istiod/templates/revision-tags.yaml" "${CHARTS_DIR}/revisiontags/templates/revision-tags.yaml" + cp "${CHARTS_DIR}/istiod/templates/zzz_profile.yaml" "${CHARTS_DIR}/revisiontags/templates/zzz_profile.yaml" +} + downloadIstioManifests patchIstioCharts -convertIstioProfiles \ No newline at end of file +convertIstioProfiles +createRevisionTagChart diff --git a/hack/update-istio.sh b/hack/update-istio.sh index b85508e7e..43d7efb43 100755 --- a/hack/update-istio.sh +++ b/hack/update-istio.sh @@ -26,6 +26,12 @@ VERSIONS_YAML_FILE=${VERSIONS_YAML_FILE:-"versions.yaml"} # The new entry will be placed immediately before the old one function add_stable_version() { echo "Adding new stable version: ${1}" + # we want to add the istiod-remote chart only for 1.23 + istiod_remote_line="" + if [[ ${1} == 1.23.* ]] + then + istiod_remote_line="\"https://istio-release.storage.googleapis.com/charts/istiod-remote-${1}.tgz\"," + fi template=$(cat <<-END { "name": "v${1}", @@ -35,7 +41,7 @@ function add_stable_version() { "charts": [ "https://istio-release.storage.googleapis.com/charts/base-${1}.tgz", "https://istio-release.storage.googleapis.com/charts/istiod-${1}.tgz", - "https://istio-release.storage.googleapis.com/charts/istiod-remote-${1}.tgz", + ${istiod_remote_line} "https://istio-release.storage.googleapis.com/charts/gateway-${1}.tgz", "https://istio-release.storage.googleapis.com/charts/cni-${1}.tgz", "https://istio-release.storage.googleapis.com/charts/ztunnel-${1}.tgz" @@ -43,6 +49,7 @@ function add_stable_version() { } END ) + # Insert the new key above the old one (https://stackoverflow.com/questions/74368503/is-it-possible-to-insert-an-element-into-a-middle-of-array-in-yaml-using-yq) # shellcheck disable=SC2016 yq -i '.versions |= ( diff --git a/hack/update-profiles-list.sh b/hack/update-profiles-list.sh index d2d41a016..b838b1f49 100755 --- a/hack/update-profiles-list.sh +++ b/hack/update-profiles-list.sh @@ -39,4 +39,4 @@ sed -i -E \ -e "/\+sail:profile/,/Profile string/ s/(\/\/ \+operator-sdk:csv:customresourcedefinitions:type=spec,displayName=\"Profile\",xDescriptors=\{.*fieldGroup:General\")[^}]*(})/\1$selectValues}/g" \ -e "/\+sail:profile/,/Profile string/ s/(\/\/ \+kubebuilder:validation:Enum=)(.*)/\1$enumValues/g" \ -e "/\+sail:profile/,/Profile string/ s/(\/\/ Must be one of:)(.*)/\1 ${profiles//,/, }./g" \ - api/v1alpha1/istio_types.go api/v1alpha1/remoteistio_types.go api/v1alpha1/istiocni_types.go + api/v1alpha1/istio_types.go api/v1alpha1/istiocni_types.go diff --git a/hack/update-version-list.sh b/hack/update-version-list.sh index 54ab56e2f..fdf39b3a9 100755 --- a/hack/update-version-list.sh +++ b/hack/update-version-list.sh @@ -31,7 +31,7 @@ function updateVersionsInIstioTypeComment() { -e "/\+sail:version/,/Version string/ s/(\/\/ \+kubebuilder:default=)(.*)/\1$defaultVersion/g" \ -e "/\+sail:version/,/Version string/ s/(\/\/ \Must be one of:)(.*)/\1 $versions./g" \ -e "s/(\+kubebuilder:default=.*version: \")[^\"]*\"/\1$defaultVersion\"/g" \ - api/v1alpha1/istio_types.go api/v1alpha1/remoteistio_types.go api/v1alpha1/istiorevision_types.go api/v1alpha1/istiocni_types.go + api/v1alpha1/istio_types.go api/v1alpha1/istiorevision_types.go api/v1alpha1/istiocni_types.go } function updateVersionsInCSVDescription() { diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index 5f88662fa..34169e316 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -65,6 +65,10 @@ const ( // reports whether the remote control plane is ready or not WebhookReadinessProbeStatusAnnotationKey = MetadataNamespace + "/readinessProbe.status" + // WebhookReadinessProbeStatusReasonAnnotationKey is an annotation on the istio-sidecar-injection MutatingWebhookConfiguration that + // reports why the remote control plane is not ready + WebhookReadinessProbeStatusReasonAnnotationKey = MetadataNamespace + "/readinessProbe.reason" + // WebhookReadinessProbePeriodSecondsAnnotationKey is an annotation on the istio-sidecar-injection MutatingWebhookConfiguration that // specifies the period for the readiness probe WebhookReadinessProbePeriodSecondsAnnotationKey = MetadataNamespace + "/readinessProbe.periodSeconds" @@ -72,4 +76,19 @@ const ( // WebhookReadinessProbeTimeoutSecondsAnnotationKey is an annotation on the istio-sidecar-injection MutatingWebhookConfiguration that // specifies the timeout for the readiness probe WebhookReadinessProbeTimeoutSecondsAnnotationKey = MetadataNamespace + "/readinessProbe.timeoutSeconds" + + // IstioInjectionLabel is the label that is used to configure injection for the 'default' IstioRevision + IstioInjectionLabel = "istio-injection" + + // IstioInjectionEnabledValue is the value for IstioInjectionLabel + IstioInjectionEnabledValue = "enabled" + + // IstioRevLabel is the label that is used to configure injection for non-default IstioRevisions + IstioRevLabel = "istio.io/rev" + + // IstioSidecarInjectLabel is the label that is used to configure injection for specific workloads + IstioSidecarInjectLabel = "sidecar.istio.io/inject" + + // IstiodChartName is the name of the chart that installs istiod + IstiodChartName = "istiod" ) diff --git a/pkg/helm/values.go b/pkg/helm/values.go index 450d10e10..6467a33ab 100644 --- a/pkg/helm/values.go +++ b/pkg/helm/values.go @@ -42,6 +42,12 @@ func (h *Values) Set(key string, val any) error { return unstructured.SetNestedField(*h, val, toKeys(key)...) } +// Set sets the value of a nested string slice to the value provided. +// Returns an error if value cannot be set because one of the nesting levels is not a map[string]any. +func (h *Values) SetStringSlice(key string, val []string) error { + return unstructured.SetNestedStringSlice(*h, val, toKeys(key)...) +} + // SetIfAbsent sets the value of a nested field to a deep copy of the value // provided if the field does not exist. func (h *Values) SetIfAbsent(key string, val any) error { diff --git a/pkg/predicate/predicate.go b/pkg/predicate/predicate.go new file mode 100644 index 000000000..fc4f7466c --- /dev/null +++ b/pkg/predicate/predicate.go @@ -0,0 +1,27 @@ +// Copyright Istio Authors +// +// 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 predicate + +import ( + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/predicate" +) + +// ignoreUpdate returns a predicate that ignores update events. +func IgnoreUpdate() predicate.Funcs { + return predicate.Funcs{ + UpdateFunc: func(e event.UpdateEvent) bool { return false }, + } +} diff --git a/pkg/reconciler/errors.go b/pkg/reconciler/errors.go index 3fd452990..eb3c5c004 100644 --- a/pkg/reconciler/errors.go +++ b/pkg/reconciler/errors.go @@ -32,3 +32,21 @@ func IsValidationError(err error) bool { e := &ValidationError{} return errors.As(err, &e) } + +// TransientError is an error returned by a Reconciler that will usually resolve itself when retrying, e.g. some resource not yet reconciled +type TransientError struct { + message string +} + +func (v TransientError) Error() string { + return "transient error: " + v.message +} + +func NewTransientError(message string) error { + return &TransientError{message: message} +} + +func IsTransientError(err error) bool { + e := &TransientError{} + return errors.As(err, &e) +} diff --git a/pkg/reconciler/reconciler.go b/pkg/reconciler/reconciler.go index d61cf03e7..b0b43002e 100644 --- a/pkg/reconciler/reconciler.go +++ b/pkg/reconciler/reconciler.go @@ -99,6 +99,12 @@ func (r *StandardReconciler[T]) Reconcile(ctx context.Context, req ctrl.Request) case errors.IsConflict(err): log.Info("Conflict detected. Retrying...") return ctrl.Result{Requeue: true}, nil + case errors.IsNotFound(err): + log.Info("Resource not found. Retrying...", "error", err) + return ctrl.Result{Requeue: true}, nil + case IsTransientError(err): + log.Info("Reconciliation failed. Retrying...", "error", err) + return ctrl.Result{Requeue: true}, nil case IsValidationError(err): log.Info("Validation failed", "error", err) return ctrl.Result{}, nil diff --git a/pkg/revision/reconcile.go b/pkg/revision/reconcile.go index a7fcea3fb..e78d1fd0d 100644 --- a/pkg/revision/reconcile.go +++ b/pkg/revision/reconcile.go @@ -26,7 +26,8 @@ import ( logf "sigs.k8s.io/controller-runtime/pkg/log" ) -func CreateOrUpdate(ctx context.Context, cl client.Client, revName string, revType v1alpha1.IstioRevisionType, version string, namespace string, +func CreateOrUpdate( + ctx context.Context, cl client.Client, revName string, version string, namespace string, values *v1alpha1.Values, ownerRef metav1.OwnerReference, ) error { log := logf.FromContext(ctx) @@ -53,7 +54,6 @@ func CreateOrUpdate(ctx context.Context, cl client.Client, revName string, revTy OwnerReferences: []metav1.OwnerReference{ownerRef}, }, Spec: v1alpha1.IstioRevisionSpec{ - Type: revType, Version: version, Namespace: namespace, Values: values, diff --git a/pkg/revision/reconcile_test.go b/pkg/revision/reconcile_test.go index 86a350b22..f48f94bb9 100644 --- a/pkg/revision/reconcile_test.go +++ b/pkg/revision/reconcile_test.go @@ -98,8 +98,7 @@ func TestReconcileActiveRevision(t *testing.T) { Controller: ptr.Of(true), BlockOwnerDeletion: ptr.Of(true), } - revType := v1alpha1.IstioRevisionTypeLocal - err := CreateOrUpdate(ctx, cl, "my-revision", revType, version, "istio-system", &tc.istioValues, ownerRef) + err := CreateOrUpdate(ctx, cl, "my-revision", version, "istio-system", &tc.istioValues, ownerRef) if err != nil { t.Errorf("Expected no error, but got: %v", err) } diff --git a/pkg/revision/remote.go b/pkg/revision/remote.go new file mode 100644 index 000000000..068963cc7 --- /dev/null +++ b/pkg/revision/remote.go @@ -0,0 +1,25 @@ +// Copyright Istio Authors +// +// 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 revision + +import "github.com/istio-ecosystem/sail-operator/api/v1alpha1" + +// IsUsingRemoteControlPlane returns true if the IstioRevision is configured to +// connect to a remote rather than deploy a local control plane. +func IsUsingRemoteControlPlane(rev *v1alpha1.IstioRevision) bool { + // TODO: we should use values.istiodRemote.enabled instead of the profile, but we can't get the final set of values because of new profiles implementation + values := rev.Spec.Values + return values != nil && values.Profile != nil && *values.Profile == "remote" +} diff --git a/pkg/test/environment.go b/pkg/test/environment.go index 5f46853a5..ea379025f 100644 --- a/pkg/test/environment.go +++ b/pkg/test/environment.go @@ -40,6 +40,10 @@ func SetupEnv(logWriter io.Writer, installCRDs bool) (*envtest.Environment, clie ErrorIfCRDPathMissing: true, } + // disabling mutatingwebhooks to avoid failing calls to the injection webhooks + // once we implement mutatingwebhooks in the operator we might have to find another way + testEnv.ControlPlane.GetAPIServer().Configure().Append("disable-admission-plugins", "MutatingAdmissionWebhook") + cfg, err := testEnv.Start() if err != nil || cfg == nil { panic(err) diff --git a/pkg/validation/namespace.go b/pkg/validation/namespace.go index 8e95eb9b3..09c493059 100644 --- a/pkg/validation/namespace.go +++ b/pkg/validation/namespace.go @@ -18,6 +18,7 @@ import ( "context" "fmt" + "github.com/istio-ecosystem/sail-operator/api/v1alpha1" "github.com/istio-ecosystem/sail-operator/pkg/reconciler" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -39,3 +40,14 @@ func ValidateTargetNamespace(ctx context.Context, cl client.Client, namespace st } return nil } + +func IstioRevisionTagExists(ctx context.Context, cl client.Client, name string) (bool, error) { + tag := &v1alpha1.IstioRevisionTag{} + if err := cl.Get(ctx, types.NamespacedName{Name: name}, tag); err != nil { + if apierrors.IsNotFound(err) { + return false, nil + } + return false, err + } + return true, nil +} diff --git a/pkg/version/semverutils.go b/pkg/version/semverutils.go new file mode 100644 index 000000000..4417ccc6b --- /dev/null +++ b/pkg/version/semverutils.go @@ -0,0 +1,27 @@ +// Copyright Istio Authors +// +// 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 version + +import "github.com/Masterminds/semver/v3" + +// VersionConstraint returns a semver constraint for the given string or panics +// if the string is not a valid semver constraint. +func Constraint(constraint string) semver.Constraints { + c, err := semver.NewConstraint(constraint) + if err == nil { + return *c + } + panic(err) +} diff --git a/pkg/version/semverutils_test.go b/pkg/version/semverutils_test.go new file mode 100644 index 000000000..846485e6a --- /dev/null +++ b/pkg/version/semverutils_test.go @@ -0,0 +1,34 @@ +// Copyright Istio Authors +// +// 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 version + +import ( + "testing" +) + +func TestConstraint(t *testing.T) { + t.Run("valid constraint", func(t *testing.T) { + _ = Constraint(">1.0.0") + }) + + t.Run("invalid constraint", func(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Error("Expected panic for invalid constraint") + } + }() + _ = Constraint("invalid_version") + }) +} diff --git a/resources/v1.23.2/charts/revisiontags/Chart.yaml b/resources/v1.23.2/charts/revisiontags/Chart.yaml new file mode 100644 index 000000000..382523174 --- /dev/null +++ b/resources/v1.23.2/charts/revisiontags/Chart.yaml @@ -0,0 +1,8 @@ +apiVersion: v2 +appVersion: v1.23.2 +description: Helm chart for istio revision tags +name: revisiontags +sources: +- https://github.com/istio-ecosystem/sail-operator +version: 0.1.0 + diff --git a/resources/v1.23.2/charts/revisiontags/templates/revision-tags.yaml b/resources/v1.23.2/charts/revisiontags/templates/revision-tags.yaml new file mode 100644 index 000000000..be80804bc --- /dev/null +++ b/resources/v1.23.2/charts/revisiontags/templates/revision-tags.yaml @@ -0,0 +1,141 @@ +# Adapted from istio-discovery/templates/mutatingwebhook.yaml +# Removed paths for legacy and default selectors since a revision tag +# is inherently created from a specific revision +{{- $whv := dict + "revision" .Values.revision + "injectionPath" .Values.istiodRemote.injectionPath + "injectionURL" .Values.istiodRemote.injectionURL + "reinvocationPolicy" .Values.sidecarInjectorWebhook.reinvocationPolicy + "namespace" .Release.Namespace }} +{{- define "core" }} +{{- /* Kubernetes unfortunately requires a unique name for the webhook in some newer versions, so we assign +a unique prefix to each. */}} +- name: {{.Prefix}}sidecar-injector.istio.io + clientConfig: + {{- if .injectionURL }} + url: "{{ .injectionURL }}" + {{- else }} + service: + name: istiod{{- if not (eq .revision "") }}-{{ .revision }}{{- end }} + namespace: {{ .namespace }} + path: "{{ .injectionPath }}" + port: 443 + {{- end }} + sideEffects: None + rules: + - operations: [ "CREATE" ] + apiGroups: [""] + apiVersions: ["v1"] + resources: ["pods"] + failurePolicy: Fail + admissionReviewVersions: ["v1"] +{{- end }} +{{- range $tagName := $.Values.revisionTags }} +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: +{{- if eq $.Release.Namespace "istio-system"}} + name: istio-revision-tag-{{ $tagName }} +{{- else }} + name: istio-revision-tag-{{ $tagName }}-{{ $.Release.Namespace }} +{{- end }} + labels: + istio.io/tag: {{ $tagName }} + istio.io/rev: {{ $.Values.revision | default "default" | quote }} + install.operator.istio.io/owning-resource: {{ $.Values.ownerName | default "unknown" }} + operator.istio.io/component: "Pilot" + app: sidecar-injector + release: {{ $.Release.Name }} +webhooks: +{{- include "core" (mergeOverwrite (deepCopy $whv) (dict "Prefix" "rev.namespace.") ) }} + namespaceSelector: + matchExpressions: + - key: istio.io/rev + operator: In + values: + - "{{ $tagName }}" + - key: istio-injection + operator: DoesNotExist + objectSelector: + matchExpressions: + - key: sidecar.istio.io/inject + operator: NotIn + values: + - "false" +{{- include "core" (mergeOverwrite (deepCopy $whv) (dict "Prefix" "rev.object.") ) }} + namespaceSelector: + matchExpressions: + - key: istio.io/rev + operator: DoesNotExist + - key: istio-injection + operator: DoesNotExist + objectSelector: + matchExpressions: + - key: sidecar.istio.io/inject + operator: NotIn + values: + - "false" + - key: istio.io/rev + operator: In + values: + - "{{ $tagName }}" + +{{- /* When the tag is "default" we want to create webhooks for the default revision */}} +{{- /* These webhooks should be kept in sync with istio-discovery/templates/mutatingwebhook.yaml */}} +{{- if (eq $tagName "default") }} + +{{- /* Case 1: Namespace selector enabled, and object selector is not injected */}} +{{- include "core" (mergeOverwrite (deepCopy $whv) (dict "Prefix" "namespace.") ) }} + namespaceSelector: + matchExpressions: + - key: istio-injection + operator: In + values: + - enabled + objectSelector: + matchExpressions: + - key: sidecar.istio.io/inject + operator: NotIn + values: + - "false" + +{{- /* Case 2: no namespace label, but object selector is enabled (and revision label is not, which has priority) */}} +{{- include "core" (mergeOverwrite (deepCopy $whv) (dict "Prefix" "object.") ) }} + namespaceSelector: + matchExpressions: + - key: istio-injection + operator: DoesNotExist + - key: istio.io/rev + operator: DoesNotExist + objectSelector: + matchExpressions: + - key: sidecar.istio.io/inject + operator: In + values: + - "true" + - key: istio.io/rev + operator: DoesNotExist + +{{- if $.Values.sidecarInjectorWebhook.enableNamespacesByDefault }} +{{- /* Special case 3: no labels at all */}} +{{- include "core" (mergeOverwrite (deepCopy $whv) (dict "Prefix" "auto.") ) }} + namespaceSelector: + matchExpressions: + - key: istio-injection + operator: DoesNotExist + - key: istio.io/rev + operator: DoesNotExist + - key: "kubernetes.io/metadata.name" + operator: "NotIn" + values: ["kube-system","kube-public","kube-node-lease","local-path-storage"] + objectSelector: + matchExpressions: + - key: sidecar.istio.io/inject + operator: DoesNotExist + - key: istio.io/rev + operator: DoesNotExist +{{- end }} + +{{- end }} +--- +{{- end }} diff --git a/resources/v1.23.2/charts/revisiontags/templates/zzz_profile.yaml b/resources/v1.23.2/charts/revisiontags/templates/zzz_profile.yaml new file mode 100644 index 000000000..2d0bd4af7 --- /dev/null +++ b/resources/v1.23.2/charts/revisiontags/templates/zzz_profile.yaml @@ -0,0 +1,43 @@ +{{/* +WARNING: DO NOT EDIT, THIS FILE IS A PROBABLY COPY. +The original version of this file is located at /manifests directory. +If you want to make a change in this file, edit the original one and run "make gen". + +Complex logic ahead... +We have three sets of values, in order of precedence (last wins): +1. The builtin values.yaml defaults +2. The profile the user selects +3. Users input (-f or --set) + +Unfortunately, Helm provides us (1) and (3) together (as .Values), making it hard to insert (2). + +However, we can workaround this by placing all of (1) under a specific key (.Values.defaults). +We can then merge the profile onto the defaults, then the user settings onto that. +Finally, we can set all of that under .Values so the chart behaves without awareness. +*/}} +{{- $globals := $.Values.global | default dict | deepCopy }} +{{- $defaults := $.Values.defaults }} +{{- $_ := unset $.Values "defaults" }} +{{- $profile := dict }} +{{- with .Values.profile }} +{{- with $.Files.Get (printf "files/profile-%s.yaml" .)}} +{{- $profile = (. | fromYaml) }} +{{- else }} +{{ fail (cat "unknown profile" $.Values.profile) }} +{{- end }} +{{- end }} +{{- with .Values.compatibilityVersion }} +{{- with $.Files.Get (printf "files/profile-compatibility-version-%s.yaml" .) }} +{{- $ignore := mustMergeOverwrite $profile (. | fromYaml) }} +{{- else }} +{{ fail (cat "unknown compatibility version" $.Values.compatibilityVersion) }} +{{- end }} +{{- end }} +{{- if $profile }} +{{- $a := mustMergeOverwrite $defaults $profile }} +{{- end }} +# Flatten globals, if defined on a per-chart basis +{{- if false }} +{{- $a := mustMergeOverwrite $defaults $globals }} +{{- end }} +{{- $b := set $ "Values" (mustMergeOverwrite $defaults $.Values) }} diff --git a/resources/v1.23.2/charts/revisiontags/values.yaml b/resources/v1.23.2/charts/revisiontags/values.yaml new file mode 100644 index 000000000..fbb4d2695 --- /dev/null +++ b/resources/v1.23.2/charts/revisiontags/values.yaml @@ -0,0 +1,525 @@ +# "defaults" is a workaround for Helm limitations. Users should NOT set ".defaults" explicitly, but rather directly set the fields internally. +# For instance, instead of `--set defaults.foo=bar`, just set `--set foo=bar`. +defaults: + #.Values.pilot for discovery and mesh wide config + + ## Discovery Settings + pilot: + autoscaleEnabled: true + autoscaleMin: 1 + autoscaleMax: 5 + autoscaleBehavior: {} + replicaCount: 1 + rollingMaxSurge: 100% + rollingMaxUnavailable: 25% + + hub: "" + tag: "" + variant: "" + + # Can be a full hub/image:tag + image: pilot + traceSampling: 1.0 + + # Resources for a small pilot install + resources: + requests: + cpu: 500m + memory: 2048Mi + + # Set to `type: RuntimeDefault` to use the default profile if available. + seccompProfile: {} + + # Whether to use an existing CNI installation + cni: + enabled: false + provider: default + + # Additional container arguments + extraContainerArgs: [] + + env: {} + + # Settings related to the untaint controller + # This controller will remove `cni.istio.io/not-ready` from nodes when the istio-cni pod becomes ready + # It should be noted that cluster operator/owner is responsible for having the taint set by their infrastructure provider when new nodes are added to the cluster; the untaint controller does not taint nodes + taint: + # Controls whether or not the untaint controller is active + enabled: false + # What namespace the untaint controller should watch for istio-cni pods. This is only required when istio-cni is running in a different namespace than istiod + namespace: "" + + affinity: {} + + tolerations: [] + + cpu: + targetAverageUtilization: 80 + memory: {} + # targetAverageUtilization: 80 + + # Additional volumeMounts to the istiod container + volumeMounts: [] + + # Additional volumes to the istiod pod + volumes: [] + + nodeSelector: {} + podAnnotations: {} + serviceAnnotations: {} + serviceAccountAnnotations: {} + + topologySpreadConstraints: [] + + # You can use jwksResolverExtraRootCA to provide a root certificate + # in PEM format. This will then be trusted by pilot when resolving + # JWKS URIs. + jwksResolverExtraRootCA: "" + + # The following is used to limit how long a sidecar can be connected + # to a pilot. It balances out load across pilot instances at the cost of + # increasing system churn. + keepaliveMaxServerConnectionAge: 30m + + # Additional labels to apply to the deployment. + deploymentLabels: {} + + ## Mesh config settings + + # Install the mesh config map, generated from values.yaml. + # If false, pilot wil use default values (by default) or user-supplied values. + configMap: true + + # Additional labels to apply on the pod level for monitoring and logging configuration. + podLabels: {} + + # Setup how istiod Service is configured. See https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services + ipFamilyPolicy: "" + ipFamilies: [] + + # Ambient mode only. + # Set this if you install ztunnel to a different namespace from `istiod`. + # If set, `istiod` will allow connections from trusted node proxy ztunnels + # in the provided namespace. + # If unset, `istiod` will assume the trusted node proxy ztunnel resides + # in the same namespace as itself. + trustedZtunnelNamespace: "" + + sidecarInjectorWebhook: + # You can use the field called alwaysInjectSelector and neverInjectSelector which will always inject the sidecar or + # always skip the injection on pods that match that label selector, regardless of the global policy. + # See https://istio.io/docs/setup/kubernetes/additional-setup/sidecar-injection/#more-control-adding-exceptions + neverInjectSelector: [] + alwaysInjectSelector: [] + + # injectedAnnotations are additional annotations that will be added to the pod spec after injection + # This is primarily to support PSP annotations. For example, if you defined a PSP with the annotations: + # + # annotations: + # apparmor.security.beta.kubernetes.io/allowedProfileNames: runtime/default + # apparmor.security.beta.kubernetes.io/defaultProfileName: runtime/default + # + # The PSP controller would add corresponding annotations to the pod spec for each container. However, this happens before + # the inject adds additional containers, so we must specify them explicitly here. With the above example, we could specify: + # injectedAnnotations: + # container.apparmor.security.beta.kubernetes.io/istio-init: runtime/default + # container.apparmor.security.beta.kubernetes.io/istio-proxy: runtime/default + injectedAnnotations: {} + + # This enables injection of sidecar in all namespaces, + # with the exception of namespaces with "istio-injection:disabled" annotation + # Only one environment should have this enabled. + enableNamespacesByDefault: false + + # Mutations that occur after the sidecar injector are not handled by default, as the Istio sidecar injector is only run + # once. For example, an OPA sidecar injected after the Istio sidecar will not have it's liveness/readiness probes rewritten. + # Setting this to `IfNeeded` will result in the sidecar injector being run again if additional mutations occur. + reinvocationPolicy: Never + + rewriteAppHTTPProbe: true + + # Templates defines a set of custom injection templates that can be used. For example, defining: + # + # templates: + # hello: | + # metadata: + # labels: + # hello: world + # + # Then starting a pod with the `inject.istio.io/templates: hello` annotation, will result in the pod + # being injected with the hello=world labels. + # This is intended for advanced configuration only; most users should use the built in template + templates: {} + + # Default templates specifies a set of default templates that are used in sidecar injection. + # By default, a template `sidecar` is always provided, which contains the template of default sidecar. + # To inject other additional templates, define it using the `templates` option, and add it to + # the default templates list. + # For example: + # + # templates: + # hello: | + # metadata: + # labels: + # hello: world + # + # defaultTemplates: ["sidecar", "hello"] + defaultTemplates: [] + istiodRemote: + # Sidecar injector mutating webhook configuration clientConfig.url value. + # For example: https://$remotePilotAddress:15017/inject + # The host should not refer to a service running in the cluster; use a service reference by specifying + # the clientConfig.service field instead. + injectionURL: "" + + # Sidecar injector mutating webhook configuration path value for the clientConfig.service field. + # Override to pass env variables, for example: /inject/cluster/remote/net/network2 + injectionPath: "/inject" + + injectionCABundle: "" + telemetry: + enabled: true + v2: + # For Null VM case now. + # This also enables metadata exchange. + enabled: true + # Indicate if prometheus stats filter is enabled or not + prometheus: + enabled: true + # stackdriver filter settings. + stackdriver: + enabled: false + # Revision is set as 'version' label and part of the resource names when installing multiple control planes. + revision: "" + + # Revision tags are aliases to Istio control plane revisions + revisionTags: [] + + # For Helm compatibility. + ownerName: "" + + # meshConfig defines runtime configuration of components, including Istiod and istio-agent behavior + # See https://istio.io/docs/reference/config/istio.mesh.v1alpha1/ for all available options + meshConfig: + enablePrometheusMerge: true + + experimental: + stableValidationPolicy: false + + global: + # Used to locate istiod. + istioNamespace: istio-system + # List of cert-signers to allow "approve" action in the istio cluster role + # + # certSigners: + # - clusterissuers.cert-manager.io/istio-ca + certSigners: [] + # enable pod disruption budget for the control plane, which is used to + # ensure Istio control plane components are gradually upgraded or recovered. + defaultPodDisruptionBudget: + enabled: true + # The values aren't mutable due to a current PodDisruptionBudget limitation + # minAvailable: 1 + + # A minimal set of requested resources to applied to all deployments so that + # Horizontal Pod Autoscaler will be able to function (if set). + # Each component can overwrite these default values by adding its own resources + # block in the relevant section below and setting the desired resources values. + defaultResources: + requests: + cpu: 10m + # memory: 128Mi + # limits: + # cpu: 100m + # memory: 128Mi + + # Default hub for Istio images. + # Releases are published to docker hub under 'istio' project. + # Dev builds from prow are on gcr.io + hub: docker.io/istio + # Default tag for Istio images. + tag: 1.23.2 + # Variant of the image to use. + # Currently supported are: [debug, distroless] + variant: "" + + # Specify image pull policy if default behavior isn't desired. + # Default behavior: latest images will be Always else IfNotPresent. + imagePullPolicy: "" + + # ImagePullSecrets for all ServiceAccount, list of secrets in the same namespace + # to use for pulling any images in pods that reference this ServiceAccount. + # For components that don't use ServiceAccounts (i.e. grafana, servicegraph, tracing) + # ImagePullSecrets will be added to the corresponding Deployment(StatefulSet) objects. + # Must be set for any cluster configured with private docker registry. + imagePullSecrets: [] + # - private-registry-key + + # Enabled by default in master for maximising testing. + istiod: + enableAnalysis: false + + # To output all istio components logs in json format by adding --log_as_json argument to each container argument + logAsJson: false + + # Comma-separated minimum per-scope logging level of messages to output, in the form of :,: + # The control plane has different scopes depending on component, but can configure default log level across all components + # If empty, default scope and level will be used as configured in code + logging: + level: "default:info" + + omitSidecarInjectorConfigMap: false + + # Configure whether Operator manages webhook configurations. The current behavior + # of Istiod is to manage its own webhook configurations. + # When this option is set as true, Istio Operator, instead of webhooks, manages the + # webhook configurations. When this option is set as false, webhooks manage their + # own webhook configurations. + operatorManageWebhooks: false + + # Custom DNS config for the pod to resolve names of services in other + # clusters. Use this to add additional search domains, and other settings. + # see + # https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#dns-config + # This does not apply to gateway pods as they typically need a different + # set of DNS settings than the normal application pods (e.g., in + # multicluster scenarios). + # NOTE: If using templates, follow the pattern in the commented example below. + #podDNSSearchNamespaces: + #- global + #- "{{ valueOrDefault .DeploymentMeta.Namespace \"default\" }}.global" + + # Kubernetes >=v1.11.0 will create two PriorityClass, including system-cluster-critical and + # system-node-critical, it is better to configure this in order to make sure your Istio pods + # will not be killed because of low priority class. + # Refer to https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#priorityclass + # for more detail. + priorityClassName: "" + + proxy: + image: proxyv2 + + # This controls the 'policy' in the sidecar injector. + autoInject: enabled + + # CAUTION: It is important to ensure that all Istio helm charts specify the same clusterDomain value + # cluster domain. Default value is "cluster.local". + clusterDomain: "cluster.local" + + # Per Component log level for proxy, applies to gateways and sidecars. If a component level is + # not set, then the global "logLevel" will be used. + componentLogLevel: "misc:error" + + # If set, newly injected sidecars will have core dumps enabled. + enableCoreDump: false + + # istio ingress capture allowlist + # examples: + # Redirect only selected ports: --includeInboundPorts="80,8080" + excludeInboundPorts: "" + includeInboundPorts: "*" + + # istio egress capture allowlist + # https://istio.io/docs/tasks/traffic-management/egress.html#calling-external-services-directly + # example: includeIPRanges: "172.30.0.0/16,172.20.0.0/16" + # would only capture egress traffic on those two IP Ranges, all other outbound traffic would + # be allowed by the sidecar + includeIPRanges: "*" + excludeIPRanges: "" + includeOutboundPorts: "" + excludeOutboundPorts: "" + + # Log level for proxy, applies to gateways and sidecars. + # Expected values are: trace|debug|info|warning|error|critical|off + logLevel: warning + + # Specify the path to the outlier event log. + # Example: /dev/stdout + outlierLogPath: "" + + #If set to true, istio-proxy container will have privileged securityContext + privileged: false + + # The number of successive failed probes before indicating readiness failure. + readinessFailureThreshold: 4 + + # The initial delay for readiness probes in seconds. + readinessInitialDelaySeconds: 0 + + # The period between readiness probes. + readinessPeriodSeconds: 15 + + # Enables or disables a startup probe. + # For optimal startup times, changing this should be tied to the readiness probe values. + # + # If the probe is enabled, it is recommended to have delay=0s,period=15s,failureThreshold=4. + # This ensures the pod is marked ready immediately after the startup probe passes (which has a 1s poll interval), + # and doesn't spam the readiness endpoint too much + # + # If the probe is disabled, it is recommended to have delay=1s,period=2s,failureThreshold=30. + # This ensures the startup is reasonable fast (polling every 2s). 1s delay is used since the startup is not often ready instantly. + startupProbe: + enabled: true + failureThreshold: 600 # 10 minutes + + # Resources for the sidecar. + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 2000m + memory: 1024Mi + + # Default port for Pilot agent health checks. A value of 0 will disable health checking. + statusPort: 15020 + + # Specify which tracer to use. One of: zipkin, lightstep, datadog, stackdriver, none. + # If using stackdriver tracer outside GCP, set env GOOGLE_APPLICATION_CREDENTIALS to the GCP credential file. + tracer: "none" + + proxy_init: + # Base name for the proxy_init container, used to configure iptables. + image: proxyv2 + + # configure remote pilot and istiod service and endpoint + remotePilotAddress: "" + + ############################################################################################## + # The following values are found in other charts. To effectively modify these values, make # + # make sure they are consistent across your Istio helm charts # + ############################################################################################## + + # The customized CA address to retrieve certificates for the pods in the cluster. + # CSR clients such as the Istio Agent and ingress gateways can use this to specify the CA endpoint. + # If not set explicitly, default to the Istio discovery address. + caAddress: "" + + # Configure a remote cluster data plane controlled by an external istiod. + # When set to true, istiod is not deployed locally and only a subset of the other + # discovery charts are enabled. + externalIstiod: false + + # Configure a remote cluster as the config cluster for an external istiod. + configCluster: false + + # configValidation enables the validation webhook for Istio configuration. + configValidation: true + + # Mesh ID means Mesh Identifier. It should be unique within the scope where + # meshes will interact with each other, but it is not required to be + # globally/universally unique. For example, if any of the following are true, + # then two meshes must have different Mesh IDs: + # - Meshes will have their telemetry aggregated in one place + # - Meshes will be federated together + # - Policy will be written referencing one mesh from the other + # + # If an administrator expects that any of these conditions may become true in + # the future, they should ensure their meshes have different Mesh IDs + # assigned. + # + # Within a multicluster mesh, each cluster must be (manually or auto) + # configured to have the same Mesh ID value. If an existing cluster 'joins' a + # multicluster mesh, it will need to be migrated to the new mesh ID. Details + # of migration TBD, and it may be a disruptive operation to change the Mesh + # ID post-install. + # + # If the mesh admin does not specify a value, Istio will use the value of the + # mesh's Trust Domain. The best practice is to select a proper Trust Domain + # value. + meshID: "" + + # Configure the mesh networks to be used by the Split Horizon EDS. + # + # The following example defines two networks with different endpoints association methods. + # For `network1` all endpoints that their IP belongs to the provided CIDR range will be + # mapped to network1. The gateway for this network example is specified by its public IP + # address and port. + # The second network, `network2`, in this example is defined differently with all endpoints + # retrieved through the specified Multi-Cluster registry being mapped to network2. The + # gateway is also defined differently with the name of the gateway service on the remote + # cluster. The public IP for the gateway will be determined from that remote service (only + # LoadBalancer gateway service type is currently supported, for a NodePort type gateway service, + # it still need to be configured manually). + # + # meshNetworks: + # network1: + # endpoints: + # - fromCidr: "192.168.0.1/24" + # gateways: + # - address: 1.1.1.1 + # port: 80 + # network2: + # endpoints: + # - fromRegistry: reg1 + # gateways: + # - registryServiceName: istio-ingressgateway.istio-system.svc.cluster.local + # port: 443 + # + meshNetworks: {} + + # Use the user-specified, secret volume mounted key and certs for Pilot and workloads. + mountMtlsCerts: false + + multiCluster: + # Set to true to connect two kubernetes clusters via their respective + # ingressgateway services when pods in each cluster cannot directly + # talk to one another. All clusters should be using Istio mTLS and must + # have a shared root CA for this model to work. + enabled: false + # Should be set to the name of the cluster this installation will run in. This is required for sidecar injection + # to properly label proxies + clusterName: "" + + # Network defines the network this cluster belong to. This name + # corresponds to the networks in the map of mesh networks. + network: "" + + # Configure the certificate provider for control plane communication. + # Currently, two providers are supported: "kubernetes" and "istiod". + # As some platforms may not have kubernetes signing APIs, + # Istiod is the default + pilotCertProvider: istiod + + sds: + # The JWT token for SDS and the aud field of such JWT. See RFC 7519, section 4.1.3. + # When a CSR is sent from Istio Agent to the CA (e.g. Istiod), this aud is to make sure the + # JWT is intended for the CA. + token: + aud: istio-ca + + sts: + # The service port used by Security Token Service (STS) server to handle token exchange requests. + # Setting this port to a non-zero value enables STS server. + servicePort: 0 + + # The name of the CA for workload certificates. + # For example, when caName=GkeWorkloadCertificate, GKE workload certificates + # will be used as the certificates for workloads. + # The default value is "" and when caName="", the CA will be configured by other + # mechanisms (e.g., environmental variable CA_PROVIDER). + caName: "" + + # whether to use autoscaling/v2 template for HPA settings + # for internal usage only, not to be configured by users. + autoscalingv2API: true + + base: + # For istioctl usage to disable istio config crds in base + enableIstioConfigCRDs: true + + # `istio_cni` has been deprecated and will be removed in a future release. use `pilot.cni` instead + istio_cni: + # `chained` has been deprecated and will be removed in a future release. use `provider` instead + chained: true + provider: default + + # Gateway Settings + gateways: + # Define the security context for the pod. + # If unset, this will be automatically set to the minimum privileges required to bind to port 80 and 443. + # On Kubernetes 1.22+, this only requires the `net.ipv4.ip_unprivileged_port_start` sysctl. + securityContext: {} + + # Set to `type: RuntimeDefault` to use the default profile for templated gateways, if your container runtime supports it + seccompProfile: {} diff --git a/resources/v1.23.2/profiles/remote.yaml b/resources/v1.23.2/profiles/remote.yaml new file mode 100644 index 000000000..d72fd66e4 --- /dev/null +++ b/resources/v1.23.2/profiles/remote.yaml @@ -0,0 +1,5 @@ +# The remote profile is used to configure a mesh cluster without a locally deployed control plane. +# Only the injector mutating webhook configuration is installed. +apiVersion: sailoperator.io/v1alpha1 +kind: Istio +spec: {} diff --git a/tests/e2e/README.md b/tests/e2e/README.md index 530e911df..432ea1d7f 100644 --- a/tests/e2e/README.md +++ b/tests/e2e/README.md @@ -15,7 +15,8 @@ This end-to-end test suite utilizes Ginkgo, a testing framework known for its ex 1. [Pre-requisites](#Pre-requisites) 1. [How to Run the test](#How-to-Run-the-test) 1. [Running the test locally](#Running-the-test-locally) - 1. [Settings for end-to-end test execution](#Settings-for-end-to-end-test-execution) + 1. [Settings for end-to-end test execution](#Settings-for-end-to-end-test-execution) + 1. [Customizing the test run](#Customizing-the-test-run) 1. [Get test definitions for the end-to-end test](#Get-test-definitions-for-the-end-to-end-test) 1. [Contributing](#contributing) @@ -215,6 +216,23 @@ The following environment variables define the behavior of the test run: * DEPLOYMENT_NAME=sail-operator - The name of the operator deployment. * EXPECTED_REGISTRY=`^docker\.io|^gcr\.io` - Which image registry should the operand images come from. Useful for downstream tests. +### Customizing the test run + +The test run can be customized by setting the following environment variables: + +To change all the sample files used in the test, you can use the following environment variable: +* SAMPLE_YAML_BASE_URL=https://raw.githubusercontent.com/orgName/repoName - The base URL where the sample YAML files are located. This is useful when you want to test the operator with different sample YAML files defined in another repository. Currently the e2e framework use the upstream Istio repository to get the sample YAML files, for example: `https://raw.githubusercontent.com/istio/istio/master/samples/helloworld/helloworld.yaml` for `hello-world` sample yaml file on `master` branch. + +Note: when setting this environment variable, make sure that the sample files are available in the same paths as in the Istio upstream repository. For more information check the [code](https://github.com/istio-ecosystem/sail-operator/blob/74f96ab4a56e3d41e2d31faede290116a68551e9/tests/e2e/util/common/e2e_utils.go#L271). +Alternatively, set the following environment variables to change these file paths: +* TCP_ECHO_DUAL_STACK_YAML_PATH +* TCP_ECHO_IPV4_YAML_PATH +* TCP_ECHO_IPV6_YAML_PATH +* SLEEP_YAML_PATH +* HELLOWORLD_YAML_PATH + +`TCP_ECHO_*` are used in the `dual-stack` test suite, `SLEEP_YAML_PATH` and `HELLOWORLD_YAML_PATH` are used in both `Multicluster` and default test run. + ### Get test definitions for the end-to-end test The end-to-end test suite is defined in the `tests/e2e/operator` directory. If you want to check the test definition without running the test, you can use the following make target: diff --git a/tests/e2e/controlplane/control_plane_suite_test.go b/tests/e2e/controlplane/control_plane_suite_test.go index aca69e1a7..d4c75bdd4 100644 --- a/tests/e2e/controlplane/control_plane_suite_test.go +++ b/tests/e2e/controlplane/control_plane_suite_test.go @@ -40,7 +40,7 @@ var ( istioCniName = env.Get("ISTIOCNI_NAME", "default") skipDeploy = env.GetBool("SKIP_DEPLOY", false) expectedRegistry = env.Get("EXPECTED_REGISTRY", "^docker\\.io|^gcr\\.io") - bookinfoNamespace = env.Get("BOOKINFO_NAMESPACE", "bookinfo") + sampleNamespace = env.Get("SAMPLE_NAMESPACE", "sample") multicluster = env.GetBool("MULTICLUSTER", false) ipFamily = env.Get("IP_FAMILY", "ipv4") diff --git a/tests/e2e/controlplane/control_plane_test.go b/tests/e2e/controlplane/control_plane_test.go index e55967f80..4946e38a7 100644 --- a/tests/e2e/controlplane/control_plane_test.go +++ b/tests/e2e/controlplane/control_plane_test.go @@ -117,9 +117,6 @@ metadata: Describe("given Istio version", func() { for _, version := range supportedversion.List { - // Note: This var version is needed to avoid the closure of the loop - version := version - Context(version.Name, func() { BeforeAll(func() { Expect(k.CreateNamespace(controlPlaneNamespace)).To(Succeed(), "Istio namespace failed to be created") @@ -224,31 +221,39 @@ spec: }) }) - When("bookinfo is deployed", func() { + When("sample pod is deployed", func() { BeforeAll(func() { - Expect(k.CreateNamespace(bookinfoNamespace)).To(Succeed(), "Bookinfo namespace failed to be created") - Expect(k.Patch("namespace", bookinfoNamespace, "merge", `{"metadata":{"labels":{"istio-injection":"enabled"}}}`)). - To(Succeed(), "Error patching bookinfo namespace") - Expect(deployBookinfo(version)).To(Succeed(), "Error deploying bookinfo") - Success("Bookinfo deployed") + Expect(k.CreateNamespace(sampleNamespace)).To(Succeed(), "Sample namespace failed to be created") + Expect(k.Patch("namespace", sampleNamespace, "merge", `{"metadata":{"labels":{"istio-injection":"enabled"}}}`)). + To(Succeed(), "Error patching sample namespace") + Expect(k.WithNamespace(sampleNamespace). + ApplyWithLabels(common.GetSampleYAML(version, sampleNamespace), "version=v1")). + To(Succeed(), "Error deploying sample") + Success("sample deployed") }) - bookinfoPods := &corev1.PodList{} + samplePods := &corev1.PodList{} It("updates the pods status to Running", func(ctx SpecContext) { - Expect(cl.List(ctx, bookinfoPods, client.InNamespace(bookinfoNamespace))).To(Succeed()) - Expect(bookinfoPods.Items).ToNot(BeEmpty(), "No pods found in bookinfo namespace") + Eventually(func() bool { + // Wait until the sample pod exists. Is wraped inside a function to avoid failure on the first iteration + Expect(cl.List(ctx, samplePods, client.InNamespace(sampleNamespace))).To(Succeed()) + return len(samplePods.Items) > 0 + }).Should(BeTrue(), "No sample pods found") + + Expect(cl.List(ctx, samplePods, client.InNamespace(sampleNamespace))).To(Succeed()) + Expect(samplePods.Items).ToNot(BeEmpty(), "No pods found in sample namespace") - for _, pod := range bookinfoPods.Items { - Eventually(common.GetObject).WithArguments(ctx, cl, kube.Key(pod.Name, bookinfoNamespace), &corev1.Pod{}). + for _, pod := range samplePods.Items { + Eventually(common.GetObject).WithArguments(ctx, cl, kube.Key(pod.Name, sampleNamespace), &corev1.Pod{}). Should(HaveCondition(corev1.PodReady, metav1.ConditionTrue), "Pod is not Ready") } - Success("Bookinfo pods are ready") + Success("sample pods are ready") }) It("has sidecars with the correct istio version", func(ctx SpecContext) { - for _, pod := range bookinfoPods.Items { - sidecarVersion, err := getProxyVersion(pod.Name, bookinfoNamespace) + for _, pod := range samplePods.Items { + sidecarVersion, err := getProxyVersion(pod.Name, sampleNamespace) Expect(err).NotTo(HaveOccurred(), "Error getting sidecar version") Expect(sidecarVersion).To(Equal(version.Version), "Sidecar Istio version does not match the expected version") } @@ -256,9 +261,9 @@ spec: }) AfterAll(func(ctx SpecContext) { - By("Deleting bookinfo") - Expect(k.DeleteNamespace(bookinfoNamespace)).To(Succeed(), "Bookinfo namespace failed to be deleted") - Success("Bookinfo deleted") + By("Deleting sample") + Expect(k.DeleteNamespace(sampleNamespace)).To(Succeed(), "sample namespace failed to be deleted") + Success("sample deleted") }) }) @@ -371,27 +376,6 @@ func forceDeleteIstioResources() error { return nil } -func getBookinfoURL(version supportedversion.VersionInfo) string { - // Bookinfo YAML for the current version can be found from istio/istio repository - // If the version is latest, we need to get the latest version from the master branch - bookinfoURL := fmt.Sprintf("https://raw.githubusercontent.com/istio/istio/%s/samples/bookinfo/platform/kube/bookinfo.yaml", version.Version) - if version.Name == "latest" { - bookinfoURL = "https://raw.githubusercontent.com/istio/istio/master/samples/bookinfo/platform/kube/bookinfo.yaml" - } - - return bookinfoURL -} - -func deployBookinfo(version supportedversion.VersionInfo) error { - bookinfoURL := getBookinfoURL(version) - err := k.WithNamespace(bookinfoNamespace).Apply(bookinfoURL) - if err != nil { - return fmt.Errorf("error deploying bookinfo: %w", err) - } - - return nil -} - func getProxyVersion(podName, namespace string) (*semver.Version, error) { output, err := k.WithNamespace(namespace).Exec( podName, diff --git a/tests/e2e/dualstack/dualstack_test.go b/tests/e2e/dualstack/dualstack_test.go index 9a7768438..a754a3268 100644 --- a/tests/e2e/dualstack/dualstack_test.go +++ b/tests/e2e/dualstack/dualstack_test.go @@ -70,9 +70,6 @@ var _ = Describe("DualStack configuration ", Ordered, func() { Describe("for supported versions", func() { for _, version := range supportedversion.List { - // Note: This var version is needed to avoid the closure of the loop - version := version - // The minimum supported version is 1.23 (and above) if version.Version.LessThan(semver.MustParse("1.23.0")) { continue @@ -202,10 +199,10 @@ spec: Expect(k.Patch("namespace", SleepNamespace, "merge", `{"metadata":{"labels":{"istio-injection":"enabled"}}}`)). To(Succeed(), "Error patching sleep namespace") - Expect(k.WithNamespace(DualStackNamespace).Apply(getYAMLPodURL(version, DualStackNamespace))).To(Succeed(), "error deploying tcpDualStack pod") - Expect(k.WithNamespace(IPv4Namespace).Apply(getYAMLPodURL(version, IPv4Namespace))).To(Succeed(), "error deploying ipv4 pod") - Expect(k.WithNamespace(IPv6Namespace).Apply(getYAMLPodURL(version, IPv6Namespace))).To(Succeed(), "error deploying ipv6 pod") - Expect(k.WithNamespace(SleepNamespace).Apply(getYAMLPodURL(version, SleepNamespace))).To(Succeed(), "error deploying sleep pod") + Expect(k.WithNamespace(DualStackNamespace).Apply(common.GetSampleYAML(version, "tcp-echo-dual-stack"))).To(Succeed(), "error deploying tcpDualStack pod") + Expect(k.WithNamespace(IPv4Namespace).Apply(common.GetSampleYAML(version, "tcp-echo-ipv4"))).To(Succeed(), "error deploying ipv4 pod") + Expect(k.WithNamespace(IPv6Namespace).Apply(common.GetSampleYAML(version, "tcp-echo-ipv6"))).To(Succeed(), "error deploying ipv6 pod") + Expect(k.WithNamespace(SleepNamespace).Apply(common.GetSampleYAML(version, "sleep"))).To(Succeed(), "error deploying sleep pod") Success("dualStack validation pods deployed") }) @@ -326,29 +323,6 @@ func getEnvVars(container corev1.Container) []corev1.EnvVar { return container.Env } -func getYAMLPodURL(version supportedversion.VersionInfo, namespace string) string { - var url string - - switch namespace { - case DualStackNamespace: - url = "samples/tcp-echo/tcp-echo-dual-stack.yaml" - case IPv4Namespace: - url = "samples/tcp-echo/tcp-echo-ipv4.yaml" - case IPv6Namespace: - url = "samples/tcp-echo/tcp-echo-ipv6.yaml" - case SleepNamespace: - url = "samples/sleep/sleep.yaml" - default: - return "" - } - - if version.Name == "latest" { - return fmt.Sprintf("https://raw.githubusercontent.com/istio/istio/master/%s", url) - } - - return fmt.Sprintf("https://raw.githubusercontent.com/istio/istio/%s/%s", version.Version, url) -} - func checkPodConnectivity(podName, namespace, echoStr string) { command := fmt.Sprintf(`sh -c 'echo %s | nc tcp-echo.%s 9000'`, echoStr, echoStr) response, err := k.WithNamespace(namespace).Exec(podName, "sleep", command) diff --git a/tests/e2e/multicluster/multicluster_multiprimary_test.go b/tests/e2e/multicluster/multicluster_multiprimary_test.go index 8d647504a..bc34186a3 100644 --- a/tests/e2e/multicluster/multicluster_multiprimary_test.go +++ b/tests/e2e/multicluster/multicluster_multiprimary_test.go @@ -198,8 +198,29 @@ spec: When("sample apps are deployed in both clusters", func() { BeforeAll(func(ctx SpecContext) { + // Create the namespace + Expect(k1.CreateNamespace("sample")).To(Succeed(), "Namespace failed to be created") + Expect(k2.CreateNamespace("sample")).To(Succeed(), "Namespace failed to be created") + + // Label the sample namespace + Expect(k1.Patch("namespace", "sample", "merge", `{"metadata":{"labels":{"istio-injection":"enabled"}}}`)). + To(Succeed(), "Error patching sample namespace") + Expect(k2.Patch("namespace", "sample", "merge", `{"metadata":{"labels":{"istio-injection":"enabled"}}}`)). + To(Succeed(), "Error patching sample namespace") + // Deploy the sample app in both clusters - deploySampleApp("sample", version) + helloWorldURL := common.GetSampleYAML(version, "helloworld") + sleepURL := common.GetSampleYAML(version, "sleep") + + // On Cluster 0, create a service for the helloworld app v1 + Expect(k1.WithNamespace("sample").ApplyWithLabels(helloWorldURL, "service=helloworld")).To(Succeed(), "Failed to deploy helloworld service") + Expect(k1.WithNamespace("sample").ApplyWithLabels(helloWorldURL, "version=v1")).To(Succeed(), "Failed to deploy helloworld service") + Expect(k1.WithNamespace("sample").Apply(sleepURL)).To(Succeed(), "Failed to deploy sleep service") + + // On Cluster 1, create a service for the helloworld app v2 + Expect(k2.WithNamespace("sample").ApplyWithLabels(helloWorldURL, "service=helloworld")).To(Succeed(), "Failed to deploy helloworld service") + Expect(k2.WithNamespace("sample").ApplyWithLabels(helloWorldURL, "version=v2")).To(Succeed(), "Failed to deploy helloworld service") + Expect(k2.WithNamespace("sample").Apply(sleepURL)).To(Succeed(), "Failed to deploy sleep service") Success("Sample app is deployed in both clusters") }) @@ -207,7 +228,7 @@ spec: samplePodsCluster1 := &corev1.PodList{} Expect(clPrimary.List(ctx, samplePodsCluster1, client.InNamespace("sample"))).To(Succeed()) - Expect(samplePodsCluster1.Items).ToNot(BeEmpty(), "No pods found in bookinfo namespace") + Expect(samplePodsCluster1.Items).ToNot(BeEmpty(), "No pods found in sample namespace") for _, pod := range samplePodsCluster1.Items { Eventually(common.GetObject). @@ -217,7 +238,7 @@ spec: samplePodsCluster2 := &corev1.PodList{} Expect(clRemote.List(ctx, samplePodsCluster2, client.InNamespace("sample"))).To(Succeed()) - Expect(samplePodsCluster2.Items).ToNot(BeEmpty(), "No pods found in bookinfo namespace") + Expect(samplePodsCluster2.Items).ToNot(BeEmpty(), "No pods found in sample namespace") for _, pod := range samplePodsCluster2.Items { Eventually(common.GetObject). @@ -290,32 +311,3 @@ spec: Expect(k2.WaitNamespaceDeleted(namespace)).To(Succeed()) }) }) - -// deploySampleApp deploys the sample app in the given cluster -func deploySampleApp(ns string, istioVersion supportedversion.VersionInfo) { - // Create the namespace - Expect(k1.CreateNamespace(ns)).To(Succeed(), "Namespace failed to be created") - Expect(k2.CreateNamespace(ns)).To(Succeed(), "Namespace failed to be created") - - // Label the namespace - Expect(k1.Patch("namespace", ns, "merge", `{"metadata":{"labels":{"istio-injection":"enabled"}}}`)). - To(Succeed(), "Error patching sample namespace") - Expect(k2.Patch("namespace", ns, "merge", `{"metadata":{"labels":{"istio-injection":"enabled"}}}`)). - To(Succeed(), "Error patching sample namespace") - - version := istioVersion.Version.String() - // Deploy the sample app from upstream URL in both clusters - if istioVersion.Name == "latest" { - version = "master" - } - helloWorldURL := fmt.Sprintf("https://raw.githubusercontent.com/istio/istio/%s/samples/helloworld/helloworld.yaml", version) - Expect(k1.WithNamespace(ns).ApplyWithLabels(helloWorldURL, "service=helloworld")).To(Succeed(), "Sample service deploy failed on Cluster #1") - Expect(k2.WithNamespace(ns).ApplyWithLabels(helloWorldURL, "service=helloworld")).To(Succeed(), "Sample service deploy failed on Cluster #2") - - Expect(k1.WithNamespace(ns).ApplyWithLabels(helloWorldURL, "version=v1")).To(Succeed(), "Sample service deploy failed on Cluster #1") - Expect(k2.WithNamespace(ns).ApplyWithLabels(helloWorldURL, "version=v2")).To(Succeed(), "Sample service deploy failed on Cluster #2") - - sleepURL := fmt.Sprintf("https://raw.githubusercontent.com/istio/istio/%s/samples/sleep/sleep.yaml", version) - Expect(k1.WithNamespace(ns).Apply(sleepURL)).To(Succeed(), "Sample sleep deploy failed on Cluster #1") - Expect(k2.WithNamespace(ns).Apply(sleepURL)).To(Succeed(), "Sample sleep deploy failed on Cluster #2") -} diff --git a/tests/e2e/multicluster/multicluster_primaryremote_test.go b/tests/e2e/multicluster/multicluster_primaryremote_test.go index 7ff6db158..55d983041 100644 --- a/tests/e2e/multicluster/multicluster_primaryremote_test.go +++ b/tests/e2e/multicluster/multicluster_primaryremote_test.go @@ -25,6 +25,7 @@ import ( "github.com/istio-ecosystem/sail-operator/pkg/kube" . "github.com/istio-ecosystem/sail-operator/pkg/test/util/ginkgo" "github.com/istio-ecosystem/sail-operator/pkg/test/util/supportedversion" + "github.com/istio-ecosystem/sail-operator/pkg/version" "github.com/istio-ecosystem/sail-operator/tests/e2e/util/certs" "github.com/istio-ecosystem/sail-operator/tests/e2e/util/common" . "github.com/istio-ecosystem/sail-operator/tests/e2e/util/gomega" @@ -68,14 +69,14 @@ var _ = Describe("Multicluster deployment models", Ordered, func() { Describe("Primary-Remote - Multi-Network configuration", func() { // Test the Primary-Remote - Multi-Network configuration for each supported Istio version - for _, version := range supportedversion.List { - // The Primary-Remote - Multi-Network configuration is only supported in Istio 1.23, because that's the only - // version that has the istiod-remote chart. For 1.24, we need to rewrite the support for RemoteIstio. - if !(version.Version.Major() == 1 && version.Version.Minor() == 23) { + for _, v := range supportedversion.List { + // The Primary-Remote - Multi-Network configuration is only supported in Istio 1.24+. + if version.Constraint("<1.24").Check(v.Version) { + Log(fmt.Sprintf("Skipping test, because Istio version %s does not support Primary-Remote Multi-Network configuration", v.Version)) continue } - Context(fmt.Sprintf("Istio version %s", version.Version), func() { + Context(fmt.Sprintf("Istio version %s", v.Version), func() { When("Istio resources are created in both clusters", func() { BeforeAll(func(ctx SpecContext) { Expect(k1.CreateNamespace(controlPlaneNamespace)).To(Succeed(), "Namespace failed to be created") @@ -115,7 +116,7 @@ spec: multiCluster: clusterName: %s network: %s` - multiclusterPrimaryYAML := fmt.Sprintf(PrimaryYAML, version.Name, controlPlaneNamespace, "mesh1", "cluster1", "network1") + multiclusterPrimaryYAML := fmt.Sprintf(PrimaryYAML, v.Name, controlPlaneNamespace, "mesh1", "cluster1", "network1") Log("Istio CR Primary: ", multiclusterPrimaryYAML) Expect(k1.CreateFromString(multiclusterPrimaryYAML)).To(Succeed(), "Istio Resource creation failed on Primary Cluster") }) @@ -131,7 +132,7 @@ spec: Eventually(common.GetObject). WithArguments(ctx, clPrimary, kube.Key("istiod", controlPlaneNamespace), &appsv1.Deployment{}). Should(HaveCondition(appsv1.DeploymentAvailable, metav1.ConditionTrue), "Istiod is not Available on Primary; unexpected Condition") - Expect(common.GetVersionFromIstiod()).To(Equal(version.Version), "Unexpected istiod version") + Expect(common.GetVersionFromIstiod()).To(Equal(v.Version), "Unexpected istiod version") Success("Istiod is deployed in the namespace and Running on Primary Cluster") }) }) @@ -154,17 +155,18 @@ spec: }) }) - When("RemoteIstio is created in Remote cluster", func() { + When("Istio is created in Remote cluster", func() { BeforeAll(func(ctx SpecContext) { - RemoteYAML := ` + istioYAMLTemplate := ` apiVersion: sailoperator.io/v1alpha1 -kind: RemoteIstio +kind: Istio metadata: name: default spec: version: %s namespace: istio-system values: + profile: remote istiodRemote: injectionPath: /inject/cluster/remote/net/network2 global: @@ -173,10 +175,10 @@ spec: remotePilotAddress, err := common.GetSVCLoadBalancerAddress(ctx, clPrimary, controlPlaneNamespace, "istio-eastwestgateway") Expect(remotePilotAddress).NotTo(BeEmpty(), "Remote Pilot Address is empty") Expect(err).NotTo(HaveOccurred(), "Error getting Remote Pilot Address") - remoteIstioYAML := fmt.Sprintf(RemoteYAML, version.Name, remotePilotAddress) - Log("RemoteIstio CR: ", remoteIstioYAML) - By("Creating RemoteIstio CR on Remote Cluster") - Expect(k2.CreateFromString(remoteIstioYAML)).To(Succeed(), "RemoteIstio Resource creation failed on Remote Cluster") + istioYAML := fmt.Sprintf(istioYAMLTemplate, v.Name, remotePilotAddress) + Log("Istio CR: ", istioYAML) + By("Creating Istio CR on Remote Cluster") + Expect(k2.CreateFromString(istioYAML)).To(Succeed(), "Istio Resource creation failed on Remote Cluster") // Set the controlplane cluster and network for Remote namespace By("Patching the istio-system namespace on Remote Cluster") @@ -196,13 +198,13 @@ spec: To(Succeed(), "Error patching istio-system namespace") // To be able to access the remote cluster from the primary cluster, we need to create a secret in the primary cluster - // RemoteIstio resource will not be Ready until the secret is created + // Remote Istio resource will not be Ready until the secret is created // Get the internal IP of the control plane node in Remote cluster internalIPRemote, err := k2.GetInternalIP("node-role.kubernetes.io/control-plane") Expect(internalIPRemote).NotTo(BeEmpty(), "Internal IP is empty for Remote Cluster") Expect(err).NotTo(HaveOccurred()) - // Wait for the RemoteIstio CR to be created, this can be moved to a condition verification, but the resource it not will be Ready at this point + // Wait for the remote Istio CR to be created, this can be moved to a condition verification, but the resource it not will be Ready at this point time.Sleep(5 * time.Second) // Install a remote secret in Primary cluster that provides access to the Remote cluster API server. @@ -219,11 +221,11 @@ spec: Success("Remote secret is created in Primary cluster") }) - It("updates RemoteIstio CR status to Ready", func(ctx SpecContext) { + It("updates remote Istio CR status to Ready", func(ctx SpecContext) { Eventually(common.GetObject). - WithArguments(ctx, clRemote, kube.Key(istioName), &v1alpha1.RemoteIstio{}). + WithArguments(ctx, clRemote, kube.Key(istioName), &v1alpha1.Istio{}). Should(HaveCondition(v1alpha1.IstioConditionReady, metav1.ConditionTrue), "Istio is not Ready on Remote; unexpected Condition") - Success("RemoteIstio CR is Ready on Remote Cluster") + Success("Istio CR is Ready on Remote Cluster") }) }) @@ -243,8 +245,29 @@ spec: When("sample apps are deployed in both clusters", func() { BeforeAll(func(ctx SpecContext) { + // Create the namespace + Expect(k1.CreateNamespace("sample")).To(Succeed(), "Namespace failed to be created") + Expect(k2.CreateNamespace("sample")).To(Succeed(), "Namespace failed to be created") + + // Label the sample namespace + Expect(k1.Patch("namespace", "sample", "merge", `{"metadata":{"labels":{"istio-injection":"enabled"}}}`)). + To(Succeed(), "Error patching sample namespace") + Expect(k2.Patch("namespace", "sample", "merge", `{"metadata":{"labels":{"istio-injection":"enabled"}}}`)). + To(Succeed(), "Error patching sample namespace") + // Deploy the sample app in both clusters - deploySampleApp("sample", version) + helloWorldURL := common.GetSampleYAML(v, "helloworld") + sleepURL := common.GetSampleYAML(v, "sleep") + + // On Cluster 0, create a service for the helloworld app v1 + Expect(k1.WithNamespace("sample").ApplyWithLabels(helloWorldURL, "service=helloworld")).To(Succeed(), "Failed to deploy helloworld service") + Expect(k1.WithNamespace("sample").ApplyWithLabels(helloWorldURL, "version=v1")).To(Succeed(), "Failed to deploy helloworld service") + Expect(k1.WithNamespace("sample").Apply(sleepURL)).To(Succeed(), "Failed to deploy sleep service") + + // On Cluster 1, create a service for the helloworld app v2 + Expect(k2.WithNamespace("sample").ApplyWithLabels(helloWorldURL, "service=helloworld")).To(Succeed(), "Failed to deploy helloworld service") + Expect(k2.WithNamespace("sample").ApplyWithLabels(helloWorldURL, "version=v2")).To(Succeed(), "Failed to deploy helloworld service") + Expect(k2.WithNamespace("sample").Apply(sleepURL)).To(Succeed(), "Failed to deploy sleep service") Success("Sample app is deployed in both clusters") }) @@ -252,7 +275,7 @@ spec: samplePodsPrimary := &corev1.PodList{} Expect(clPrimary.List(ctx, samplePodsPrimary, client.InNamespace("sample"))).To(Succeed()) - Expect(samplePodsPrimary.Items).ToNot(BeEmpty(), "No pods found in bookinfo namespace") + Expect(samplePodsPrimary.Items).ToNot(BeEmpty(), "No pods found in sample namespace") for _, pod := range samplePodsPrimary.Items { Eventually(common.GetObject). @@ -262,7 +285,7 @@ spec: samplePodsRemote := &corev1.PodList{} Expect(clRemote.List(ctx, samplePodsRemote, client.InNamespace("sample"))).To(Succeed()) - Expect(samplePodsRemote.Items).ToNot(BeEmpty(), "No pods found in bookinfo namespace") + Expect(samplePodsRemote.Items).ToNot(BeEmpty(), "No pods found in sample namespace") for _, pod := range samplePodsRemote.Items { Eventually(common.GetObject). @@ -279,11 +302,11 @@ spec: }) }) - When("Istio CR and RemoteIstio CR are deleted in both clusters", func() { + When("Istio CR is deleted in both clusters", func() { BeforeEach(func() { - Expect(k1.WithNamespace(controlPlaneNamespace).Delete("istio", istioName)).To(Succeed(), "Istio CR failed to be deleted") - Expect(k2.WithNamespace(controlPlaneNamespace).Delete("remoteistio", istioName)).To(Succeed(), "RemoteIstio CR failed to be deleted") - Success("Istio and RemoteIstio are deleted") + Expect(k1.WithNamespace(controlPlaneNamespace).Delete("istio", istioName)).To(Succeed(), "primary Istio CR failed to be deleted") + Expect(k2.WithNamespace(controlPlaneNamespace).Delete("istio", istioName)).To(Succeed(), "remote Istio CR failed to be deleted") + Success("Primary and Remote Istio resources are deleted") }) It("removes istiod on Primary", func(ctx SpecContext) { @@ -313,6 +336,12 @@ spec: Expect(k1.WaitNamespaceDeleted("sample")).To(Succeed()) Expect(k2.WaitNamespaceDeleted("sample")).To(Succeed()) Success("Sample app is deleted in both clusters") + + // Delete the resources created by istioctl create-remote-secret + Expect(k2.Delete("ClusterRoleBinding", "istiod-clusterrole-istio-system")).To(Succeed()) + Expect(k2.Delete("ClusterRole", "istiod-clusterrole-istio-system")).To(Succeed()) + Expect(k2.Delete("ClusterRoleBinding", "istiod-gateway-controller-istio-system")).To(Succeed()) + Expect(k2.Delete("ClusterRole", "istiod-gateway-controller-istio-system")).To(Succeed()) }) }) } diff --git a/tests/e2e/util/common/e2e_utils.go b/tests/e2e/util/common/e2e_utils.go index eb1423cc8..0d93c9404 100644 --- a/tests/e2e/util/common/e2e_utils.go +++ b/tests/e2e/util/common/e2e_utils.go @@ -19,6 +19,7 @@ package common import ( "context" "fmt" + "os" "path/filepath" "regexp" "strings" @@ -27,6 +28,7 @@ import ( "github.com/Masterminds/semver/v3" "github.com/istio-ecosystem/sail-operator/pkg/kube" "github.com/istio-ecosystem/sail-operator/pkg/test/project" + "github.com/istio-ecosystem/sail-operator/pkg/test/util/supportedversion" "github.com/istio-ecosystem/sail-operator/tests/e2e/util/env" . "github.com/istio-ecosystem/sail-operator/tests/e2e/util/gomega" "github.com/istio-ecosystem/sail-operator/tests/e2e/util/helm" @@ -261,3 +263,60 @@ func InstallOperatorViaHelm(extraArgs ...string) error { func UninstallOperator() error { return helm.Uninstall("sail-operator", "--namespace", OperatorNamespace) } + +// GetSampleYAML returns the URL of the yaml file for the testing app. +// args: +// version: the version of the Istio to get the yaml file from. +// appName: the name of the testing app. Example: helloworld, sleep, tcp-echo. +func GetSampleYAML(version supportedversion.VersionInfo, appName string) string { + // This func will be used to get URLs for the yaml files of the testing apps. Example: helloworld, sleep, tcp-echo. + // Default values points to upstream Istio sample yaml files. Custom paths can be provided using environment variables. + + // Define environment variables for specific apps + envVarMap := map[string]string{ + "tcp-echo-dual-stack": "TCP_ECHO_DUAL_STACK_YAML_PATH", + "tcp-echo-ipv4": "TCP_ECHO_IPV4_YAML_PATH", + "tcp-echo": "TCP_ECHO_IPV4_YAML_PATH", + "tcp-echo-ipv6": "TCP_ECHO_IPV6_YAML_PATH", + "sleep": "SLEEP_YAML_PATH", + "helloworld": "HELLOWORLD_YAML_PATH", + "sample": "HELLOWORLD_YAML_PATH", + } + + // Check if there's a custom path for the given appName + if envVar, exists := envVarMap[appName]; exists { + customPath := os.Getenv(envVar) + if customPath != "" { + return customPath + } + } + + // Default paths if no custom path is provided + var url string + switch appName { + case "tcp-echo-dual-stack": + url = "samples/tcp-echo/tcp-echo-dual-stack.yaml" + case "tcp-echo-ipv4", "tcp-echo": + url = "samples/tcp-echo/tcp-echo-ipv4.yaml" + case "tcp-echo-ipv6": + url = "samples/tcp-echo/tcp-echo-ipv6.yaml" + case "sleep": + url = "samples/sleep/sleep.yaml" + case "helloworld", "sample": + url = "samples/helloworld/helloworld.yaml" + default: + return "" + } + + // Base URL logic + baseURL := os.Getenv("SAMPLE_YAML_BASE_URL") + if baseURL == "" { + baseURL = "https://raw.githubusercontent.com/istio/istio" + } + + if version.Name == "latest" { + return fmt.Sprintf("%s/master/%s", baseURL, url) + } + + return fmt.Sprintf("%s/%s/%s", baseURL, version.Version, url) +} diff --git a/tests/integration/api/istio_test.go b/tests/integration/api/istio_test.go index d939f9cd3..44e634eb4 100644 --- a/tests/integration/api/istio_test.go +++ b/tests/integration/api/istio_test.go @@ -28,6 +28,7 @@ import ( . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" "istio.io/istio/pkg/ptr" @@ -137,7 +138,6 @@ var _ = Describe("Istio resource", Ordered, func() { }).Should(Succeed()) Expect(rev.Spec).To(Equal(v1alpha1.IstioRevisionSpec{ - Type: v1alpha1.IstioRevisionTypeLocal, Version: istio.Spec.Version, Namespace: istio.Spec.Namespace, Values: &v1alpha1.Values{ @@ -174,7 +174,6 @@ var _ = Describe("Istio resource", Ordered, func() { Eventually(k8sClient.Get).WithArguments(ctx, revKey, rev).Should(Succeed()) Expect(rev.GetOwnerReferences()).To(ContainElement(NewOwnerReference(istio))) Expect(rev.Spec).To(Equal(v1alpha1.IstioRevisionSpec{ - Type: v1alpha1.IstioRevisionTypeLocal, Version: istio.Spec.Version, Namespace: istio.Spec.Namespace, Values: &v1alpha1.Values{ @@ -224,8 +223,6 @@ var _ = Describe("Istio resource", Ordered, func() { }) for _, withWorkloads := range []bool{true, false} { - withWorkloads := withWorkloads - Context(generateContextName(withWorkloads), func() { if withWorkloads { BeforeAll(func() { @@ -426,22 +423,22 @@ var _ = Describe("Istio resource", Ordered, func() { }) When("strategy is changed to InPlace", func() { + var oldRevisionKey types.NamespacedName BeforeAll(func() { + oldRevisionKey = getRevisionKey(istio, supportedversion.New) Expect(k8sClient.Get(ctx, istioKey, istio)).To(Succeed()) istio.Spec.UpdateStrategy.Type = v1alpha1.UpdateStrategyTypeInPlace Expect(k8sClient.Update(ctx, istio)).To(Succeed()) }) It("creates an IstioRevision with no version in the name", func() { - revKey := client.ObjectKey{Name: istio.Name} - Eventually(k8sClient.Get).WithArguments(ctx, revKey, rev).Should(Succeed()) + Eventually(k8sClient.Get).WithArguments(ctx, oldRevisionKey, rev).Should(Succeed()) Expect(rev.Spec.Version).To(Equal(istio.Spec.Version)) }) if withWorkloads { It("doesn't delete the previous IstioRevision while workloads reference it", func() { - revKey := getRevisionKey(istio, supportedversion.New) - Consistently(k8sClient.Get).WithArguments(ctx, revKey, rev).Should(Succeed()) + Consistently(k8sClient.Get).WithArguments(ctx, oldRevisionKey, rev).Should(Succeed()) }) When("workloads are moved to the IstioRevision with no version in the name", func() { @@ -452,23 +449,20 @@ var _ = Describe("Istio resource", Ordered, func() { It("doesn't immediately delete the previous IstioRevision", func() { marginOfError := 2 * time.Second - revKey := getRevisionKey(istio, supportedversion.New) - Consistently(k8sClient.Get, gracePeriod-marginOfError).WithArguments(ctx, revKey, rev).Should(Succeed()) + Consistently(k8sClient.Get, gracePeriod-marginOfError).WithArguments(ctx, oldRevisionKey, rev).Should(Succeed()) }) When("grace period expires", func() { It("deletes the previous IstioRevision", func() { - revKey := getRevisionKey(istio, supportedversion.New) - Eventually(k8sClient.Get).WithArguments(ctx, revKey, rev).Should(ReturnNotFoundError()) + Eventually(k8sClient.Get).WithArguments(ctx, oldRevisionKey, rev).Should(ReturnNotFoundError()) }) }) }) } else { When("grace period expires", func() { It("deletes the previous IstioRevision", func() { - revKey := getRevisionKey(istio, supportedversion.New) marginOfError := 30 * time.Second - Eventually(k8sClient.Get, gracePeriod+marginOfError).WithArguments(ctx, revKey, rev).Should(ReturnNotFoundError()) + Eventually(k8sClient.Get, gracePeriod+marginOfError).WithArguments(ctx, oldRevisionKey, rev).Should(ReturnNotFoundError()) }) }) } @@ -511,5 +505,8 @@ func getRevisionName(istio *v1alpha1.Istio, version string) string { if istio.Name == "" { panic("istio.Name is empty") } + if istio.Spec.UpdateStrategy.Type == v1alpha1.UpdateStrategyTypeInPlace { + return istio.Name + } return istio.Name + "-" + strings.ReplaceAll(version, ".", "-") } diff --git a/tests/integration/api/istiocni_test.go b/tests/integration/api/istiocni_test.go index f43756ae3..c207d4b11 100644 --- a/tests/integration/api/istiocni_test.go +++ b/tests/integration/api/istiocni_test.go @@ -22,6 +22,7 @@ import ( "time" "github.com/istio-ecosystem/sail-operator/api/v1alpha1" + "github.com/istio-ecosystem/sail-operator/pkg/enqueuelogger" "github.com/istio-ecosystem/sail-operator/pkg/kube" . "github.com/istio-ecosystem/sail-operator/pkg/test/util/ginkgo" "github.com/istio-ecosystem/sail-operator/pkg/test/util/supportedversion" @@ -43,6 +44,8 @@ var _ = Describe("IstioCNI", Ordered, func() { SetDefaultEventuallyPollingInterval(time.Second) SetDefaultEventuallyTimeout(30 * time.Second) + enqueuelogger.LogEnqueueEvents = true + ctx := context.Background() namespace := &corev1.Namespace{ @@ -236,6 +239,32 @@ var _ = Describe("IstioCNI", Ordered, func() { }).Should(Succeed()) }) }) + + It("skips reconcile when a pull secret is added to service account", func() { + waitForInFlightReconcileToFinish() + + sa := &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: "istio-cni", + Namespace: cniNamespace, + }, + } + Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(sa), sa)).To(Succeed()) + + beforeCount := getIstioCNIReconcileCount(Default) + + By("adding pull secret to ServiceAccount") + sa.ImagePullSecrets = append(sa.ImagePullSecrets, corev1.LocalObjectReference{Name: "other-pull-secret"}) + Expect(k8sClient.Update(ctx, sa)).To(Succeed()) + + Consistently(func(g Gomega) { + afterCount := getIstioCNIReconcileCount(g) + g.Expect(afterCount).To(Equal(beforeCount)) + }, 5*time.Second).Should(Succeed(), "IstioRevision was reconciled when it shouldn't have been") + + Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(sa), sa)).To(Succeed()) + Expect(sa.ImagePullSecrets).To(ContainElement(corev1.LocalObjectReference{Name: "other-pull-secret"})) + }) }) When("the resource is deleted", func() { diff --git a/tests/integration/api/istiorevision_test.go b/tests/integration/api/istiorevision_test.go index 9b17e63ca..4bfd819a3 100644 --- a/tests/integration/api/istiorevision_test.go +++ b/tests/integration/api/istiorevision_test.go @@ -98,7 +98,6 @@ var _ = Describe("IstioRevision resource", Ordered, func() { Name: revName, }, Spec: v1alpha1.IstioRevisionSpec{ - Type: v1alpha1.IstioRevisionTypeLocal, Version: supportedversion.Default, Namespace: istioNamespace, Values: &v1alpha1.Values{ @@ -118,7 +117,6 @@ var _ = Describe("IstioRevision resource", Ordered, func() { Name: revName, }, Spec: v1alpha1.IstioRevisionSpec{ - Type: v1alpha1.IstioRevisionTypeLocal, Version: supportedversion.Default, Namespace: istioNamespace, Values: &v1alpha1.Values{ @@ -138,7 +136,6 @@ var _ = Describe("IstioRevision resource", Ordered, func() { Name: "default", }, Spec: v1alpha1.IstioRevisionSpec{ - Type: v1alpha1.IstioRevisionTypeLocal, Version: supportedversion.Default, Namespace: istioNamespace, Values: &v1alpha1.Values{ @@ -158,7 +155,6 @@ var _ = Describe("IstioRevision resource", Ordered, func() { Name: "default", }, Spec: v1alpha1.IstioRevisionSpec{ - Type: v1alpha1.IstioRevisionTypeLocal, Version: supportedversion.Default, Namespace: istioNamespace, Values: &v1alpha1.Values{ @@ -182,7 +178,6 @@ var _ = Describe("IstioRevision resource", Ordered, func() { Name: revName, }, Spec: v1alpha1.IstioRevisionSpec{ - Type: v1alpha1.IstioRevisionTypeLocal, Version: supportedversion.Default, Namespace: nsName, Values: &v1alpha1.Values{ @@ -249,7 +244,6 @@ var _ = Describe("IstioRevision resource", Ordered, func() { Name: revName, }, Spec: v1alpha1.IstioRevisionSpec{ - Type: v1alpha1.IstioRevisionTypeLocal, Version: supportedversion.Default, Namespace: istioNamespace, Values: &v1alpha1.Values{ @@ -321,8 +315,8 @@ var _ = Describe("IstioRevision resource", Ordered, func() { DescribeTable("reconciles owned resource", func(obj client.Object, modify func(obj client.Object), validate func(g Gomega, obj client.Object)) { By("on update", func() { - // ensure all in-flight reconcile operations finish before the test; unfortunately, sleeping seems to be the only option to achieve this - time.Sleep(5 * time.Second) + // ensure all in-flight reconcile operations finish before the test + waitForInFlightReconcileToFinish() Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(obj), obj)).To(Succeed()) @@ -386,20 +380,18 @@ var _ = Describe("IstioRevision resource", Ordered, func() { DescribeTable("skips reconcile when only the status of the owned resource is updated", func(obj client.Object, modify func(obj client.Object)) { - // wait for the in-flight reconcile operations to finish - // unfortunately, I don't see a good way to do this other than by waiting - time.Sleep(5 * time.Second) + waitForInFlightReconcileToFinish() Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(obj), obj)).To(Succeed()) - beforeCount := getReconcileCount(Default) + beforeCount := getIstioRevisionReconcileCount(Default) By("modifying object") modify(obj) Expect(k8sClient.Status().Update(ctx, obj)).To(Succeed()) Consistently(func(g Gomega) { - afterCount := getReconcileCount(g) + afterCount := getIstioRevisionReconcileCount(g) g.Expect(afterCount).To(Equal(beforeCount)) }, 5*time.Second).Should(Succeed()) }, @@ -429,6 +421,34 @@ var _ = Describe("IstioRevision resource", Ordered, func() { ), ) + It("skips reconcile when a pull secret is added to service account", func() { + waitForInFlightReconcileToFinish() + + sa := &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: "istiod-" + revName, + Namespace: istioNamespace, + }, + } + Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(sa), sa)).To(Succeed()) + + GinkgoWriter.Println("sa:", sa) + + beforeCount := getIstioRevisionReconcileCount(Default) + + By("adding pull secret to ServiceAccount") + sa.ImagePullSecrets = append(sa.ImagePullSecrets, corev1.LocalObjectReference{Name: "other-pull-secret"}) + Expect(k8sClient.Update(ctx, sa)).To(Succeed()) + + Consistently(func(g Gomega) { + afterCount := getIstioRevisionReconcileCount(g) + g.Expect(afterCount).To(Equal(beforeCount)) + }, 5*time.Second).Should(Succeed(), "IstioRevision was reconciled when it shouldn't have been") + + Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(sa), sa)).To(Succeed()) + Expect(sa.ImagePullSecrets).To(ContainElement(corev1.LocalObjectReference{Name: "other-pull-secret"})) + }) + It("supports concurrent deployment of two control planes", func() { rev2Name := revName + "2" rev2Key := client.ObjectKey{Name: rev2Name} @@ -440,7 +460,6 @@ var _ = Describe("IstioRevision resource", Ordered, func() { Name: rev2Key.Name, }, Spec: v1alpha1.IstioRevisionSpec{ - Type: v1alpha1.IstioRevisionTypeLocal, Version: supportedversion.Default, Namespace: istioNamespace, Values: &v1alpha1.Values{ @@ -473,7 +492,21 @@ var _ = Describe("IstioRevision resource", Ordered, func() { }) }) -func getReconcileCount(g Gomega) float64 { +func waitForInFlightReconcileToFinish() { + // wait for the in-flight reconcile operations to finish + // unfortunately, I don't see a good way to do this other than by waiting + time.Sleep(5 * time.Second) +} + +func getIstioRevisionReconcileCount(g Gomega) float64 { + return getReconcileCount(g, "istiorevision") +} + +func getIstioCNIReconcileCount(g Gomega) float64 { + return getReconcileCount(g, "istiocni") +} + +func getReconcileCount(g Gomega, controllerName string) float64 { resp, err := http.Get("http://localhost:8080/metrics") g.Expect(err).NotTo(HaveOccurred()) defer resp.Body.Close() @@ -487,7 +520,7 @@ func getReconcileCount(g Gomega) float64 { sum := float64(0) for _, metric := range mf.Metric { for _, l := range metric.Label { - if *l.Name == "controller" && *l.Value == "istiorevision" { + if *l.Name == "controller" && *l.Value == controllerName { sum += metric.GetCounter().GetValue() } } diff --git a/tests/integration/api/istiorevisiontag_test.go b/tests/integration/api/istiorevisiontag_test.go new file mode 100644 index 000000000..75acd211d --- /dev/null +++ b/tests/integration/api/istiorevisiontag_test.go @@ -0,0 +1,382 @@ +//go:build integration + +// Copyright Istio Authors +// +// 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 integration + +import ( + "context" + "time" + + "github.com/istio-ecosystem/sail-operator/api/v1alpha1" + . "github.com/istio-ecosystem/sail-operator/pkg/test/util/ginkgo" + "github.com/istio-ecosystem/sail-operator/pkg/test/util/supportedversion" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + + "istio.io/istio/pkg/ptr" +) + +var _ = Describe("IstioRevisionTag resource", Ordered, func() { + const ( + defaultTagName = "default" + istioName = "test-istio" + istioRevisionTagNamespace = "istiorevisiontag-test" + workloadNamespace = "istiorevisiontag-test-workloads" + + gracePeriod = 5 * time.Second + ) + istio := &v1alpha1.Istio{} + istioKey := client.ObjectKey{Name: istioName} + defaultTagKey := client.ObjectKey{Name: defaultTagName} + workloadNamespaceKey := client.ObjectKey{Name: workloadNamespace} + tag := &v1alpha1.IstioRevisionTag{} + + SetDefaultEventuallyTimeout(30 * time.Second) + SetDefaultEventuallyPollingInterval(time.Second) + + SetDefaultConsistentlyDuration(10 * time.Second) + SetDefaultConsistentlyPollingInterval(time.Second) + + ctx := context.Background() + + namespace := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: istioRevisionTagNamespace, + }, + } + workloadNs := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: workloadNamespace, + }, + } + workload := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-workload", + Namespace: workloadNamespace, + Labels: map[string]string{ + "istio.io/rev": "default", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "test", + Image: "test", + }, + }, + }, + } + BeforeAll(func() { + Step("Creating the Namespaces to perform the tests") + Expect(k8sClient.Create(ctx, namespace)).To(Succeed()) + Expect(k8sClient.Create(ctx, workloadNs)).To(Succeed()) + }) + + AfterAll(func() { + // TODO(user): Attention if you improve this code by adding other context test you MUST + // be aware of the current delete namespace limitations. + // More info: https://book.kubebuilder.io/reference/envtest.html#testing-considerations + Step("Deleting the Namespace to perform the tests") + Expect(k8sClient.Delete(ctx, namespace)).To(Succeed()) + }) + + for _, referencedResource := range []string{v1alpha1.IstioKind, v1alpha1.IstioRevisionKind} { + for _, updateStrategy := range []v1alpha1.UpdateStrategyType{v1alpha1.UpdateStrategyTypeRevisionBased, v1alpha1.UpdateStrategyTypeInPlace} { + Describe("referencing "+referencedResource+" resource, "+string(updateStrategy)+" update", func() { + BeforeAll(func() { + Step("Creating the Istio resource") + istio = &v1alpha1.Istio{ + ObjectMeta: metav1.ObjectMeta{ + Name: istioName, + }, + Spec: v1alpha1.IstioSpec{ + Version: supportedversion.Old, + Namespace: istioRevisionTagNamespace, + UpdateStrategy: &v1alpha1.IstioUpdateStrategy{ + Type: updateStrategy, + InactiveRevisionDeletionGracePeriodSeconds: ptr.Of(int64(gracePeriod.Seconds())), + }, + }, + } + Expect(k8sClient.Create(ctx, istio)).To(Succeed()) + }) + + AfterAll(func() { + deleteAllIstioRevisionTags(ctx) + deleteAllIstiosAndRevisions(ctx) + }) + + When("creating the IstioRevisionTag", func() { + BeforeAll(func() { + targetRef := v1alpha1.IstioRevisionTagTargetReference{ + Kind: referencedResource, + Name: getRevisionName(istio, istio.Spec.Version), + } + if referencedResource == v1alpha1.IstioKind { + targetRef.Name = istioName + } + tag = &v1alpha1.IstioRevisionTag{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default", + }, + Spec: v1alpha1.IstioRevisionTagSpec{ + TargetRef: targetRef, + }, + } + Expect(k8sClient.Create(ctx, tag)).To(Succeed()) + }) + It("updates IstioRevisionTag status", func() { + Eventually(func(g Gomega) { + g.Expect(k8sClient.Get(ctx, defaultTagKey, tag)).To(Succeed()) + g.Expect(tag.Status.ObservedGeneration).To(Equal(tag.Generation)) + g.Expect(tag.Status.IstioRevision).To(Equal(getRevisionName(istio, supportedversion.Old))) + g.Expect(tag.Status.GetCondition(v1alpha1.IstioRevisionTagConditionInUse).Status).To(Equal(metav1.ConditionFalse)) + }).Should(Succeed()) + }) + }) + When("workload ns is labeled with istio-injection label", func() { + BeforeAll(func() { + Expect(k8sClient.Get(ctx, workloadNamespaceKey, workloadNs)).To(Succeed()) + workloadNs.Labels["istio-injection"] = "enabled" + Expect(k8sClient.Update(ctx, workloadNs)).To(Succeed()) + }) + It("updates IstioRevisionTag status and detects that the revision tag is in use", func() { + Eventually(func(g Gomega) { + g.Expect(k8sClient.Get(ctx, defaultTagKey, tag)).To(Succeed()) + g.Expect(tag.Status.ObservedGeneration).To(Equal(tag.Generation)) + g.Expect(tag.Status.GetCondition(v1alpha1.IstioRevisionTagConditionInUse).Status).To(Equal(metav1.ConditionTrue)) + }).Should(Succeed()) + }) + }) + + When("updating the Istio control plane version", func() { + BeforeAll(func() { + Expect(k8sClient.Get(ctx, istioKey, istio)).To(Succeed()) + istio.Spec.Version = supportedversion.Default + Expect(k8sClient.Update(ctx, istio)).To(Succeed()) + }) + + if referencedResource == v1alpha1.IstioRevisionKind { + It("updates IstioRevisionTag status and still references old revision", func() { + Eventually(func(g Gomega) { + g.Expect(k8sClient.Get(ctx, defaultTagKey, tag)).To(Succeed()) + g.Expect(tag.Status.IstioRevision).To(Equal(getRevisionName(istio, supportedversion.Old))) + g.Expect(tag.Status.GetCondition(v1alpha1.IstioRevisionTagConditionInUse).Status).To(Equal(metav1.ConditionTrue)) + }).Should(Succeed()) + }) + } else { + It("updates IstioRevisionTag status and shows new referenced revision", func() { + Eventually(func(g Gomega) { + g.Expect(k8sClient.Get(ctx, defaultTagKey, tag)).To(Succeed()) + g.Expect(tag.Status.IstioRevision).To(Equal(getRevisionName(istio, supportedversion.New))) + g.Expect(tag.Status.GetCondition(v1alpha1.IstioRevisionTagConditionInUse).Status).To(Equal(metav1.ConditionTrue)) + }).Should(Succeed()) + }) + } + }) + + When("deleting the label on the workload namespace", func() { + BeforeAll(func() { + Expect(k8sClient.Get(ctx, workloadNamespaceKey, workloadNs)).To(Succeed()) + delete(workloadNs.Labels, "istio-injection") + Expect(k8sClient.Update(ctx, workloadNs)).To(Succeed()) + }) + + It("updates IstioRevisionTag status and detects that the tag is no longer in use", func() { + Eventually(func(g Gomega) { + g.Expect(k8sClient.Get(ctx, defaultTagKey, tag)).To(Succeed()) + g.Expect(tag.Status.GetCondition(v1alpha1.IstioRevisionTagConditionInUse).Status).To(Equal(metav1.ConditionFalse)) + }).Should(Succeed()) + }) + if referencedResource == v1alpha1.IstioRevisionKind && updateStrategy == v1alpha1.UpdateStrategyTypeRevisionBased { + It("does not delete the referenced IstioRevision even though it is no longer in use and not the active revision", func() { + revKey := client.ObjectKey{Name: getRevisionName(istio, supportedversion.Old)} + rev := &v1alpha1.IstioRevision{} + Consistently(k8sClient.Get).WithArguments(ctx, revKey, rev).Should(Succeed()) + }) + } + }) + + When("creating a Pod that references the tag", func() { + BeforeAll(func() { + Expect(k8sClient.Create(ctx, workload.DeepCopy())).To(Succeed()) + }) + + AfterAll(func() { + deletePod(ctx, workload) + }) + + It("updates IstioRevisionTag status and detects that the revision tag is in use", func() { + Eventually(func(g Gomega) { + g.Expect(k8sClient.Get(ctx, defaultTagKey, tag)).To(Succeed()) + g.Expect(tag.Status.ObservedGeneration).To(Equal(tag.Generation)) + g.Expect(tag.Status.GetCondition(v1alpha1.IstioRevisionTagConditionInUse).Status).To(Equal(metav1.ConditionTrue)) + }).Should(Succeed()) + }) + }) + }) + } + } + + When("Creating an Istio with name 'default' and attempting to create another IstioRevisionTag with the same name", func() { + BeforeAll(func() { + Step("Creating the Istio resources") + istio = &v1alpha1.Istio{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default", + }, + Spec: v1alpha1.IstioSpec{ + Version: supportedversion.Old, + Namespace: istioRevisionTagNamespace, + UpdateStrategy: &v1alpha1.IstioUpdateStrategy{ + Type: v1alpha1.UpdateStrategyTypeInPlace, + InactiveRevisionDeletionGracePeriodSeconds: ptr.Of(int64(gracePeriod.Seconds())), + }, + }, + } + Expect(k8sClient.Create(ctx, istio)).To(Succeed()) + istio = &v1alpha1.Istio{ + ObjectMeta: metav1.ObjectMeta{ + Name: istioName, + }, + Spec: v1alpha1.IstioSpec{ + Version: supportedversion.Old, + Namespace: istioRevisionTagNamespace, + UpdateStrategy: &v1alpha1.IstioUpdateStrategy{ + Type: v1alpha1.UpdateStrategyTypeInPlace, + InactiveRevisionDeletionGracePeriodSeconds: ptr.Of(int64(gracePeriod.Seconds())), + }, + }, + } + Expect(k8sClient.Create(ctx, istio)).To(Succeed()) + tag = &v1alpha1.IstioRevisionTag{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default", + }, + Spec: v1alpha1.IstioRevisionTagSpec{ + TargetRef: v1alpha1.IstioRevisionTagTargetReference{ + Kind: "Istio", + Name: istioName, + }, + }, + } + Expect(k8sClient.Create(ctx, tag)).To(Succeed()) + }) + + AfterAll(func() { + deleteAllIstioRevisionTags(ctx) + deleteAllIstiosAndRevisions(ctx) + }) + + It("fails to reconcile IstioRevisionTag", func() { + Eventually(func(g Gomega) { + g.Expect(k8sClient.Get(ctx, defaultTagKey, tag)).To(Succeed()) + g.Expect(tag.Status.ObservedGeneration).To(Equal(tag.Generation)) + }).Should(Succeed()) + Consistently(func(g Gomega) { + g.Expect(k8sClient.Get(ctx, defaultTagKey, tag)).To(Succeed()) + g.Expect(tag.Status.GetCondition(v1alpha1.IstioRevisionTagConditionReconciled).Status).To(Equal(metav1.ConditionFalse)) + g.Expect(tag.Status.GetCondition(v1alpha1.IstioRevisionTagConditionReconciled).Reason).To(Equal(v1alpha1.IstioRevisionTagReasonNameAlreadyExists)) + }).Should(Succeed()) + }) + }) + + When("Creating an IstioRevisionTag with a dangling TargetRef", func() { + BeforeAll(func() { + tag = &v1alpha1.IstioRevisionTag{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default", + }, + Spec: v1alpha1.IstioRevisionTagSpec{ + TargetRef: v1alpha1.IstioRevisionTagTargetReference{ + Kind: "Istio", + Name: istioName, + }, + }, + } + Expect(k8sClient.Create(ctx, tag)).To(Succeed()) + }) + + AfterAll(func() { + deleteAllIstiosAndRevisions(ctx) + deleteAllIstioRevisionTags(ctx) + }) + + It("fails to reconcile IstioRevisionTag", func() { + Eventually(func(g Gomega) { + g.Expect(k8sClient.Get(ctx, defaultTagKey, tag)).To(Succeed()) + g.Expect(tag.Status.ObservedGeneration).To(Equal(tag.Generation)) + }).Should(Succeed()) + Consistently(func(g Gomega) { + g.Expect(k8sClient.Get(ctx, defaultTagKey, tag)).To(Succeed()) + g.Expect(tag.Status.GetCondition(v1alpha1.IstioRevisionTagConditionReconciled).Status).To(Equal(metav1.ConditionFalse)) + g.Expect(tag.Status.GetCondition(v1alpha1.IstioRevisionTagConditionReconciled).Reason).To(Equal(v1alpha1.IstioRevisionTagReasonReferenceNotFound)) + }).Should(Succeed()) + }) + + When("attempting to create IstioRevision with same name as the tag's", func() { + BeforeAll(func() { + istio = &v1alpha1.Istio{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default", + }, + Spec: v1alpha1.IstioSpec{ + Version: supportedversion.Old, + Namespace: istioRevisionTagNamespace, + UpdateStrategy: &v1alpha1.IstioUpdateStrategy{ + Type: v1alpha1.UpdateStrategyTypeInPlace, + InactiveRevisionDeletionGracePeriodSeconds: ptr.Of(int64(gracePeriod.Seconds())), + }, + }, + } + Expect(k8sClient.Create(ctx, istio)).To(Succeed()) + }) + + It("fails with ValidationError", func() { + Eventually(func(g Gomega) { + rev := &v1alpha1.IstioRevision{} + g.Expect(k8sClient.Get(ctx, defaultTagKey, rev)).To(Succeed()) + condition := rev.Status.GetCondition(v1alpha1.IstioRevisionConditionReconciled) + g.Expect(condition.Status).To(Equal(metav1.ConditionFalse)) + g.Expect(condition.Message).To(ContainSubstring("an IstioRevisionTag exists with this name")) + }).Should(Succeed()) + }) + }) + }) +}) + +func deleteAllIstioRevisionTags(ctx context.Context) { + Step("Deleting all IstioRevisionTags") + Eventually(k8sClient.DeleteAllOf).WithArguments(ctx, &v1alpha1.IstioRevisionTag{}).Should(Succeed()) + Eventually(func(g Gomega) { + list := &v1alpha1.IstioRevisionTagList{} + g.Expect(k8sClient.List(ctx, list)).To(Succeed()) + g.Expect(list.Items).To(BeEmpty()) + }).Should(Succeed()) +} + +func deletePod(ctx context.Context, pod *corev1.Pod) { + Step("Deleting pod") + Eventually(k8sClient.Delete).WithArguments(ctx, pod).Should(Succeed()) + Eventually(func(g Gomega) { + p := &corev1.Pod{} + g.Expect(k8sClient.Get(ctx, types.NamespacedName{Name: pod.Name, Namespace: pod.Namespace}, p)).To(ReturnNotFoundError()) + }).Should(Succeed()) +} diff --git a/tests/integration/api/suite_test.go b/tests/integration/api/suite_test.go index f7cd9e7f4..a4cc42f09 100644 --- a/tests/integration/api/suite_test.go +++ b/tests/integration/api/suite_test.go @@ -24,7 +24,7 @@ import ( "github.com/istio-ecosystem/sail-operator/controllers/istio" "github.com/istio-ecosystem/sail-operator/controllers/istiocni" "github.com/istio-ecosystem/sail-operator/controllers/istiorevision" - "github.com/istio-ecosystem/sail-operator/controllers/remoteistio" + "github.com/istio-ecosystem/sail-operator/controllers/istiorevisiontag" "github.com/istio-ecosystem/sail-operator/pkg/config" "github.com/istio-ecosystem/sail-operator/pkg/helm" "github.com/istio-ecosystem/sail-operator/pkg/scheme" @@ -83,8 +83,8 @@ var _ = BeforeSuite(func() { cl := mgr.GetClient() scheme := mgr.GetScheme() Expect(istio.NewReconciler(cfg, cl, scheme).SetupWithManager(mgr)).To(Succeed()) - Expect(remoteistio.NewReconciler(cfg, cl, scheme).SetupWithManager(mgr)).To(Succeed()) Expect(istiorevision.NewReconciler(cfg, cl, scheme, chartManager).SetupWithManager(mgr)).To(Succeed()) + Expect(istiorevisiontag.NewReconciler(cfg, cl, scheme, chartManager).SetupWithManager(mgr)).To(Succeed()) Expect(istiocni.NewReconciler(cfg, cl, scheme, chartManager).SetupWithManager(mgr)).To(Succeed()) // create new cancellable context diff --git a/versions.yaml b/versions.yaml index e42e1851a..bfcf86649 100644 --- a/versions.yaml +++ b/versions.yaml @@ -11,6 +11,37 @@ # go.mod affect the generated API schema for the Sail CRDs (e.g. IstioRevision), # as well as all the Istio CRDs (e.g. VirtualService). versions: + - name: v1.24.1 + version: 1.24.1 + repo: https://github.com/istio/istio + commit: 1.24.1 + charts: + - https://istio-release.storage.googleapis.com/charts/base-1.24.1.tgz + - https://istio-release.storage.googleapis.com/charts/istiod-1.24.1.tgz + - https://istio-release.storage.googleapis.com/charts/gateway-1.24.1.tgz + - https://istio-release.storage.googleapis.com/charts/cni-1.24.1.tgz + - https://istio-release.storage.googleapis.com/charts/ztunnel-1.24.1.tgz + - name: v1.24.0 + version: 1.24.0 + repo: https://github.com/istio/istio + commit: 1.24.0 + charts: + - https://istio-release.storage.googleapis.com/charts/base-1.24.0.tgz + - https://istio-release.storage.googleapis.com/charts/istiod-1.24.0.tgz + - https://istio-release.storage.googleapis.com/charts/gateway-1.24.0.tgz + - https://istio-release.storage.googleapis.com/charts/cni-1.24.0.tgz + - https://istio-release.storage.googleapis.com/charts/ztunnel-1.24.0.tgz + - name: v1.23.3 + version: 1.23.3 + repo: https://github.com/istio/istio + commit: 1.23.3 + charts: + - https://istio-release.storage.googleapis.com/charts/base-1.23.3.tgz + - https://istio-release.storage.googleapis.com/charts/istiod-1.23.3.tgz + - https://istio-release.storage.googleapis.com/charts/istiod-remote-1.23.3.tgz + - https://istio-release.storage.googleapis.com/charts/gateway-1.23.3.tgz + - https://istio-release.storage.googleapis.com/charts/cni-1.23.3.tgz + - https://istio-release.storage.googleapis.com/charts/ztunnel-1.23.3.tgz - name: v1.23.2 version: 1.23.2 repo: https://github.com/istio/istio @@ -22,6 +53,16 @@ versions: - https://istio-release.storage.googleapis.com/charts/gateway-1.23.2.tgz - https://istio-release.storage.googleapis.com/charts/cni-1.23.2.tgz - https://istio-release.storage.googleapis.com/charts/ztunnel-1.23.2.tgz + - name: v1.22.6 + version: 1.22.6 + repo: https://github.com/istio/istio + commit: 1.22.6 + charts: + - https://istio-release.storage.googleapis.com/charts/base-1.22.6.tgz + - https://istio-release.storage.googleapis.com/charts/istiod-1.22.6.tgz + - https://istio-release.storage.googleapis.com/charts/gateway-1.22.6.tgz + - https://istio-release.storage.googleapis.com/charts/cni-1.22.6.tgz + - https://istio-release.storage.googleapis.com/charts/ztunnel-1.22.6.tgz - name: v1.22.5 version: 1.22.5 repo: https://github.com/istio/istio @@ -43,13 +84,13 @@ versions: - https://istio-release.storage.googleapis.com/charts/cni-1.21.6.tgz - https://istio-release.storage.googleapis.com/charts/ztunnel-1.21.6.tgz - name: latest - version: 1.24-alpha.bb972b546125d3f001cc11114e9fc95486b891ec + version: 1.25-alpha.4b76e91786ce92fbba2e2c53b6242c0946157f6b repo: https://github.com/istio/istio branch: master - commit: bb972b546125d3f001cc11114e9fc95486b891ec + commit: 4b76e91786ce92fbba2e2c53b6242c0946157f6b charts: - - https://storage.googleapis.com/istio-build/dev/1.24-alpha.bb972b546125d3f001cc11114e9fc95486b891ec/helm/base-1.24-alpha.bb972b546125d3f001cc11114e9fc95486b891ec.tgz - - https://storage.googleapis.com/istio-build/dev/1.24-alpha.bb972b546125d3f001cc11114e9fc95486b891ec/helm/cni-1.24-alpha.bb972b546125d3f001cc11114e9fc95486b891ec.tgz - - https://storage.googleapis.com/istio-build/dev/1.24-alpha.bb972b546125d3f001cc11114e9fc95486b891ec/helm/gateway-1.24-alpha.bb972b546125d3f001cc11114e9fc95486b891ec.tgz - - https://storage.googleapis.com/istio-build/dev/1.24-alpha.bb972b546125d3f001cc11114e9fc95486b891ec/helm/istiod-1.24-alpha.bb972b546125d3f001cc11114e9fc95486b891ec.tgz - - https://storage.googleapis.com/istio-build/dev/1.24-alpha.bb972b546125d3f001cc11114e9fc95486b891ec/helm/ztunnel-1.24-alpha.bb972b546125d3f001cc11114e9fc95486b891ec.tgz + - https://storage.googleapis.com/istio-build/dev/1.25-alpha.4b76e91786ce92fbba2e2c53b6242c0946157f6b/helm/base-1.25-alpha.4b76e91786ce92fbba2e2c53b6242c0946157f6b.tgz + - https://storage.googleapis.com/istio-build/dev/1.25-alpha.4b76e91786ce92fbba2e2c53b6242c0946157f6b/helm/cni-1.25-alpha.4b76e91786ce92fbba2e2c53b6242c0946157f6b.tgz + - https://storage.googleapis.com/istio-build/dev/1.25-alpha.4b76e91786ce92fbba2e2c53b6242c0946157f6b/helm/gateway-1.25-alpha.4b76e91786ce92fbba2e2c53b6242c0946157f6b.tgz + - https://storage.googleapis.com/istio-build/dev/1.25-alpha.4b76e91786ce92fbba2e2c53b6242c0946157f6b/helm/istiod-1.25-alpha.4b76e91786ce92fbba2e2c53b6242c0946157f6b.tgz + - https://storage.googleapis.com/istio-build/dev/1.25-alpha.4b76e91786ce92fbba2e2c53b6242c0946157f6b/helm/ztunnel-1.25-alpha.4b76e91786ce92fbba2e2c53b6242c0946157f6b.tgz