github.com/verrazzano/verrazzano@v1.7.1/tests/e2e/update/fluentd/fluentd_update.go (about)

     1  // Copyright (c) 2022, Oracle and/or its affiliates.
     2  // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
     3  
     4  package fluentd
     5  
     6  import (
     7  	"context"
     8  	"crypto/rand"
     9  	"crypto/rsa"
    10  	"crypto/x509"
    11  	"encoding/pem"
    12  	"fmt"
    13  	"strings"
    14  	"time"
    15  
    16  	"github.com/onsi/gomega"
    17  	"github.com/verrazzano/verrazzano/pkg/constants"
    18  	"github.com/verrazzano/verrazzano/pkg/k8sutil"
    19  	vzapi "github.com/verrazzano/verrazzano/platform-operator/apis/verrazzano/v1alpha1"
    20  	"github.com/verrazzano/verrazzano/platform-operator/apis/verrazzano/v1beta1"
    21  	pcons "github.com/verrazzano/verrazzano/platform-operator/constants"
    22  	"github.com/verrazzano/verrazzano/tests/e2e/pkg"
    23  	"github.com/verrazzano/verrazzano/tests/e2e/pkg/update"
    24  	appsv1 "k8s.io/api/apps/v1"
    25  	corev1 "k8s.io/api/core/v1"
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  )
    28  
    29  const (
    30  	fluentdName     = "fluentd"
    31  	cacerts         = "cacerts"
    32  	shortWait       = 1 * time.Minute
    33  	longWait        = 10 * time.Minute
    34  	pollingInterval = 5 * time.Second
    35  )
    36  
    37  type FluentdModifier struct {
    38  	Component vzapi.FluentdComponent
    39  }
    40  
    41  type FluentdModifierV1beta1 struct {
    42  	Component v1beta1.FluentdComponent
    43  }
    44  
    45  func (u *FluentdModifierV1beta1) ModifyCRV1beta1(cr *v1beta1.Verrazzano) {
    46  	cr.Spec.Components.Fluentd = &u.Component
    47  }
    48  
    49  func (u *FluentdModifier) ModifyCR(cr *vzapi.Verrazzano) {
    50  	cr.Spec.Components.Fluentd = &u.Component
    51  }
    52  
    53  func ValidateUpdate(m update.CRModifier, expectedError string) {
    54  	gomega.Eventually(func() bool {
    55  		err := update.UpdateCR(m)
    56  		if err != nil {
    57  			pkg.Log(pkg.Info, fmt.Sprintf("Update error: %v", err))
    58  		}
    59  		if expectedError == "" {
    60  			return err == nil
    61  		}
    62  		if err == nil {
    63  			return false
    64  		}
    65  		return strings.Contains(err.Error(), expectedError)
    66  	}).WithPolling(pollingInterval).WithTimeout(longWait).Should(gomega.BeTrue(), fmt.Sprintf("expected error %v", expectedError))
    67  }
    68  
    69  func ValidateUpdateV1beta1(m update.CRModifierV1beta1, expectedError string) {
    70  	gomega.Eventually(func() bool {
    71  		err := update.UpdateCRV1beta1(m)
    72  		if err != nil {
    73  			pkg.Log(pkg.Info, fmt.Sprintf("v1beta1 - Update error: %v", err))
    74  		}
    75  		if expectedError == "" {
    76  			return err == nil
    77  		}
    78  		if err == nil {
    79  			return false
    80  		}
    81  		return strings.Contains(err.Error(), expectedError)
    82  	}).WithPolling(pollingInterval).WithTimeout(longWait).Should(gomega.BeTrue(), fmt.Sprintf("expected error %v", expectedError))
    83  }
    84  
    85  func ValidateDaemonset(osURL, osSec, apiSec string, extra ...vzapi.VolumeMount) bool {
    86  	ds, err := pkg.GetDaemonSet(constants.VerrazzanoSystemNamespace, fluentdName)
    87  	if err != nil {
    88  		return false
    89  	}
    90  	validateCacertsVolume(ds)
    91  	for _, cntr := range ds.Spec.Template.Spec.Containers {
    92  		if !validateFluentdContainer(cntr, osURL, osSec, extra...) {
    93  			return false
    94  		}
    95  	}
    96  	if !validateOciSec(ds, apiSec) {
    97  		return false
    98  	}
    99  	if len(extra) > 0 && !checkExtraVolumes(ds, extra...) {
   100  		return false
   101  	}
   102  	return ds.Status.NumberReady > 0
   103  }
   104  
   105  func ValidateDaemonsetV1beta1(osURL, osSec, apiSec string, extra ...v1beta1.VolumeMount) bool {
   106  	ds, err := pkg.GetDaemonSet(constants.VerrazzanoSystemNamespace, fluentdName)
   107  	if err != nil {
   108  		return false
   109  	}
   110  	validateCacertsVolume(ds)
   111  	for _, cntr := range ds.Spec.Template.Spec.Containers {
   112  		if !validateFluentdContainerV1beta1(cntr, osURL, osSec, extra...) {
   113  			return false
   114  		}
   115  	}
   116  	if !validateOciSec(ds, apiSec) {
   117  		return false
   118  	}
   119  	if len(extra) > 0 && !checkExtraVolumesV1beta1(ds, extra...) {
   120  		return false
   121  	}
   122  	return ds.Status.NumberReady > 0
   123  }
   124  
   125  func checkExtraVolumes(ds *appsv1.DaemonSet, extra ...vzapi.VolumeMount) bool {
   126  	for _, vm := range extra {
   127  		if found := findVol(ds, "", vm.Source); found == nil {
   128  			return false
   129  		}
   130  	}
   131  	return true
   132  }
   133  
   134  func checkExtraVolumesV1beta1(ds *appsv1.DaemonSet, extra ...v1beta1.VolumeMount) bool {
   135  	for _, vm := range extra {
   136  		if found := findVol(ds, "", vm.Source); found == nil {
   137  			return false
   138  		}
   139  	}
   140  	return true
   141  }
   142  
   143  func validateFluentdContainer(cntr corev1.Container, osURL, osSec string, extra ...vzapi.VolumeMount) bool {
   144  	if cntr.Name == fluentdName {
   145  		if !validateCacerts(cntr) {
   146  			return false
   147  		}
   148  		if !validateEsURL(cntr, osURL) {
   149  			return false
   150  		}
   151  		if !validateEsSec(cntr, osSec) {
   152  			return false
   153  		}
   154  		if len(extra) > 0 && !checkExtraMounts(cntr, extra...) {
   155  			return false
   156  		}
   157  	}
   158  	return true
   159  }
   160  
   161  func validateFluentdContainerV1beta1(cntr corev1.Container, osURL, osSec string, extra ...v1beta1.VolumeMount) bool {
   162  	if cntr.Name == fluentdName {
   163  		if !validateCacerts(cntr) {
   164  			return false
   165  		}
   166  		if !validateEsURL(cntr, osURL) {
   167  			return false
   168  		}
   169  		if !validateEsSec(cntr, osSec) {
   170  			return false
   171  		}
   172  		if len(extra) > 0 && !checkExtraMountsV1beta1(cntr, extra...) {
   173  			return false
   174  		}
   175  	}
   176  	return true
   177  }
   178  
   179  func checkExtraMounts(cntr corev1.Container, extra ...vzapi.VolumeMount) bool {
   180  	for _, vm := range extra {
   181  		dest := vm.Destination
   182  		if dest == "" {
   183  			dest = vm.Source
   184  		}
   185  		if found := findVolMount(cntr, "", dest); found == nil {
   186  			return false
   187  		}
   188  	}
   189  	return true
   190  }
   191  
   192  func checkExtraMountsV1beta1(cntr corev1.Container, extra ...v1beta1.VolumeMount) bool {
   193  	for _, vm := range extra {
   194  		dest := vm.Destination
   195  		if dest == "" {
   196  			dest = vm.Source
   197  		}
   198  		if found := findVolMount(cntr, "", dest); found == nil {
   199  			return false
   200  		}
   201  	}
   202  	return true
   203  }
   204  
   205  func validateEsURL(cntr corev1.Container, esURL string) bool {
   206  	esURL = strings.TrimSpace(esURL)
   207  	if esURL != "" {
   208  		env := findEnv(cntr, "ELASTICSEARCH_URL")
   209  		pkg.Log(pkg.Info, fmt.Sprintf("Expecting %v found env: %v", esURL, env))
   210  		if env == nil || env.Value != esURL {
   211  			return false
   212  		}
   213  	}
   214  	return true
   215  }
   216  
   217  func validateEsSec(cntr corev1.Container, esSec string) bool {
   218  	if esSec != "" {
   219  		env := findEnv(cntr, "ELASTICSEARCH_USER")
   220  		pkg.Log(pkg.Info, fmt.Sprintf("Found env: %v", env))
   221  		if env == nil || env.ValueFrom.SecretKeyRef.Name != esSec {
   222  			return false
   223  		}
   224  	}
   225  	return true
   226  }
   227  
   228  func validateCacerts(cntr corev1.Container) bool {
   229  	vm := findVolMount(cntr, cacerts, "")
   230  	pkg.Log(pkg.Info, fmt.Sprintf("Found %s VolumeMount: %v", cacerts, vm))
   231  	return vm != nil && vm.MountPath == "/fluentd/cacerts"
   232  }
   233  
   234  func validateCacertsVolume(ds *appsv1.DaemonSet) bool {
   235  	vol := findVol(ds, cacerts, "")
   236  	pkg.Log(pkg.Info, fmt.Sprintf("Found %s Volume: %v", cacerts, vol))
   237  	return vol != nil
   238  }
   239  
   240  func validateOciSec(ds *appsv1.DaemonSet, apiSec string) bool {
   241  	if apiSec != "" {
   242  		vol := findVol(ds, "oci-secret-volume", "")
   243  		pkg.Log(pkg.Info, fmt.Sprintf("Found oci-secret-volume: %v", vol))
   244  		if vol == nil || vol.VolumeSource.Secret.SecretName != apiSec {
   245  			return false
   246  		}
   247  	}
   248  	return true
   249  }
   250  
   251  func findVol(ds *appsv1.DaemonSet, name, path string) *corev1.Volume {
   252  	for _, vol := range ds.Spec.Template.Spec.Volumes {
   253  		if name != "" && vol.Name == name {
   254  			return &vol
   255  		}
   256  		if path != "" && vol.HostPath != nil && vol.HostPath.Path == path {
   257  			return &vol
   258  		}
   259  	}
   260  	return nil
   261  }
   262  
   263  func findEnv(c corev1.Container, name string) *corev1.EnvVar {
   264  	for _, env := range c.Env {
   265  		if env.Name == name {
   266  			return &env
   267  		}
   268  	}
   269  	return nil
   270  }
   271  
   272  func findVolMount(c corev1.Container, name, path string) *corev1.VolumeMount {
   273  	for _, vm := range c.VolumeMounts {
   274  		if name != "" && vm.Name == name {
   275  			return &vm
   276  		}
   277  		if path != "" && vm.MountPath == path {
   278  			return &vm
   279  		}
   280  	}
   281  	return nil
   282  }
   283  
   284  func createOciLoggingSecret(name string) (*corev1.Secret, error) {
   285  	// Get the kubernetes clientset
   286  	clientset, err := k8sutil.GetKubernetesClientset()
   287  	if err != nil {
   288  		pkg.Log(pkg.Error, fmt.Sprintf("Failed to get clientset with error: %v", err))
   289  		return nil, err
   290  	}
   291  	key, _ := rsa.GenerateKey(rand.Reader, 4096)
   292  	secret := &corev1.Secret{
   293  		ObjectMeta: metav1.ObjectMeta{
   294  			Name:      name,
   295  			Namespace: pcons.VerrazzanoInstallNamespace,
   296  		},
   297  		Data: map[string][]byte{
   298  			"config": []byte(`
   299  [DEFAULT]
   300  user=ocid1.user.oc1..testuser
   301  tenancy=ocid1.tenancy.oc1..testtenancy
   302  region=us-ashburn-1
   303  fingerprint=test:fingerprint
   304  key_file=/root/.oci/key
   305  `),
   306  			// Encode private key to PKCS#1 ASN.1 PEM.
   307  			"key": pem.EncodeToMemory(
   308  				&pem.Block{
   309  					Type:  "RSA PRIVATE KEY",
   310  					Bytes: x509.MarshalPKCS1PrivateKey(key),
   311  				},
   312  			),
   313  		},
   314  	}
   315  	scr, err := clientset.CoreV1().Secrets(pcons.VerrazzanoInstallNamespace).Create(context.TODO(), secret, metav1.CreateOptions{})
   316  	if err != nil {
   317  		pkg.Log(pkg.Error, fmt.Sprintf("CreateOciLoggingSecret %v error: %v", name, err))
   318  	}
   319  	return scr, err
   320  }
   321  
   322  func ValidateConfigMap(sysLog, defLog string) bool {
   323  	cmName := fluentdName + "-config"
   324  	cm, err := pkg.GetConfigMap(cmName, constants.VerrazzanoSystemNamespace)
   325  	pkg.Log(pkg.Info, fmt.Sprintf("Found ConfigMap: %v %v", cmName, err))
   326  	if err != nil {
   327  		return false
   328  	}
   329  	if sysLog != "" {
   330  		entry := "oci-logging-system.conf"
   331  		conf, ok := cm.Data[entry]
   332  		pkg.Log(pkg.Info, fmt.Sprintf("Found sysLog %v in ConfigMap: %v", entry, ok))
   333  		if !ok || !strings.Contains(conf, sysLog) {
   334  			return false
   335  		}
   336  	}
   337  	if defLog != "" {
   338  		entry := "oci-logging-default-app.conf"
   339  		conf, ok := cm.Data[entry]
   340  		pkg.Log(pkg.Info, fmt.Sprintf("Found defLog %v in ConfigMap: %v", entry, ok))
   341  		if !ok || !strings.Contains(conf, defLog) {
   342  			return false
   343  		}
   344  	}
   345  	return true
   346  }