github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/controller/configuration/config_template_utils.go (about)

     1  /*
     2  Copyright (C) 2022-2023 ApeCloud Co., Ltd
     3  
     4  This file is part of KubeBlocks project
     5  
     6  This program is free software: you can redistribute it and/or modify
     7  it under the terms of the GNU Affero General Public License as published by
     8  the Free Software Foundation, either version 3 of the License, or
     9  (at your option) any later version.
    10  
    11  This program is distributed in the hope that it will be useful
    12  but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  GNU Affero General Public License for more details.
    15  
    16  You should have received a copy of the GNU Affero General Public License
    17  along with this program.  If not, see <http://www.gnu.org/licenses/>.
    18  */
    19  
    20  package configuration
    21  
    22  import (
    23  	"context"
    24  	"fmt"
    25  
    26  	corev1 "k8s.io/api/core/v1"
    27  	"k8s.io/apimachinery/pkg/types"
    28  
    29  	appsv1alpha1 "github.com/1aal/kubeblocks/apis/apps/v1alpha1"
    30  	ictrlclient "github.com/1aal/kubeblocks/pkg/controller/client"
    31  	"github.com/1aal/kubeblocks/pkg/controller/component"
    32  )
    33  
    34  type Visitor interface {
    35  	Visit(VisitorFunc) error
    36  }
    37  
    38  type VisitorFunc func(*component.SynthesizedComponent, error) error
    39  
    40  // DecoratedVisitor will invoke the decorators in order prior to invoking the visitor function
    41  // passed to Visit. An error will terminate the visit.
    42  type DecoratedVisitor struct {
    43  	visitor    Visitor
    44  	decorators []VisitorFunc
    45  }
    46  
    47  // NewDecoratedVisitor will create a visitor that invokes the provided visitor functions before
    48  // the user supplied visitor function is invoked, giving them the opportunity to mutate the Info
    49  // object or terminate early with an error.
    50  func NewDecoratedVisitor(v Visitor, fn ...VisitorFunc) Visitor {
    51  	if len(fn) == 0 {
    52  		return v
    53  	}
    54  	return DecoratedVisitor{v, fn}
    55  }
    56  
    57  // Visit implements Visitor
    58  func (v DecoratedVisitor) Visit(fn VisitorFunc) error {
    59  	return v.visitor.Visit(func(component *component.SynthesizedComponent, err error) error {
    60  		if err != nil {
    61  			return err
    62  		}
    63  		for i := range v.decorators {
    64  			if err := v.decorators[i](component, nil); err != nil {
    65  				return err
    66  			}
    67  		}
    68  		return fn(component, nil)
    69  	})
    70  }
    71  
    72  // ComponentVisitor implements Visitor, it will visit the component.SynthesizedComponent
    73  type ComponentVisitor struct {
    74  	component *component.SynthesizedComponent
    75  }
    76  
    77  // Visit implements Visitor
    78  func (r *ComponentVisitor) Visit(fn VisitorFunc) error {
    79  	return fn(r.component, nil)
    80  }
    81  
    82  // resolveServiceReferences is the visitor function to resolve the service reference
    83  func resolveServiceReferences(cli ictrlclient.ReadonlyClient, ctx context.Context) VisitorFunc {
    84  	return func(component *component.SynthesizedComponent, err error) error {
    85  		if err != nil {
    86  			return err
    87  		}
    88  		if component.ServiceReferences == nil {
    89  			return nil
    90  		}
    91  		for _, serviceDescriptor := range component.ServiceReferences {
    92  			// TODO: currently, only support endpoint and port, serviceDescriptor.Spec.Auth is not supported
    93  			if err := resolveCredentialVar(cli, ctx, serviceDescriptor.Namespace, serviceDescriptor.Spec.Endpoint, serviceDescriptor.Spec.Port); err != nil {
    94  				return err
    95  			}
    96  		}
    97  		return nil
    98  	}
    99  }
   100  
   101  // resolveCredentialVar resolve the credentialVar.ValueFrom to the real value
   102  // TODO: currently, we set the valueFrom to the value, which need to be refactored
   103  func resolveCredentialVar(cli ictrlclient.ReadonlyClient, ctx context.Context, namespace string, credentialVars ...*appsv1alpha1.CredentialVar) error {
   104  	resolveSecretKeyRef := func(credentialVar *appsv1alpha1.CredentialVar) error {
   105  		if credentialVar.ValueFrom == nil || credentialVar.ValueFrom.SecretKeyRef == nil {
   106  			return nil
   107  		}
   108  		secretName := credentialVar.ValueFrom.SecretKeyRef.Name
   109  		secretKey := credentialVar.ValueFrom.SecretKeyRef.Key
   110  		secretRef := &corev1.Secret{}
   111  		if err := cli.Get(ctx, types.NamespacedName{Name: secretName, Namespace: namespace}, secretRef); err != nil {
   112  			return err
   113  		}
   114  		runtimeValBytes, ok := secretRef.Data[secretKey]
   115  		if !ok {
   116  			return fmt.Errorf("couldn't find key %v in Secret %v/%v", secretKey, namespace, secretName)
   117  		}
   118  		// Set the valueFrom to the value and clear the valueFrom
   119  		credentialVar.ValueFrom = nil
   120  		credentialVar.Value = string(runtimeValBytes)
   121  		return nil
   122  	}
   123  
   124  	resolveConfigMapKeyRef := func(credentialVar *appsv1alpha1.CredentialVar) error {
   125  		if credentialVar.ValueFrom == nil || credentialVar.ValueFrom.ConfigMapKeyRef == nil {
   126  			return nil
   127  		}
   128  		configMapName := credentialVar.ValueFrom.ConfigMapKeyRef.Name
   129  		configMapKey := credentialVar.ValueFrom.ConfigMapKeyRef.Key
   130  		configMapRef := &corev1.ConfigMap{}
   131  		if err := cli.Get(ctx, types.NamespacedName{Name: configMapName, Namespace: namespace}, configMapRef); err != nil {
   132  			return err
   133  		}
   134  		runtimeValBytes, ok := configMapRef.Data[configMapKey]
   135  		if !ok {
   136  			return fmt.Errorf("couldn't find key %v in ConfigMap %v/%v", configMapKey, namespace, configMapName)
   137  		}
   138  		// Set the valueFrom to the value and clear the valueFrom
   139  		credentialVar.ValueFrom = nil
   140  		credentialVar.Value = runtimeValBytes
   141  		return nil
   142  	}
   143  
   144  	if len(credentialVars) == 0 {
   145  		return nil
   146  	}
   147  
   148  	for _, credentialVar := range credentialVars {
   149  		// TODO: replace thw build-in placeholder with the real value
   150  		if credentialVar.Value != "" {
   151  			return nil
   152  		}
   153  		// TODO: currently, we set the valueFrom to the value, which need to be refactored
   154  		if credentialVar.ValueFrom != nil {
   155  			if err := resolveSecretKeyRef(credentialVar); err != nil {
   156  				return err
   157  			}
   158  			if err := resolveConfigMapKeyRef(credentialVar); err != nil {
   159  				return err
   160  			}
   161  		}
   162  	}
   163  	return nil
   164  }