Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions bundle/bundle.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// 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 bundle

import _ "embed"

//go:embed manifests/servicemeshoperator3.clusterserviceversion.yaml
var CSV []byte
27 changes: 27 additions & 0 deletions chart/crds/crds.go
Original file line number Diff line number Diff line change
@@ -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 crds provides embedded CRD YAML files for Istio and Sail resources.
package crds

import "embed"

// FS contains all CRD YAML files from the chart/crds directory.
// This allows programmatic access to CRDs for installation.
//
// CRD files follow the naming convention: {group}_{plural}.yaml
// Example: extensions.istio.io_wasmplugins.yaml
//
//go:embed *.yaml
var FS embed.FS
110 changes: 15 additions & 95 deletions controllers/istiocni/istiocni_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"context"
"errors"
"fmt"
"path"
"reflect"

"github.com/go-logr/logr"
Expand All @@ -28,12 +27,10 @@ import (
"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/istiovalues"
"github.com/istio-ecosystem/sail-operator/pkg/istioversion"
"github.com/istio-ecosystem/sail-operator/pkg/kube"
"github.com/istio-ecosystem/sail-operator/pkg/predicate"
sharedreconcile "github.com/istio-ecosystem/sail-operator/pkg/reconcile"
"github.com/istio-ecosystem/sail-operator/pkg/reconciler"
"github.com/istio-ecosystem/sail-operator/pkg/validation"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
networkingv1 "k8s.io/api/networking/v1"
Expand All @@ -53,11 +50,6 @@ import (
"istio.io/istio/pkg/ptr"
)

const (
cniReleaseName = "istio-cni"
cniChartName = "cni"
)

// Reconciler reconciles an IstioCNI object
type Reconciler struct {
client.Client
Expand Down Expand Up @@ -107,33 +99,19 @@ func (r *Reconciler) Reconcile(ctx context.Context, cni *v1.IstioCNI) (ctrl.Resu
}

func (r *Reconciler) Finalize(ctx context.Context, cni *v1.IstioCNI) error {
return r.uninstallHelmChart(ctx, cni)
cniReconciler := r.newCNIReconciler()
return cniReconciler.Uninstall(ctx, cni.Spec.Namespace)
}

func (r *Reconciler) doReconcile(ctx context.Context, cni *v1.IstioCNI) error {
log := logf.FromContext(ctx)
if err := r.validate(ctx, cni); err != nil {
return err
}

log.Info("Installing Helm chart")
return r.installHelmChart(ctx, cni)
}
cniReconciler := r.newCNIReconciler()

func (r *Reconciler) validate(ctx context.Context, cni *v1.IstioCNI) error {
if cni.Spec.Version == "" {
return reconciler.NewValidationError("spec.version not set")
}
if cni.Spec.Namespace == "" {
return reconciler.NewValidationError("spec.namespace not set")
}
if err := validation.ValidateTargetNamespace(ctx, r.Client, cni.Spec.Namespace); err != nil {
if err := cniReconciler.Validate(ctx, cni.Spec.Version, cni.Spec.Namespace); err != nil {
return err
}
return nil
}

func (r *Reconciler) installHelmChart(ctx context.Context, cni *v1.IstioCNI) error {
log.Info("Installing Helm chart")
ownerReference := metav1.OwnerReference{
APIVersion: v1.GroupVersion.String(),
Kind: v1.IstioCNIKind,
Expand All @@ -142,75 +120,17 @@ func (r *Reconciler) installHelmChart(ctx context.Context, cni *v1.IstioCNI) err
Controller: ptr.Of(true),
BlockOwnerDeletion: ptr.Of(true),
}

version, err := istioversion.Resolve(cni.Spec.Version)
if err != nil {
return fmt.Errorf("failed to resolve IstioCNI version for %q: %w", cni.Name, err)
}

// get userValues from Istio.spec.values
userValues := cni.Spec.Values

// apply image digests from configuration, if not already set by user
userValues = applyImageDigests(version, userValues, config.Config)

// apply vendor-specific default values
userValues, err = istiovalues.ApplyIstioCNIVendorDefaults(version, userValues)
if err != nil {
return fmt.Errorf("failed to apply vendor defaults: %w", err)
}

// apply userValues on top of defaultValues from profiles
mergedHelmValues, err := istiovalues.ApplyProfilesAndPlatform(
r.Config.ResourceFS, version, r.Config.Platform, r.Config.DefaultProfile, cni.Spec.Profile, helm.FromValues(userValues))
if err != nil {
return fmt.Errorf("failed to apply profile: %w", err)
}

_, err = r.ChartManager.UpgradeOrInstallChart(
ctx, r.Config.ResourceFS, r.getChartPath(version), mergedHelmValues, cni.Spec.Namespace, cniReleaseName, &ownerReference)
if err != nil {
return fmt.Errorf("failed to install/update Helm chart %q: %w", cniChartName, err)
}
return nil
return cniReconciler.Install(ctx, cni.Spec.Version, cni.Spec.Namespace, cni.Spec.Values, cni.Spec.Profile, &ownerReference)
}

func (r *Reconciler) getChartPath(version string) string {
return path.Join(version, "charts", cniChartName)
}

func applyImageDigests(version string, values *v1.CNIValues, config config.OperatorConfig) *v1.CNIValues {
imageDigests, digestsDefined := config.ImageDigests[version]
// if we don't have default image digests defined for this version, it's a no-op
if !digestsDefined {
return values
}

// if a global hub or tag value is configured by the user, don't set image digests
if values != nil && values.Global != nil && (values.Global.Hub != nil || values.Global.Tag != nil) {
return values
}

if values == nil {
values = &v1.CNIValues{}
}

// set image digest unless any part of the image has been configured by the user
if values.Cni == nil {
values.Cni = &v1.CNIConfig{}
}
if values.Cni.Image == nil && values.Cni.Hub == nil && values.Cni.Tag == nil {
values.Cni.Image = &imageDigests.CNIImage
}
return values
}

func (r *Reconciler) uninstallHelmChart(ctx context.Context, cni *v1.IstioCNI) error {
_, err := r.ChartManager.UninstallChart(ctx, cniReleaseName, cni.Spec.Namespace)
if err != nil {
return fmt.Errorf("failed to uninstall Helm chart %q: %w", cniChartName, err)
}
return nil
func (r *Reconciler) newCNIReconciler() *sharedreconcile.CNIReconciler {
return sharedreconcile.NewCNIReconciler(sharedreconcile.Config{
ResourceFS: r.Config.ResourceFS,
Platform: r.Config.Platform,
DefaultProfile: r.Config.DefaultProfile,
OperatorNamespace: r.Config.OperatorNamespace,
ChartManager: r.ChartManager,
}, r.Client)
}

// SetupWithManager sets up the controller with the Manager.
Expand Down
18 changes: 12 additions & 6 deletions controllers/istiocni/istiocni_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/istio-ecosystem/sail-operator/pkg/config"
"github.com/istio-ecosystem/sail-operator/pkg/istiovalues"
"github.com/istio-ecosystem/sail-operator/pkg/istioversion"
sharedreconcile "github.com/istio-ecosystem/sail-operator/pkg/reconcile"
"github.com/istio-ecosystem/sail-operator/pkg/scheme"
. "github.com/onsi/gomega"
appsv1 "k8s.io/api/apps/v1"
Expand Down Expand Up @@ -77,7 +78,7 @@ func TestValidate(t *testing.T) {
},
},
objects: []client.Object{ns},
expectErr: "spec.version not set",
expectErr: "version not set",
},
{
name: "no namespace",
Expand All @@ -90,7 +91,7 @@ func TestValidate(t *testing.T) {
},
},
objects: []client.Object{ns},
expectErr: "spec.namespace not set",
expectErr: "namespace not set",
},
{
name: "namespace not found",
Expand All @@ -111,9 +112,14 @@ func TestValidate(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
g := NewWithT(t)
cl := fake.NewClientBuilder().WithScheme(scheme.Scheme).WithObjects(tc.objects...).Build()
r := NewReconciler(cfg, cl, scheme.Scheme, nil)

err := r.validate(context.TODO(), tc.cni)
cniReconciler := sharedreconcile.NewCNIReconciler(sharedreconcile.Config{
ResourceFS: cfg.ResourceFS,
Platform: cfg.Platform,
DefaultProfile: cfg.DefaultProfile,
OperatorNamespace: cfg.OperatorNamespace,
}, cl)

err := cniReconciler.Validate(context.TODO(), tc.cni.Spec.Version, tc.cni.Spec.Namespace)
if tc.expectErr == "" {
g.Expect(err).ToNot(HaveOccurred())
} else {
Expand Down Expand Up @@ -496,7 +502,7 @@ func TestApplyImageDigests(t *testing.T) {
if err != nil {
t.Errorf("failed to resolve IstioCNI version for %q: %v", tc.input.Name, err)
}
result := applyImageDigests(version, tc.input.Spec.Values, tc.config)
result := sharedreconcile.ApplyCNIImageDigests(version, tc.input.Spec.Values, tc.config)
if diff := cmp.Diff(tc.expectValues, result); diff != "" {
t.Errorf("unexpected merge result; diff (-expected, +actual):\n%v", diff)
}
Expand Down
Loading