github.com/verrazzano/verrazzano@v1.7.1/tests/e2e/quickcreate/context.go (about) 1 // Copyright (c) 2023, 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 quickcreate 5 6 import ( 7 "context" 8 _ "embed" 9 "encoding/base64" 10 "errors" 11 "fmt" 12 "github.com/verrazzano/verrazzano/cluster-operator/controllers/quickcreate/controller/ocne" 13 "github.com/verrazzano/verrazzano/pkg/k8sutil" 14 "github.com/verrazzano/verrazzano/pkg/semver" 15 "github.com/verrazzano/verrazzano/tests/e2e/pkg" 16 corev1 "k8s.io/api/core/v1" 17 apierrors "k8s.io/apimachinery/pkg/api/errors" 18 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 19 "k8s.io/apimachinery/pkg/types" 20 "os" 21 "sigs.k8s.io/cluster-api/api/v1beta1" 22 clipkg "sigs.k8s.io/controller-runtime/pkg/client" 23 "sigs.k8s.io/yaml" 24 "strings" 25 ) 26 27 const ( 28 Ocneoci = "ocneoci" 29 Oke = "oke" 30 Namespace = "NAMESPACE" 31 VZFleetName = "VZFLEET_NAME" 32 ) 33 34 var ( 35 //go:embed templates/ocneoci.goyaml 36 ocneociTemplate []byte 37 //go:embed templates/oke.goyaml 38 okeTemplate []byte 39 //go:embed templates/ociclusteridentity.goyaml 40 ociClusterIdentity []byte 41 42 //go:embed templates/verrazzanofleet-mc-profile.goyaml 43 verrazzanoFleet []byte 44 45 clusterTemplateMap = map[string][]byte{ 46 Ocneoci: ocneociTemplate, 47 Oke: okeTemplate, 48 } 49 50 clusterName string 51 clusterNamespace string 52 vzFleetName string 53 ) 54 55 type ( 56 QCContext struct { 57 ClusterType string 58 Namespace string 59 Client clipkg.Client 60 RawObjects []byte 61 Parameters input 62 } 63 input map[string]interface{} 64 ) 65 66 func newContext(cli clipkg.Client, clusterType string) (*QCContext, error) { 67 qc := &QCContext{ 68 ClusterType: clusterType, 69 Namespace: pkg.SimpleNameGenerator.New("qc-"), 70 Client: cli, 71 } 72 if err := qc.setDynamicValues(); err != nil { 73 return nil, err 74 } 75 76 return qc, nil 77 } 78 79 func (qc *QCContext) setDynamicValues() error { 80 rawObjects, parameters, err := qc.getInputValues() 81 if err != nil { 82 return fmt.Errorf("failed to load template: %v", err) 83 } 84 qc.Parameters = parameters 85 qc.RawObjects = rawObjects 86 qc.Parameters[Namespace] = qc.Namespace 87 if qc.isOCICluster() { 88 err = qc.Parameters.prepareOCI(qc.ClusterType) 89 if err != nil { 90 return err 91 } 92 } 93 return nil 94 } 95 96 func (qc *QCContext) setup() error { 97 if err := qc.Client.Create(context.Background(), qc.namespaceObject()); err != nil { 98 return err 99 } 100 if qc.isOCICluster() { 101 if err := qc.applyOCIClusterIdentity(); err != nil { 102 return err 103 } 104 } 105 return nil 106 } 107 108 func (qc *QCContext) applyOCIClusterIdentity() error { 109 return k8sutil.NewYAMLApplier(qc.Client, "").ApplyBT(ociClusterIdentity, qc.Parameters) 110 } 111 112 func (qc *QCContext) applyVerrazzanoFleet() error { 113 return k8sutil.NewYAMLApplier(qc.Client, "").ApplyBT(verrazzanoFleet, qc.Parameters) 114 } 115 116 func (qc *QCContext) applyCluster() error { 117 return k8sutil.NewYAMLApplier(qc.Client, "").ApplyBT(qc.RawObjects, qc.Parameters) 118 } 119 120 func (qc *QCContext) getInputValues() ([]byte, input, error) { 121 params, err := qc.newParameters() 122 clusterName = params[ClusterID].(string) 123 clusterNamespace = qc.Namespace 124 vzFleetName = params[VZFleetName].(string) 125 if err != nil { 126 return nil, nil, err 127 } 128 b, ok := clusterTemplateMap[qc.ClusterType] 129 if !ok { 130 return nil, nil, fmt.Errorf("invalid cluster type: %s", qc.ClusterType) 131 } 132 return b, params, nil 133 } 134 135 func (qc *QCContext) newParameters() (input, error) { 136 var i input = map[string]interface{}{ 137 ClusterID: pkg.SimpleNameGenerator.New("qc-"), 138 VZFleetName: pkg.SimpleNameGenerator.New("vzfleet-"), 139 } 140 if err := i.addFileContents(); err != nil { 141 return nil, err 142 } 143 if err := i.addLatestOCNEVersion(qc.Client, qc.ClusterType); err != nil { 144 return nil, err 145 } 146 return i, nil 147 } 148 149 func (qc *QCContext) deleteObject(o clipkg.Object) error { 150 err := qc.Client.Get(context.Background(), types.NamespacedName{ 151 Namespace: o.GetNamespace(), 152 Name: o.GetName(), 153 }, o) 154 if apierrors.IsNotFound(err) { 155 return nil 156 } 157 if err != nil { 158 return err 159 } 160 if !o.GetDeletionTimestamp().IsZero() { 161 return errors.New("object is being deleted") 162 } 163 if err := qc.Client.Delete(context.Background(), o); err != nil { 164 return err 165 } 166 return errors.New("deleting object") 167 } 168 169 func (qc *QCContext) cleanupCAPICluster() error { 170 name, ok := qc.Parameters[ClusterID].(string) 171 if !ok { 172 return nil 173 } 174 return qc.deleteObject(&v1beta1.Cluster{ 175 ObjectMeta: metav1.ObjectMeta{ 176 Name: name, 177 Namespace: qc.Namespace, 178 }, 179 }) 180 } 181 182 func (qc *QCContext) namespaceObject() clipkg.Object { 183 return &corev1.Namespace{ 184 ObjectMeta: metav1.ObjectMeta{ 185 Name: qc.Namespace, 186 }, 187 } 188 } 189 190 func (qc *QCContext) isClusterReady() error { 191 cluster := &v1beta1.Cluster{} 192 if err := qc.Client.Get(context.TODO(), types.NamespacedName{ 193 Namespace: qc.Namespace, 194 Name: qc.Parameters[ClusterID].(string), 195 }, cluster); err != nil { 196 return err 197 } 198 if !cluster.Status.InfrastructureReady || !cluster.Status.ControlPlaneReady || cluster.Status.Phase != string(v1beta1.ClusterPhaseProvisioned) { 199 return dumpClusterError(cluster) 200 } 201 return nil 202 } 203 204 func (i input) addLatestOCNEVersion(client clipkg.Client, clusterType string) error { 205 if clusterType != Ocneoci { 206 return nil 207 } 208 const ( 209 ocneConfigMapName = "ocne-metadata" 210 ocneConfigMapNamespace = "verrazzano-capi" 211 ) 212 cm := &corev1.ConfigMap{} 213 if err := client.Get(context.Background(), types.NamespacedName{ 214 Namespace: ocneConfigMapNamespace, 215 Name: ocneConfigMapName, 216 }, cm); err != nil { 217 return err 218 } 219 mapping, ok := cm.Data["mapping"] 220 if !ok { 221 return errors.New("no OCNE version mapping") 222 } 223 versions := map[string]*ocne.VersionDefaults{} 224 if err := yaml.Unmarshal([]byte(mapping), &versions); err != nil { 225 return err 226 } 227 var v1 *semver.SemVersion 228 var ocneVersion string 229 for k8sVersion, defaults := range versions { 230 v2, err := semver.NewSemVersion(k8sVersion) 231 if err != nil { 232 return err 233 } 234 if v1 == nil || v2.IsGreaterThanOrEqualTo(v1) { 235 v1 = v2 236 ocneVersion = defaults.Release 237 } 238 } 239 i[OcneVersion] = ocneVersion 240 return nil 241 } 242 243 func (i input) addFileContents() error { 244 files := []string{ 245 PubKey, 246 APIKey, 247 } 248 for _, filevar := range files { 249 path := os.Getenv(filevar) 250 if len(path) < 1 { 251 return fmt.Errorf("%s env var empty", filevar) 252 } 253 b, err := os.ReadFile(path) 254 if err != nil { 255 return err 256 } 257 i[filevar] = string(b) 258 if filevar == APIKey { 259 i.b64EncodeKV(APIKey, B64Key) 260 } 261 } 262 return nil 263 } 264 265 func (i input) b64EncodeKV(key, encodedKey string) { 266 i[encodedKey] = base64.StdEncoding.EncodeToString([]byte(i[key].(string))) 267 } 268 269 func (i input) addEnvValue(key string) error { 270 value := os.Getenv(key) 271 if len(value) < 1 { 272 return fmt.Errorf("no value found for environment key %s", key) 273 } 274 i[key] = value 275 return nil 276 } 277 278 func dumpClusterError(cluster *v1beta1.Cluster) error { 279 sb := strings.Builder{} 280 if cluster.Status.FailureMessage != nil { 281 sb.WriteString(fmt.Sprintf("message: %s", *cluster.Status.FailureMessage)) 282 } 283 if !cluster.Status.ControlPlaneReady { 284 sb.WriteString("- control plane is not ready") 285 } 286 if !cluster.Status.InfrastructureReady { 287 sb.WriteString("- infrastructure is not ready") 288 } 289 for _, cond := range cluster.Status.Conditions { 290 if cond.Status != corev1.ConditionTrue { 291 sb.WriteString(fmt.Sprintf("- condition[%s]:", cond.Type)) 292 sb.WriteString(fmt.Sprintf(" reason: %s", cond.Reason)) 293 sb.WriteString(fmt.Sprintf(" message: %s", cond.Message)) 294 } 295 } 296 return errors.New(sb.String()) 297 }