Skip to content

Commit 5e5b074

Browse files
committed
fix(ENTESB-18466): Flag operator as non-upgradeable
* When operator is installed via OLM, a separate OperatorCondition CR is created which can be updated with an "upgradeable" condition flagging whether the OLM can upgrade the operator * Turn off upgradeable state when * operator is initializing * operator is upgrading * Turn on upgradeable state when * operator has started * operator has completed an upgrade * Finds the OperatorCondition using the deployment name, deployment owner (CSV) and the CSV name (OperatorCondition has same name as the CSV). * Adds conditions tests but cannot completely implement due to operator-framework/operator-lib#103
1 parent 53d5231 commit 5e5b074

File tree

12 files changed

+425
-16
lines changed

12 files changed

+425
-16
lines changed

install/operator/pkg/generator/assets/install/cluster_role_olm.yml.tmpl

+1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
- operatorgroups
6363
- subscriptions
6464
- installplans
65+
- operatorconditions
6566
verbs: [ create, delete, update, get, list, watch ]
6667
- apiGroups:
6768
- operators.coreos.com

install/operator/pkg/syndesis/action/checkupdates.go

+16-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88

99
synapi "github.com/syndesisio/syndesis/install/operator/pkg/apis/syndesis/v1beta3"
1010
"github.com/syndesisio/syndesis/install/operator/pkg/syndesis/clienttools"
11+
"github.com/syndesisio/syndesis/install/operator/pkg/syndesis/olm"
12+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1113
"sigs.k8s.io/controller-runtime/pkg/manager"
1214
)
1315

@@ -39,7 +41,7 @@ func (a checkUpdatesAction) Execute(ctx context.Context, syndesis *synapi.Syndes
3941
// Everything fine
4042
return nil
4143
} else {
42-
return a.setPhaseToUpgrading(ctx, syndesis)
44+
return a.setPhaseToUpgrading(ctx, syndesis, operatorNamespace)
4345
}
4446
}
4547

@@ -48,7 +50,19 @@ func (a checkUpdatesAction) Execute(ctx context.Context, syndesis *synapi.Syndes
4850
* needed to avoid race conditions where k8s wasn't able to update or
4951
* kubernetes didn't change the object yet
5052
*/
51-
func (a checkUpdatesAction) setPhaseToUpgrading(ctx context.Context, syndesis *synapi.Syndesis) (err error) {
53+
func (a checkUpdatesAction) setPhaseToUpgrading(ctx context.Context, syndesis *synapi.Syndesis, operatorNamespace string) (err error) {
54+
55+
// Declare an upgradeable Condition as false if applicable
56+
state := olm.ConditionState{
57+
Status: metav1.ConditionFalse,
58+
Reason: "Upgrading",
59+
Message: "Operator is upgrading the components",
60+
}
61+
err = olm.SetUpgradeCondition(ctx, a.clientTools, operatorNamespace, state)
62+
if err != nil {
63+
a.log.Error(err, "Failed to set the upgrade condition on the operator")
64+
}
65+
5266
target := syndesis.DeepCopy()
5367
target.Status.Phase = synapi.SyndesisPhaseUpgrading
5468
target.Status.TargetVersion = a.operatorVersion

install/operator/pkg/syndesis/action/initialize.go

+14
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@ import (
77

88
synapi "github.com/syndesisio/syndesis/install/operator/pkg/apis/syndesis/v1beta3"
99
"github.com/syndesisio/syndesis/install/operator/pkg/syndesis/clienttools"
10+
"github.com/syndesisio/syndesis/install/operator/pkg/syndesis/olm"
1011
"sigs.k8s.io/controller-runtime/pkg/client"
1112
"sigs.k8s.io/controller-runtime/pkg/manager"
13+
14+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1215
)
1316

1417
// Initializes a Syndesis resource with no status and starts the installation process
@@ -45,6 +48,17 @@ func (a *initializeAction) Execute(ctx context.Context, syndesis *synapi.Syndesi
4548
target.Status.Description = "Cannot install two Syndesis resources in the same namespace"
4649
a.log.Error(nil, "Cannot initialize Syndesis resource because its a duplicate", "name", syndesis.Name)
4750
} else {
51+
// Declare an upgradeable Condition as false if applicable
52+
state := olm.ConditionState{
53+
Status: metav1.ConditionFalse,
54+
Reason: "Initializing",
55+
Message: "Operator is installing",
56+
}
57+
err = olm.SetUpgradeCondition(ctx, a.clientTools, operatorNamespace, state)
58+
if err != nil {
59+
a.log.Error(err, "Failed to set the upgrade condition on the operator")
60+
}
61+
4862
syndesisVersion := pkg.DefaultOperatorTag
4963
target.Status.Phase = synapi.SyndesisPhaseInstalling
5064
target.Status.Reason = synapi.SyndesisStatusReasonMissing

install/operator/pkg/syndesis/action/startup.go

+12
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
synpkg "github.com/syndesisio/syndesis/install/operator/pkg"
99
synapi "github.com/syndesisio/syndesis/install/operator/pkg/apis/syndesis/v1beta3"
1010
"github.com/syndesisio/syndesis/install/operator/pkg/syndesis/clienttools"
11+
"github.com/syndesisio/syndesis/install/operator/pkg/syndesis/olm"
1112
corev1 "k8s.io/api/core/v1"
1213
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1314
"k8s.io/apimachinery/pkg/labels"
@@ -71,6 +72,17 @@ func (a *startupAction) Execute(ctx context.Context, syndesis *synapi.Syndesis,
7172
}
7273

7374
if ready {
75+
// Declare the operator upgradeable, if applicable
76+
state := olm.ConditionState{
77+
Status: metav1.ConditionTrue,
78+
Reason: "Started",
79+
Message: "Operator and components have been successfully started",
80+
}
81+
err = olm.SetUpgradeCondition(ctx, a.clientTools, operatorNamespace, state)
82+
if err != nil {
83+
a.log.Error(err, "Failed to set the upgrade condition on the operator")
84+
}
85+
7486
target := syndesis.DeepCopy()
7587
target.Status.Phase = synapi.SyndesisPhaseInstalled
7688
target.Status.Reason = synapi.SyndesisStatusReasonMissing

install/operator/pkg/syndesis/action/upgrade.go

+14-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"time"
66

77
"github.com/syndesisio/syndesis/install/operator/pkg/syndesis/clienttools"
8+
"github.com/syndesisio/syndesis/install/operator/pkg/syndesis/olm"
89
"github.com/syndesisio/syndesis/install/operator/pkg/syndesis/upgrade"
910

1011
"github.com/syndesisio/syndesis/install/operator/pkg"
@@ -75,7 +76,7 @@ func (a *upgradeAction) Execute(ctx context.Context, syndesis *synapi.Syndesis,
7576
} else if syndesis.Status.Phase == synapi.SyndesisPhasePostUpgradeRunSucceed {
7677
// We land here only if the install phase after upgrading finished correctly
7778
a.log.Info("syndesis resource post upgrade ran successfully", "name", syndesis.Name, "previous version", syndesis.Status.Version, "target version", targetVersion)
78-
return a.completeUpgrade(ctx, syndesis, targetVersion)
79+
return a.completeUpgrade(ctx, syndesis, targetVersion, operatorNamespace)
7980
} else if syndesis.Status.Phase == synapi.SyndesisPhasePostUpgradeRun {
8081
// If the first run of the install action failed, we land here. We need to retry
8182
// this few times to consider the cases where install action return error due to
@@ -103,7 +104,18 @@ func (a *upgradeAction) Execute(ctx context.Context, syndesis *synapi.Syndesis,
103104
* needed to avoid race conditions where k8s wasn't yet able to update or
104105
* kubernetes didn't change the object yet
105106
*/
106-
func (a *upgradeAction) completeUpgrade(ctx context.Context, syndesis *synapi.Syndesis, newVersion string) (err error) {
107+
func (a *upgradeAction) completeUpgrade(ctx context.Context, syndesis *synapi.Syndesis, newVersion string, operatorNamespace string) (err error) {
108+
// Declare the operator upgradeable, if applicable
109+
state := olm.ConditionState{
110+
Status: metav1.ConditionTrue,
111+
Reason: "CompletedUpgrade",
112+
Message: "Operator component state has been upgraded",
113+
}
114+
err = olm.SetUpgradeCondition(ctx, a.clientTools, operatorNamespace, state)
115+
if err != nil {
116+
a.log.Error(err, "Failed to set the upgrade condition on the operator")
117+
}
118+
107119
target := syndesis.DeepCopy()
108120
target.Status.Phase = synapi.SyndesisPhaseInstalled
109121
target.Status.TargetVersion = ""

install/operator/pkg/syndesis/clienttools/clienttools.go

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
olmapiv1 "github.com/operator-framework/api/pkg/operators/v1"
2323
olmapiv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
2424
olmapiv1alpha2 "github.com/operator-framework/api/pkg/operators/v1alpha2"
25+
olmapiv2 "github.com/operator-framework/api/pkg/operators/v2"
2526
olmcli "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned"
2627
olmpkgsvr "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1"
2728
"github.com/syndesisio/syndesis/install/operator/pkg/util"
@@ -74,6 +75,7 @@ func (ck *ClientTools) GetScheme() *runtime.Scheme {
7475
olmapiv1alpha2.SchemeBuilder.AddToScheme(ck.scheme)
7576
olmapiv1alpha1.SchemeBuilder.AddToScheme(ck.scheme)
7677
olmapiv1.SchemeBuilder.AddToScheme(ck.scheme)
78+
olmapiv2.AddToScheme(ck.scheme)
7779
olmpkgsvr.SchemeBuilder.AddToScheme(ck.scheme)
7880
projectv1.AddToScheme(ck.scheme)
7981
}

install/operator/pkg/syndesis/configuration/configuration.go

+20
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,26 @@ func GetProperties(ctx context.Context, file string, clientTools *clienttools.Cl
420420
return configuration, nil
421421
}
422422

423+
// Load the configuration and return the name of this product
424+
func GetProductName(file string) (string, error) {
425+
configuration := &Config{}
426+
if err := configuration.loadFromFile(file); err != nil {
427+
return "", err
428+
}
429+
430+
return configuration.ProductName, nil
431+
}
432+
433+
// Load the configuration and return the name of this product
434+
func GetVersion(file string) (string, error) {
435+
configuration := &Config{}
436+
if err := configuration.loadFromFile(file); err != nil {
437+
return "", err
438+
}
439+
440+
return configuration.Version, nil
441+
}
442+
423443
// Load configuration from config file. Config file is expected to be a yaml
424444
// The returned configuration is parsed to JSON and returned as a Config object
425445
func (config *Config) loadFromFile(file string) error {

install/operator/pkg/syndesis/configuration/configuration_test.go

+20
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,26 @@ func Test_loadFromFile(t *testing.T) {
105105
}
106106
}
107107

108+
func Test_getProductName(t *testing.T) {
109+
configFile := "../../../build/conf/config-test.yaml"
110+
name, err := GetProductName(configFile)
111+
assert.NoError(t, err)
112+
113+
andOrName := func() bool {
114+
return name == "syndesis" || name == "fuse-online"
115+
}
116+
117+
assert.Condition(t, andOrName)
118+
}
119+
120+
func Test_getVersion(t *testing.T) {
121+
configFile := "../../../build/conf/config-test.yaml"
122+
version, err := GetVersion(configFile)
123+
assert.NoError(t, err)
124+
125+
assert.Equal(t, "7.7.0", version)
126+
}
127+
108128
func Test_setConfigFromEnv(t *testing.T) {
109129
tests := []struct {
110130
name string
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
/*
2+
* Copyright (C) 2020 Red Hat, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package olm
18+
19+
import (
20+
"context"
21+
"os"
22+
23+
olmapiv2 "github.com/operator-framework/api/pkg/operators/v2"
24+
conditions "github.com/operator-framework/operator-lib/conditions"
25+
errs "github.com/pkg/errors"
26+
synpkg "github.com/syndesisio/syndesis/install/operator/pkg"
27+
"github.com/syndesisio/syndesis/install/operator/pkg/syndesis/capabilities"
28+
"github.com/syndesisio/syndesis/install/operator/pkg/syndesis/clienttools"
29+
"github.com/syndesisio/syndesis/install/operator/pkg/syndesis/configuration"
30+
appsv1 "k8s.io/api/apps/v1"
31+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
32+
"k8s.io/apimachinery/pkg/types"
33+
logf "sigs.k8s.io/controller-runtime/pkg/log"
34+
)
35+
36+
var opCondLog = logf.Log.WithName("operator-condition-log")
37+
38+
type ConditionState struct {
39+
// The value of the condition, either metav1.ConditionTrue or metav1.ConditionFalse
40+
Status metav1.ConditionStatus
41+
// The single word reason for the condition setting
42+
// Must start with a letter
43+
// Rest of the world can include letters, numbers, commas and colons
44+
// Cannot end with comma or colon
45+
Reason string
46+
// The description of the reason for the condition change.
47+
Message string
48+
}
49+
50+
func GetConditionName(ctx context.Context, clientTools *clienttools.ClientTools, namespace string) (string, error) {
51+
opCondLog.V(synpkg.DEBUG_LOGGING_LVL).Info("Finding OLM Operator Condition")
52+
53+
apiSpec, err := capabilities.ApiCapabilities(clientTools)
54+
if err != nil {
55+
return "", err
56+
}
57+
58+
if !apiSpec.OlmSupport {
59+
//
60+
// This cluster does not support OLM so nothing to do
61+
//
62+
opCondLog.V(synpkg.DEBUG_LOGGING_LVL).Info("No OLM support ... aborting Operation Condition Search")
63+
return "", nil
64+
}
65+
66+
rtClient, err := clientTools.RuntimeClient()
67+
if err != nil {
68+
return "", errs.Wrap(err, "Failed to initialise runtime client")
69+
}
70+
71+
//
72+
// Find the operator condition associated with this operator
73+
//
74+
// deployment -> owned by CSV -> operator condition has the same name
75+
//
76+
configName, err := configuration.GetProductName(configuration.TemplateConfig)
77+
if err != nil {
78+
return "", errs.Wrap(err, "Failed to determine product name")
79+
}
80+
deploymentName := configName + "-operator"
81+
82+
opCondLog.V(synpkg.DEBUG_LOGGING_LVL).Info("Finding Operator Deployment", "name", deploymentName)
83+
deployment := &appsv1.Deployment{}
84+
if err = rtClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: deploymentName}, deployment); err != nil {
85+
return "", err // Should find the deployment of this operator
86+
}
87+
88+
opCondLog.V(synpkg.DEBUG_LOGGING_LVL).Info("Operator Deployment found", "name", deploymentName)
89+
ownerRefs := deployment.GetOwnerReferences()
90+
if len(ownerRefs) > 1 || len(ownerRefs) == 0 {
91+
// No operator condition as this is not owned by a CSV
92+
return "", nil
93+
}
94+
95+
if ownerRefs[0].Kind != "ClusterServiceVersion" {
96+
// No operator condition as this is not owned by a CSV
97+
return "", nil
98+
}
99+
100+
opCondLog.V(synpkg.DEBUG_LOGGING_LVL).Info("CSV Owned Deployment", "name", deploymentName, "owner", ownerRefs[0].Name)
101+
return ownerRefs[0].Name, nil
102+
}
103+
104+
//
105+
// Creates the condition if it does not already exist
106+
//
107+
func SetUpgradeCondition(ctx context.Context, clientTools *clienttools.ClientTools, namespace string, state ConditionState) error {
108+
109+
conditionName, err := GetConditionName(ctx, clientTools, namespace)
110+
if err != nil {
111+
return err
112+
} else if conditionName == "" {
113+
return nil
114+
}
115+
116+
rtClient, err := clientTools.RuntimeClient()
117+
if err != nil {
118+
return errs.Wrap(err, "Failed to initialise runtime client")
119+
}
120+
121+
clusterFactory := conditions.InClusterFactory{rtClient}
122+
err = os.Setenv("OPERATOR_CONDITION_NAME", conditionName)
123+
if err != nil {
124+
return err
125+
}
126+
uc, err := clusterFactory.NewCondition(olmapiv2.ConditionType(olmapiv2.Upgradeable))
127+
if err != nil {
128+
return err
129+
}
130+
131+
err = uc.Set(ctx,
132+
state.Status,
133+
conditions.WithReason(state.Reason),
134+
conditions.WithMessage(state.Message))
135+
if err != nil {
136+
return err
137+
}
138+
139+
return nil
140+
}

0 commit comments

Comments
 (0)