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 }