Skip to content

Commit

Permalink
introduce upgrade mechanism and add upgrade patch to v1 storage migra…
Browse files Browse the repository at this point in the history
…tion

Signed-off-by: Jeeva Kandasamy <jkandasa@redhat.com>
  • Loading branch information
jkandasa committed Sep 11, 2023
1 parent 2159c07 commit 09bf9ac
Show file tree
Hide file tree
Showing 23 changed files with 2,002 additions and 19 deletions.
1 change: 1 addition & 0 deletions pkg/apis/operator/v1alpha1/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ const (
LabelOperandName = "operator.tekton.dev/operand-name"
DbSecretHash = "operator.tekton.dev/db-secret-hash"
DeploymentSpecHashValueLabelKey = "operator.tekton.dev/deployment-spec-applied-hash" // used to recreate pods, if there is a change detected in deployments spec
LastAppliedUpgradeKey = "operator.tekton.dev/last-applied-upgrade" // used to monitor last applied patch, via upgrade package

UpgradePending = "upgrade pending"
Reinstalling = "reinstalling"
Expand Down
16 changes: 16 additions & 0 deletions pkg/apis/operator/v1alpha1/tektonconfig_lifecycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,19 @@ func (tcs *TektonConfigStatus) GetVersion() string {
func (tcs *TektonConfigStatus) SetVersion(version string) {
tcs.Version = version
}

// returns last applied upgrade version
func (tcs *TektonConfigStatus) GetLastAppliedUpgradeVersion() string {
if tcs.Annotations == nil {
return ""
}
return tcs.Annotations[LastAppliedUpgradeKey]
}

// updates last applied upgrade version
func (tcs *TektonConfigStatus) SetLastAppliedUpgradeVersion(lastAppliedUpgradeVersion string) {
if tcs.Annotations == nil {
tcs.Annotations = map[string]string{}
}
tcs.Annotations[LastAppliedUpgradeKey] = lastAppliedUpgradeVersion
}
41 changes: 25 additions & 16 deletions pkg/reconciler/platform/platform.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
/*
Copyright 2022 The Tekton 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 platform

import (
Expand All @@ -7,6 +23,7 @@ import (
"strings"

installer "github.com/tektoncd/operator/pkg/reconciler/shared/tektoninstallerset"
"github.com/tektoncd/operator/upgrade"
"knative.dev/pkg/injection"
"knative.dev/pkg/injection/sharedmain"
"knative.dev/pkg/signals"
Expand Down Expand Up @@ -69,22 +86,6 @@ func activeControllers(p Platform) ControllerMap {
// the result of disabledControllers is the set of controllers excluded by activeControllers function
// in other words, disabledControllers returns a map which has controllers "not" specified in the controlelrNames input to a platform
// the returned map is a subset of the platform specific map which stores all-supported-controllers
/*
Copyright 2022 The Tekton 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.
*/

func disabledControllers(p Platform) ControllerMap {
pParams := p.PlatformParams()
result := p.AllSupportedControllers()
Expand All @@ -109,6 +110,14 @@ func startMain(p Platform, ctrls ControllerMap) {
ctx, _ := injection.EnableInjectionOrDie(signals.NewContext(), cfg)
ctx = contextWithPlatformName(ctx, pParams.Name)
installer.InitTektonInstallerSetClient(ctx)

// execute upgrade patches only on "tekton-operator-lifecycle" container
for _, controllerName := range p.PlatformParams().ControllerNames {
if controllerName == ControllerTektonConfig {
upgrade.StartUpgrade(ctx, cfg)
}
}

sharedmain.MainWithConfig(ctx,
pParams.SharedMainName,
cfg,
Expand Down
12 changes: 9 additions & 3 deletions pkg/reconciler/shared/tektonconfig/tektonconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ import (
"github.com/tektoncd/operator/pkg/reconciler/shared/tektonconfig/chain"
"github.com/tektoncd/operator/pkg/reconciler/shared/tektonconfig/pipeline"
"github.com/tektoncd/operator/pkg/reconciler/shared/tektonconfig/trigger"
"github.com/tektoncd/operator/upgrade"
apierrs "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"knative.dev/pkg/logging"
pkgreconciler "knative.dev/pkg/reconciler"
Expand Down Expand Up @@ -100,6 +100,12 @@ func (r *Reconciler) ReconcileKind(ctx context.Context, tc *v1alpha1.TektonConfi
tc.Status.InitializeConditions()
tc.Status.SetVersion(r.operatorVersion)

// if there is not lastAppliedUpgradeVersion defined, assumes this is fresh installation,
// and updates the lastAppliedUpgradeVersion to most recent to avoid run upgrade patches on fresh installation
if tc.Status.GetLastAppliedUpgradeVersion() == "" {
tc.Status.SetLastAppliedUpgradeVersion(upgrade.GetLatestUpgradeVersion().String())
}

logger.Infow("Reconciling TektonConfig", "status", tc.Status)
if tc.GetName() != v1alpha1.ConfigResourceName {
msg := fmt.Sprintf("Resource ignored, Expected Name: %s, Got Name: %s",
Expand Down Expand Up @@ -193,7 +199,7 @@ func (r *Reconciler) ReconcileKind(ctx context.Context, tc *v1alpha1.TektonConfi
tc.Status.MarkPostInstallComplete()

// Update the object for any spec changes
if _, err := r.operatorClientSet.OperatorV1alpha1().TektonConfigs().Update(ctx, tc, v1.UpdateOptions{}); err != nil {
if _, err := r.operatorClientSet.OperatorV1alpha1().TektonConfigs().Update(ctx, tc, metav1.UpdateOptions{}); err != nil {
return err
}

Expand Down Expand Up @@ -241,7 +247,7 @@ func (r *Reconciler) markUpgrade(ctx context.Context, tc *v1alpha1.TektonConfig)

// Update the object for any spec changes
if _, err := r.operatorClientSet.OperatorV1alpha1().TektonConfigs().Update(ctx,
tc, v1.UpdateOptions{}); err != nil {
tc, metav1.UpdateOptions{}); err != nil {
return err
}
return v1alpha1.RECONCILE_AGAIN_ERR
Expand Down
55 changes: 55 additions & 0 deletions upgrade/helper/storage_version_migrator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
Copyright 2023 The Tekton 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 upgrade

import (
"context"
"fmt"

"go.uber.org/zap"
apierrs "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime/schema"
"knative.dev/pkg/apiextensions/storageversion"
)

// performs crd storage version upgrade
// lists all the resources and,
// keeps only one storage version on the crd
func MigrateStorageVersion(ctx context.Context, logger *zap.SugaredLogger, migrator *storageversion.Migrator, crdGroups []string) error {
logger.Infof("migrating %d group resources", len(crdGroups))

for _, crdGroupString := range crdGroups {
crdGroup := schema.ParseGroupResource(crdGroupString)
if crdGroup.Empty() {
logger.Errorf("unable to parse group version: %s", crdGroupString)
return fmt.Errorf("unable to parse group version: %s", crdGroupString)
}
logger.Info("migrating group resource ", crdGroup)
if err := migrator.Migrate(ctx, crdGroup); err != nil {
if apierrs.IsNotFound(err) {
logger.Infow("ignoring resource migration - unable to fetch a crd",
"crd", crdGroup, err,
)
continue
}
logger.Errorw("failed to migrate: ", err)
return err
}
}

return nil
}
103 changes: 103 additions & 0 deletions upgrade/helper/storage_version_migrator_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
Copyright 2023 The Tekton 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 upgrade

import (
"context"
"testing"

"github.com/stretchr/testify/assert"
apix "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apixFake "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/fake"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
runtime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
dynamicFake "k8s.io/client-go/dynamic/fake"
"knative.dev/pkg/apiextensions/storageversion"
"knative.dev/pkg/logging"
)

func TestMigrateStorageVersion(t *testing.T) {
fakeKind := schema.GroupKind{
Kind: "Fake",
Group: "group.dev",
}

fakeGroup := schema.GroupResource{
Resource: "fakes",
Group: "group.dev",
}

fakeCRD := &apix.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: fakeGroup.String(),
},
Spec: apix.CustomResourceDefinitionSpec{
Group: fakeKind.Group,
Versions: []apix.CustomResourceDefinitionVersion{
{Name: "v1alpha1", Served: true, Storage: false},
{Name: "v1beta1", Served: true, Storage: false},
{Name: "v1", Served: true, Storage: true},
},
},
Status: apix.CustomResourceDefinitionStatus{
StoredVersions: []string{
"v1alpha1",
"v1beta1",
"v1",
},
},
}

ctx := context.TODO()
resources := []runtime.Object{
fakeCrdResource("resource-1", "group.dev/v1alpha1"),
fakeCrdResource("resource-2", "group.dev/v1beta1"),
fakeCrdResource("resource-3", "group.dev/v1beta2"),
fakeCrdResource("resource-4", "group.dev/v1"),
}

dclient := dynamicFake.NewSimpleDynamicClient(runtime.NewScheme(), resources...)
cclient := apixFake.NewSimpleClientset(fakeCRD)
migrator := storageversion.NewMigrator(dclient, cclient)
logger := logging.FromContext(ctx)

// TEST
// expects only "v1"
err := MigrateStorageVersion(ctx, logger, migrator, []string{"fakes.group.dev", "unknown.group.dev"})
assert.NoError(t, err)
crd, err := cclient.ApiextensionsV1().CustomResourceDefinitions().Get(ctx, fakeCRD.GetName(), metav1.GetOptions{})
assert.NoError(t, err)
storageVersions := crd.Status.StoredVersions
assert.Len(t, storageVersions, 1)
assert.Equal(t, "v1", storageVersions[0])

}

func fakeCrdResource(name, apiVersion string) runtime.Object {
return &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": apiVersion,
"kind": "Fake",
"metadata": map[string]interface{}{
"name": name,
"namespace": "default",
},
},
}
}

0 comments on commit 09bf9ac

Please sign in to comment.