|
| 1 | +package snc |
| 2 | + |
| 3 | +import ( |
| 4 | + "fmt" |
| 5 | + "time" |
| 6 | + |
| 7 | + "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/apiextensions" |
| 8 | + corev1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/core/v1" |
| 9 | + metav1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/meta/v1" |
| 10 | + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" |
| 11 | + "k8s.io/apimachinery/pkg/runtime/schema" |
| 12 | +) |
| 13 | + |
| 14 | +const ( |
| 15 | + istioSystemNamespace = "istio-system" |
| 16 | + istioCNINamespace = "istio-cni" |
| 17 | +) |
| 18 | + |
| 19 | +var ( |
| 20 | + sailCSVGVR = schema.GroupVersionResource{ |
| 21 | + Group: "operators.coreos.com", |
| 22 | + Version: "v1alpha1", |
| 23 | + Resource: "clusterserviceversions", |
| 24 | + } |
| 25 | + istioGVR = schema.GroupVersionResource{ |
| 26 | + Group: "sailoperator.io", |
| 27 | + Version: "v1", |
| 28 | + Resource: "istios", |
| 29 | + } |
| 30 | + istioCNIGVR = schema.GroupVersionResource{ |
| 31 | + Group: "sailoperator.io", |
| 32 | + Version: "v1", |
| 33 | + Resource: "istiocnis", |
| 34 | + } |
| 35 | +) |
| 36 | + |
| 37 | +func deployServiceMesh(ctx *pulumi.Context, args *ProfileDeployArgs) (pulumi.Resource, error) { |
| 38 | + goCtx := ctx.Context() |
| 39 | + rn := func(suffix string) string { |
| 40 | + return fmt.Sprintf("%s-smesh-%s", args.Prefix, suffix) |
| 41 | + } |
| 42 | + |
| 43 | + // Create istio-system namespace |
| 44 | + nsSystem, err := corev1.NewNamespace(ctx, rn("ns-system"), |
| 45 | + &corev1.NamespaceArgs{ |
| 46 | + Metadata: &metav1.ObjectMetaArgs{ |
| 47 | + Name: pulumi.String(istioSystemNamespace), |
| 48 | + }, |
| 49 | + }, |
| 50 | + pulumi.Provider(args.K8sProvider), |
| 51 | + pulumi.DependsOn(args.Deps)) |
| 52 | + if err != nil { |
| 53 | + return nil, err |
| 54 | + } |
| 55 | + |
| 56 | + // Create istio-cni namespace |
| 57 | + nsCNI, err := corev1.NewNamespace(ctx, rn("ns-cni"), |
| 58 | + &corev1.NamespaceArgs{ |
| 59 | + Metadata: &metav1.ObjectMetaArgs{ |
| 60 | + Name: pulumi.String(istioCNINamespace), |
| 61 | + }, |
| 62 | + }, |
| 63 | + pulumi.Provider(args.K8sProvider), |
| 64 | + pulumi.DependsOn(args.Deps)) |
| 65 | + if err != nil { |
| 66 | + return nil, err |
| 67 | + } |
| 68 | + |
| 69 | + // Create Subscription for the OpenShift Service Mesh 3 operator |
| 70 | + sub, err := apiextensions.NewCustomResource(ctx, rn("sub"), |
| 71 | + &apiextensions.CustomResourceArgs{ |
| 72 | + ApiVersion: pulumi.String("operators.coreos.com/v1alpha1"), |
| 73 | + Kind: pulumi.String("Subscription"), |
| 74 | + Metadata: &metav1.ObjectMetaArgs{ |
| 75 | + Name: pulumi.String("servicemeshoperator3"), |
| 76 | + Namespace: pulumi.String("openshift-operators"), |
| 77 | + }, |
| 78 | + OtherFields: map[string]interface{}{ |
| 79 | + "spec": map[string]interface{}{ |
| 80 | + "source": "redhat-operators", |
| 81 | + "sourceNamespace": "openshift-marketplace", |
| 82 | + "name": "servicemeshoperator3", |
| 83 | + "channel": "stable", |
| 84 | + "installPlanApproval": "Automatic", |
| 85 | + }, |
| 86 | + }, |
| 87 | + }, |
| 88 | + pulumi.Provider(args.K8sProvider), |
| 89 | + pulumi.DependsOn([]pulumi.Resource{nsSystem, nsCNI})) |
| 90 | + if err != nil { |
| 91 | + return nil, err |
| 92 | + } |
| 93 | + |
| 94 | + // Wait for the Service Mesh operator CSV to succeed |
| 95 | + csvReady := pulumi.All(sub.ID(), args.Kubeconfig).ApplyT( |
| 96 | + func(allArgs []interface{}) (string, error) { |
| 97 | + kc := allArgs[1].(string) |
| 98 | + if err := waitForCRCondition(goCtx, kc, sailCSVGVR, |
| 99 | + "openshift-operators", "servicemeshoperator3", |
| 100 | + "", "Succeeded", 20*time.Minute, true); err != nil { |
| 101 | + return "", fmt.Errorf("waiting for Service Mesh operator CSV: %w", err) |
| 102 | + } |
| 103 | + return "ready", nil |
| 104 | + }).(pulumi.StringOutput) |
| 105 | + |
| 106 | + // Create IstioCNI CR |
| 107 | + istioCNIName := csvReady.ApplyT(func(_ string) string { |
| 108 | + return "default" |
| 109 | + }).(pulumi.StringOutput) |
| 110 | + |
| 111 | + // IstioCNI is cluster-scoped |
| 112 | + cni, err := apiextensions.NewCustomResource(ctx, rn("istiocni"), |
| 113 | + &apiextensions.CustomResourceArgs{ |
| 114 | + ApiVersion: pulumi.String("sailoperator.io/v1"), |
| 115 | + Kind: pulumi.String("IstioCNI"), |
| 116 | + Metadata: &metav1.ObjectMetaArgs{ |
| 117 | + Name: istioCNIName, |
| 118 | + }, |
| 119 | + OtherFields: map[string]interface{}{ |
| 120 | + "spec": map[string]interface{}{ |
| 121 | + "namespace": istioCNINamespace, |
| 122 | + "profile": "openshift", |
| 123 | + }, |
| 124 | + }, |
| 125 | + }, |
| 126 | + pulumi.Provider(args.K8sProvider)) |
| 127 | + if err != nil { |
| 128 | + return nil, err |
| 129 | + } |
| 130 | + |
| 131 | + // Wait for IstioCNI to be ready (cluster-scoped, empty namespace) |
| 132 | + cniReady := pulumi.All(cni.ID(), args.Kubeconfig).ApplyT( |
| 133 | + func(allArgs []interface{}) (string, error) { |
| 134 | + kc := allArgs[1].(string) |
| 135 | + if err := waitForCRCondition(goCtx, kc, istioCNIGVR, |
| 136 | + "", "default", |
| 137 | + "Ready", "True", 20*time.Minute, false); err != nil { |
| 138 | + return "", fmt.Errorf("waiting for IstioCNI: %w", err) |
| 139 | + } |
| 140 | + return "ready", nil |
| 141 | + }).(pulumi.StringOutput) |
| 142 | + |
| 143 | + // Create Istio CR (cluster-scoped, depends on CNI being ready) |
| 144 | + istioName := cniReady.ApplyT(func(_ string) string { |
| 145 | + return "default" |
| 146 | + }).(pulumi.StringOutput) |
| 147 | + |
| 148 | + istio, err := apiextensions.NewCustomResource(ctx, rn("istio"), |
| 149 | + &apiextensions.CustomResourceArgs{ |
| 150 | + ApiVersion: pulumi.String("sailoperator.io/v1"), |
| 151 | + Kind: pulumi.String("Istio"), |
| 152 | + Metadata: &metav1.ObjectMetaArgs{ |
| 153 | + Name: istioName, |
| 154 | + }, |
| 155 | + OtherFields: map[string]interface{}{ |
| 156 | + "spec": map[string]interface{}{ |
| 157 | + "namespace": istioSystemNamespace, |
| 158 | + }, |
| 159 | + }, |
| 160 | + }, |
| 161 | + pulumi.Provider(args.K8sProvider)) |
| 162 | + if err != nil { |
| 163 | + return nil, err |
| 164 | + } |
| 165 | + |
| 166 | + // Wait for Istio to be ready (cluster-scoped, empty namespace) |
| 167 | + istioReady := pulumi.All(istio.ID(), args.Kubeconfig).ApplyT( |
| 168 | + func(allArgs []interface{}) (string, error) { |
| 169 | + kc := allArgs[1].(string) |
| 170 | + if err := waitForCRCondition(goCtx, kc, istioGVR, |
| 171 | + "", "default", |
| 172 | + "Ready", "True", 20*time.Minute, false); err != nil { |
| 173 | + return "", fmt.Errorf("waiting for Istio: %w", err) |
| 174 | + } |
| 175 | + return "ready", nil |
| 176 | + }).(pulumi.StringOutput) |
| 177 | + |
| 178 | + ctx.Export("istioReady", istioReady) |
| 179 | + |
| 180 | + return istio, nil |
| 181 | +} |
0 commit comments