...

Source file src/github.com/GoogleCloudPlatform/k8s-config-connector/operator/pkg/preflight/upgrade.go

Documentation: github.com/GoogleCloudPlatform/k8s-config-connector/operator/pkg/preflight

     1  // Copyright 2022 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package preflight
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  
    21  	"github.com/GoogleCloudPlatform/k8s-config-connector/operator/pkg/k8s"
    22  	"github.com/GoogleCloudPlatform/k8s-config-connector/operator/pkg/manifest"
    23  
    24  	"github.com/blang/semver"
    25  	corev1 "k8s.io/api/core/v1"
    26  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    27  	"k8s.io/apimachinery/pkg/types"
    28  	ctrl "sigs.k8s.io/controller-runtime"
    29  	"sigs.k8s.io/controller-runtime/pkg/client"
    30  	"sigs.k8s.io/kubebuilder-declarative-pattern/pkg/patterns/declarative"
    31  )
    32  
    33  var (
    34  	ulog = ctrl.Log.WithName("UpgradeChecker")
    35  )
    36  
    37  // NewUpgradeChecker provides an implementation of declarative.Preflight that
    38  // does version comparison between the version of the existing KCC and and the version to deploy.
    39  // If it's a major version change, it returns error and surface the error status on the DeclarativeObject.
    40  func NewUpgradeChecker(client client.Client, repo manifest.Repository) *UpgradeChecker {
    41  	return &UpgradeChecker{client: client, repo: repo}
    42  }
    43  
    44  type UpgradeChecker struct {
    45  	client client.Client
    46  	repo   manifest.Repository
    47  }
    48  
    49  func (u *UpgradeChecker) Preflight(ctx context.Context, o declarative.DeclarativeObject) error {
    50  	ulog.Info("preflight check before reconciling the object", "kind", o.GetObjectKind().GroupVersionKind().Kind, "name", o.GetName(), "namespace", o.GetNamespace())
    51  	if !o.GetDeletionTimestamp().IsZero() {
    52  		return nil
    53  	}
    54  	ns := &corev1.Namespace{}
    55  	if err := u.client.Get(ctx, types.NamespacedName{Name: k8s.CNRMSystemNamespace}, ns); err != nil {
    56  		if apierrors.IsNotFound(err) {
    57  			ulog.Info(fmt.Sprintf("%v namespace is not found. Continue the reconciliation.", k8s.CNRMSystemNamespace))
    58  			return nil
    59  		}
    60  		return err
    61  	}
    62  
    63  	currentVersionRaw := ns.GetAnnotations()[k8s.VersionAnnotation]
    64  	if currentVersionRaw == "" {
    65  		ulog.Info(fmt.Sprintf("WARNING: No ConfigConnector version is annotated with '%v' namespace. Attempt to deploy ConfigConnector bundle with best effort", k8s.CNRMSystemNamespace))
    66  		return nil
    67  	}
    68  
    69  	channel, err := u.repo.LoadChannel(ctx, k8s.StableChannel)
    70  	if err != nil {
    71  		return fmt.Errorf("preflight check failed loading the channel %v: %v", k8s.StableChannel, err)
    72  	}
    73  	version, err := channel.Latest(ctx, k8s.ConfigConnectorComponentName)
    74  	if err != nil {
    75  		return fmt.Errorf("preflight check failed resolving the version to deploy: %v", err)
    76  	}
    77  	if version == nil {
    78  		return fmt.Errorf("could not find the latest version in channel %v", k8s.StableChannel)
    79  	}
    80  	versionToDeployRaw := version.Version
    81  	currentVersion, err := semver.ParseTolerant(currentVersionRaw)
    82  	if err != nil {
    83  		return fmt.Errorf("current version %v is not a valid semantic version: %v", currentVersionRaw, err)
    84  	}
    85  	ulog.Info("Checking version", "current version", currentVersion)
    86  	versionToDeploy, err := semver.ParseTolerant(versionToDeployRaw)
    87  	if err != nil {
    88  		return fmt.Errorf("the version to deploy %v is not a valid semantic version: %v", versionToDeployRaw, err)
    89  	}
    90  	ulog.Info("Checking version", "version to deploy", versionToDeploy)
    91  	if compareMajorOnly(currentVersion, versionToDeploy) != 0 {
    92  		return fmt.Errorf("incompatible version: stop reconciling the existing ConfigConnector of version %v to version %v since it's a major version change. Please kubectl delete the existing ConfigConnector object and recreate it", currentVersion, versionToDeploy)
    93  	}
    94  	return nil
    95  }
    96  
    97  func compareMajorOnly(v, w semver.Version) int {
    98  	if v.Major != w.Major {
    99  		if v.Major > w.Major {
   100  			return 1
   101  		}
   102  		return -1
   103  	}
   104  	return 0
   105  }
   106  

View as plain text