Skip to content
This repository has been archived by the owner on Mar 4, 2024. It is now read-only.

EVEREST-633 Fix adding namespaces after initial install #286

Merged
merged 3 commits into from
Feb 9, 2024
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
36 changes: 25 additions & 11 deletions pkg/install/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"errors"
"fmt"
"net/url"
"os"
"strings"

"github.com/AlecAivazis/survey/v2"
Expand Down Expand Up @@ -82,6 +83,8 @@
monitoringNamespace = "everest-monitoring"
// EverestMonitoringNamespaceEnvVar is the name of the environment variable that holds the monitoring namespace.
EverestMonitoringNamespaceEnvVar = "MONITORING_NAMESPACE"
// disableTelemetryEnvVar is the name of the environment variable that disables telemetry.
disableTelemetryEnvVar = "DISABLE_TELEMETRY"
)

type (
Expand Down Expand Up @@ -258,7 +261,7 @@
}

if !everestExists {
o.l.Info(fmt.Sprintf("Deploying Everest to %s", SystemNamespace))

Check failure on line 264 in pkg/install/install.go

View workflow job for this annotation

GitHub Actions / Check (1.21.x, false)

fmt.Sprintf can be replaced with string addition (perfsprint)
err = o.kubeClient.InstallEverest(ctx, SystemNamespace)
if err != nil {
return err
Expand Down Expand Up @@ -471,6 +474,11 @@

o.l.Infof("Installing %s operator", operatorName)

disableTelemetry, ok := os.LookupEnv(disableTelemetryEnvVar)
if !ok || disableTelemetry != "true" {
disableTelemetry = "false"
}

params := kubernetes.InstallOperatorRequest{
Namespace: namespace,
Name: operatorName,
Expand All @@ -479,21 +487,27 @@
CatalogSourceNamespace: catalogSourceNamespace,
Channel: channel,
InstallPlanApproval: v1alpha1.ApprovalManual,
}
if len(o.config.Namespaces) != 0 && operatorName == everestOperatorName {
params.TargetNamespaces = o.config.Namespaces
params.SubscriptionConfig = &v1alpha1.SubscriptionConfig{
SubscriptionConfig: &v1alpha1.SubscriptionConfig{
Env: []corev1.EnvVar{
{
Name: kubernetes.EverestDBNamespacesEnvVar,
Value: strings.Join(o.config.Namespaces, ","),
},
{
Name: EverestMonitoringNamespaceEnvVar,
Value: monitoringNamespace,
Name: disableTelemetryEnvVar,
Value: disableTelemetry,
},
},
}
},
}
if operatorName == everestOperatorName {
params.TargetNamespaces = o.config.Namespaces
params.SubscriptionConfig.Env = append(params.SubscriptionConfig.Env, []corev1.EnvVar{
{
Name: EverestMonitoringNamespaceEnvVar,
Value: monitoringNamespace,
},
{
Name: kubernetes.EverestDBNamespacesEnvVar,
Value: strings.Join(o.config.Namespaces, ","),
},
}...)
}

if err := o.kubeClient.InstallOperator(ctx, params); err != nil {
Expand Down
16 changes: 16 additions & 0 deletions pkg/kubernetes/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -1192,6 +1192,22 @@ func (c *Client) CreateSubscription(ctx context.Context, namespace string, subsc
return sub, nil
}

// UpdateSubscription updates an OLM subscription.
func (c *Client) UpdateSubscription(ctx context.Context, namespace string, subscription *v1alpha1.Subscription) (*v1alpha1.Subscription, error) {
operatorClient, err := versioned.NewForConfig(c.restConfig)
if err != nil {
return nil, errors.Join(err, errors.New("cannot create an operator client instance"))
}
sub, err := operatorClient.
OperatorsV1alpha1().
Subscriptions(namespace).
Update(ctx, subscription, metav1.UpdateOptions{})
if err != nil {
return sub, err
}
return sub, nil
}

// CreateSubscriptionForCatalog creates an OLM subscription.
func (c *Client) CreateSubscriptionForCatalog(ctx context.Context, namespace, name, catalogNamespace, catalog,
packageName, channel, startingCSV string, approval v1alpha1.Approval,
Expand Down
2 changes: 2 additions & 0 deletions pkg/kubernetes/client/kubeclient_interface.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 30 additions & 0 deletions pkg/kubernetes/client/mock_kube_client_connector.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

134 changes: 99 additions & 35 deletions pkg/kubernetes/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (
"io/fs"
"log"
"net/http"
"os"
"sort"
"strings"
"time"

Expand Down Expand Up @@ -87,7 +87,6 @@ const (
databaseClusterAPIVersion = "everest.percona.com/v1alpha1"
restartAnnotationKey = "everest.percona.com/restart"
managedByKey = "everest.percona.com/managed-by"
disableTelemetryEnvVar = "DISABLE_TELEMETRY"
// ContainerStateWaiting represents a state when container requires some
// operations being done in order to complete start up.
ContainerStateWaiting ContainerState = "waiting"
Expand Down Expand Up @@ -629,47 +628,112 @@ type InstallOperatorRequest struct {
SubscriptionConfig *olmv1alpha1.SubscriptionConfig
}

func mergeNamespacesEnvVar(str1, str2 string) string {
ns1 := strings.Split(str1, ",")
ns2 := strings.Split(str2, ",")
nsMap := map[string]struct{}{}

for _, ns := range ns1 {
if ns == "" {
continue
}
nsMap[ns] = struct{}{}
}

for _, ns := range ns2 {
if ns == "" {
continue
}
nsMap[ns] = struct{}{}
}

namespaces := []string{}
for ns := range nsMap {
namespaces = append(namespaces, ns)
}

sort.Strings(namespaces)

return strings.Join(namespaces, ",")
}

func mergeSubscriptionConfig(sub *olmv1alpha1.SubscriptionConfig, cfg *olmv1alpha1.SubscriptionConfig) *olmv1alpha1.SubscriptionConfig {
if sub == nil {
sub = &olmv1alpha1.SubscriptionConfig{Env: []corev1.EnvVar{}}
}

if cfg == nil {
return sub
}

for _, e := range cfg.Env {
found := false
for i, se := range sub.Env {
if e.Name == se.Name {
found = true
// If the environment variable is not the namespaces, just override it
if e.Name != EverestDBNamespacesEnvVar {
sub.Env[i].Value = e.Value
break
}

// Merge the namespaces
sub.Env[i].Value = mergeNamespacesEnvVar(se.Value, e.Value)

break
}
}
if !found {
sub.Env = append(sub.Env, e)
}
}

return sub
}

// InstallOperator installs an operator via OLM.
func (k *Kubernetes) InstallOperator(ctx context.Context, req InstallOperatorRequest) error { //nolint:funlen
disableTelemetry, ok := os.LookupEnv(disableTelemetryEnvVar)
if !ok || disableTelemetry != "true" {
disableTelemetry = "false"
}
config := &olmv1alpha1.SubscriptionConfig{Env: []corev1.EnvVar{}}
if req.SubscriptionConfig != nil {
config = req.SubscriptionConfig
subscription, err := k.client.GetSubscription(ctx, req.Namespace, req.Name)
if err != nil && !apierrors.IsNotFound(err) {
return errors.Join(err, errors.New("cannot get subscription"))
}
config.Env = append(config.Env, corev1.EnvVar{
Name: disableTelemetryEnvVar,
Value: disableTelemetry,
})
subscription := &olmv1alpha1.Subscription{
TypeMeta: metav1.TypeMeta{
Kind: olmv1alpha1.SubscriptionKind,
APIVersion: olmv1alpha1.SubscriptionCRDAPIVersion,
},
ObjectMeta: metav1.ObjectMeta{
Namespace: req.Namespace,
Name: req.Name,
},
Spec: &olmv1alpha1.SubscriptionSpec{
CatalogSource: req.CatalogSource,
CatalogSourceNamespace: req.CatalogSourceNamespace,
Package: req.Name,
Channel: req.Channel,
StartingCSV: req.StartingCSV,
InstallPlanApproval: req.InstallPlanApproval,
Config: config,
},
if apierrors.IsNotFound(err) {
subscription = &olmv1alpha1.Subscription{
TypeMeta: metav1.TypeMeta{
Kind: olmv1alpha1.SubscriptionKind,
APIVersion: olmv1alpha1.SubscriptionCRDAPIVersion,
},
ObjectMeta: metav1.ObjectMeta{
Namespace: req.Namespace,
Name: req.Name,
},
Spec: &olmv1alpha1.SubscriptionSpec{
CatalogSource: req.CatalogSource,
CatalogSourceNamespace: req.CatalogSourceNamespace,
Package: req.Name,
Channel: req.Channel,
StartingCSV: req.StartingCSV,
InstallPlanApproval: req.InstallPlanApproval,
},
}
}
subs, err := k.client.CreateSubscription(ctx, req.Namespace, subscription)
if err != nil {
return errors.Join(err, errors.New("cannot create a subscription to install the operator"))

subscription.Spec.Config = mergeSubscriptionConfig(subscription.Spec.Config, req.SubscriptionConfig)
if apierrors.IsNotFound(err) {
_, err := k.client.CreateSubscription(ctx, req.Namespace, subscription)
if err != nil {
return errors.Join(err, errors.New("cannot create a subscription to install the operator"))
}
} else {
_, err := k.client.UpdateSubscription(ctx, req.Namespace, subscription)
if err != nil {
return errors.Join(err, errors.New("cannot update a subscription to install the operator"))
}
}

err = wait.PollUntilContextTimeout(ctx, pollInterval, pollDuration, false, func(ctx context.Context) (bool, error) {
k.l.Debugf("Polling subscription %s/%s", req.Namespace, req.Name)
subs, err = k.client.GetSubscription(ctx, req.Namespace, req.Name)
subs, err := k.client.GetSubscription(ctx, req.Namespace, req.Name)
if err != nil {
return false, errors.Join(err, fmt.Errorf("cannot get an install plan for the operator subscription: %q", req.Name))
}
Expand Down
Loading
Loading