From 728b27146e64bdbc496f1d6abd6ddb24de29751d Mon Sep 17 00:00:00 2001 From: Manuel Ganter Date: Tue, 11 Nov 2025 16:12:54 +0100 Subject: [PATCH] updated tf strcutrure --- .../edgeconnect-config/k8s-deployment.yaml | 34 +++ examples/edgeconnect-config/main.tf | 47 ++++ internal/provider/app_resource.go | 217 ++++++++++++++---- 3 files changed, 253 insertions(+), 45 deletions(-) create mode 100644 examples/edgeconnect-config/k8s-deployment.yaml create mode 100644 examples/edgeconnect-config/main.tf diff --git a/examples/edgeconnect-config/k8s-deployment.yaml b/examples/edgeconnect-config/k8s-deployment.yaml new file mode 100644 index 0000000..5747ee5 --- /dev/null +++ b/examples/edgeconnect-config/k8s-deployment.yaml @@ -0,0 +1,34 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx-deployment + labels: + app: nginx +spec: + replicas: 3 + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + app: nginx + spec: + containers: + - name: nginx + image: nginx:latest + ports: + - containerPort: 80 +--- +apiVersion: v1 +kind: Service +metadata: + name: nginx-service +spec: + selector: + app: nginx + ports: + - protocol: TCP + port: 80 + targetPort: 80 + type: LoadBalancer diff --git a/examples/edgeconnect-config/main.tf b/examples/edgeconnect-config/main.tf new file mode 100644 index 0000000..fb6e114 --- /dev/null +++ b/examples/edgeconnect-config/main.tf @@ -0,0 +1,47 @@ +terraform { + required_providers { + edge-connect = { + source = "local/edge-connect" + } + } +} + +provider "edge-connect" { + endpoint = "https://hub.apps.edge.platform.mg3.mdb.osc.live" +} + +resource "edge-connect_app" "edge_app_demo" { + name = "edge-app-demo" + app_version = "1" + organization = "edp2-orca" + + manifest = file("${path.module}/k8s-deployment.yaml") + + infra_template { + region = "US" + cloudlet_org = "TelekomOp" + cloudlet_name = "gardener-shepherd-test" + flavor_name = "default" + + network { + outbound_connections { + protocol = "tcp" + port_range_min = 80 + port_range_max = 80 + remote_cidr = "0.0.0.0/0" + } + + outbound_connections { + protocol = "tcp" + port_range_min = 443 + port_range_max = 443 + remote_cidr = "0.0.0.0/0" + } + } + } +} + +output "app_id" { + description = "ID of the EdgeConnect app" + value = edge-connect_app.edge_app_demo.id +} diff --git a/internal/provider/app_resource.go b/internal/provider/app_resource.go index ac94630..eeed10f 100644 --- a/internal/provider/app_resource.go +++ b/internal/provider/app_resource.go @@ -27,11 +27,31 @@ type AppResource struct { } type AppResourceModel struct { - Id types.String `tfsdk:"id"` - Name types.String `tfsdk:"name"` - Description types.String `tfsdk:"description"` - Version types.String `tfsdk:"version"` - Status types.String `tfsdk:"status"` + Id types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + AppVersion types.String `tfsdk:"app_version"` + Organization types.String `tfsdk:"organization"` + Manifest types.String `tfsdk:"manifest"` + InfraTemplate []InfraTemplateModel `tfsdk:"infra_template"` +} + +type InfraTemplateModel struct { + Region types.String `tfsdk:"region"` + CloudletOrg types.String `tfsdk:"cloudlet_org"` + CloudletName types.String `tfsdk:"cloudlet_name"` + FlavorName types.String `tfsdk:"flavor_name"` + Network *NetworkModel `tfsdk:"network"` +} + +type NetworkModel struct { + OutboundConnections []OutboundConnectionModel `tfsdk:"outbound_connections"` +} + +type OutboundConnectionModel struct { + Protocol types.String `tfsdk:"protocol"` + PortRangeMin types.Int64 `tfsdk:"port_range_min"` + PortRangeMax types.Int64 `tfsdk:"port_range_max"` + RemoteCIDR types.String `tfsdk:"remote_cidr"` } func (r *AppResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { @@ -40,7 +60,7 @@ func (r *AppResource) Metadata(ctx context.Context, req resource.MetadataRequest func (r *AppResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ - MarkdownDescription: "App resource", + MarkdownDescription: "EdgeConnect App deployment resource", Attributes: map[string]schema.Attribute{ "id": schema.StringAttribute{ @@ -54,17 +74,71 @@ func (r *AppResource) Schema(ctx context.Context, req resource.SchemaRequest, re MarkdownDescription: "App name", Required: true, }, - "description": schema.StringAttribute{ - MarkdownDescription: "App description", - Optional: true, - }, - "version": schema.StringAttribute{ + "app_version": schema.StringAttribute{ MarkdownDescription: "App version", - Optional: true, + Required: true, }, - "status": schema.StringAttribute{ - MarkdownDescription: "App status", - Computed: true, + "organization": schema.StringAttribute{ + MarkdownDescription: "Organization name", + Required: true, + }, + "manifest": schema.StringAttribute{ + MarkdownDescription: "Kubernetes manifest YAML content", + Required: true, + }, + "infra_template": schema.ListNestedAttribute{ + MarkdownDescription: "Infrastructure template configurations", + Required: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "region": schema.StringAttribute{ + MarkdownDescription: "Region (e.g., US, EU)", + Required: true, + }, + "cloudlet_org": schema.StringAttribute{ + MarkdownDescription: "Cloudlet organization", + Required: true, + }, + "cloudlet_name": schema.StringAttribute{ + MarkdownDescription: "Cloudlet name", + Required: true, + }, + "flavor_name": schema.StringAttribute{ + MarkdownDescription: "Flavor name", + Required: true, + }, + "network": schema.SingleNestedAttribute{ + MarkdownDescription: "Network configuration", + Optional: true, + Attributes: map[string]schema.Attribute{ + "outbound_connections": schema.ListNestedAttribute{ + MarkdownDescription: "Outbound connection rules", + Optional: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "protocol": schema.StringAttribute{ + MarkdownDescription: "Protocol (tcp, udp, icmp)", + Required: true, + }, + "port_range_min": schema.Int64Attribute{ + MarkdownDescription: "Minimum port number", + Required: true, + }, + "port_range_max": schema.Int64Attribute{ + MarkdownDescription: "Maximum port number", + Required: true, + }, + "remote_cidr": schema.StringAttribute{ + MarkdownDescription: "Remote CIDR (e.g., 0.0.0.0/0)", + Required: true, + }, + }, + }, + }, + }, + }, + }, + }, }, }, } @@ -98,17 +172,39 @@ func (r *AppResource) Create(ctx context.Context, req resource.CreateRequest, re return } + // Build outbound connections from all infra templates + var outboundConnections []edgeclient.SecurityRule + for _, infraTemplate := range data.InfraTemplate { + if infraTemplate.Network != nil && len(infraTemplate.Network.OutboundConnections) > 0 { + for _, conn := range infraTemplate.Network.OutboundConnections { + outboundConnections = append(outboundConnections, edgeclient.SecurityRule{ + Protocol: conn.Protocol.ValueString(), + PortRangeMin: int(conn.PortRangeMin.ValueInt64()), + PortRangeMax: int(conn.PortRangeMax.ValueInt64()), + RemoteCIDR: conn.RemoteCIDR.ValueString(), + }) + } + } + } + + // Use the first infra template's region as the deployment region + region := "default" + if len(data.InfraTemplate) > 0 { + region = data.InfraTemplate[0].Region.ValueString() + } + appInput := &edgeclient.NewAppInput{ - Region: "default", + Region: region, App: edgeclient.App{ Key: edgeclient.AppKey{ - Organization: "default", + Organization: data.Organization.ValueString(), Name: data.Name.ValueString(), - Version: data.Version.ValueString(), + Version: data.AppVersion.ValueString(), }, - Deployment: "kubernetes", - ImageType: "docker", - ImagePath: "nginx:latest", + Deployment: "kubernetes", + ImageType: "docker", + DeploymentManifest: data.Manifest.ValueString(), + RequiredOutboundConnections: outboundConnections, }, } @@ -119,10 +215,6 @@ func (r *AppResource) Create(ctx context.Context, req resource.CreateRequest, re } data.Id = types.StringValue(appInput.App.Key.Name) - data.Name = types.StringValue(appInput.App.Key.Name) - data.Description = types.StringValue("") - data.Version = types.StringValue(appInput.App.Key.Version) - data.Status = types.StringValue("created") tflog.Trace(ctx, "created an app resource") @@ -139,21 +231,33 @@ func (r *AppResource) Read(ctx context.Context, req resource.ReadRequest, resp * } appKey := edgeclient.AppKey{ - Organization: "default", + Organization: data.Organization.ValueString(), Name: data.Id.ValueString(), - Version: data.Version.ValueString(), + Version: data.AppVersion.ValueString(), } - app, err := r.client.ShowApp(ctx, appKey, "default") + // Use the first infra template's region for the query + region := "default" + if len(data.InfraTemplate) > 0 { + region = data.InfraTemplate[0].Region.ValueString() + } + + app, err := r.client.ShowApp(ctx, appKey, region) if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read app %s, got error: %s", data.Id.ValueString(), err)) return } + // Update state from API response data.Name = types.StringValue(app.Key.Name) - data.Description = types.StringValue("") - data.Version = types.StringValue(app.Key.Version) - data.Status = types.StringValue("") + data.AppVersion = types.StringValue(app.Key.Version) + data.Organization = types.StringValue(app.Key.Organization) + data.Manifest = types.StringValue(app.DeploymentManifest) + + // Reconstruct infra templates from outbound connections if available + // Note: The API returns RequiredOutboundConnections but not the full infra template details + // We preserve the existing infra template from state since the API doesn't return it + // The outbound connections can be compared if needed resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) } @@ -167,17 +271,39 @@ func (r *AppResource) Update(ctx context.Context, req resource.UpdateRequest, re return } + // Build outbound connections from all infra templates + var outboundConnections []edgeclient.SecurityRule + for _, infraTemplate := range data.InfraTemplate { + if infraTemplate.Network != nil && len(infraTemplate.Network.OutboundConnections) > 0 { + for _, conn := range infraTemplate.Network.OutboundConnections { + outboundConnections = append(outboundConnections, edgeclient.SecurityRule{ + Protocol: conn.Protocol.ValueString(), + PortRangeMin: int(conn.PortRangeMin.ValueInt64()), + PortRangeMax: int(conn.PortRangeMax.ValueInt64()), + RemoteCIDR: conn.RemoteCIDR.ValueString(), + }) + } + } + } + + // Use the first infra template's region as the deployment region + region := "default" + if len(data.InfraTemplate) > 0 { + region = data.InfraTemplate[0].Region.ValueString() + } + updateInput := &edgeclient.UpdateAppInput{ - Region: "default", + Region: region, App: edgeclient.App{ Key: edgeclient.AppKey{ - Organization: "default", + Organization: data.Organization.ValueString(), Name: data.Name.ValueString(), - Version: data.Version.ValueString(), + Version: data.AppVersion.ValueString(), }, - Deployment: "kubernetes", - ImageType: "docker", - ImagePath: "nginx:latest", + Deployment: "kubernetes", + ImageType: "docker", + DeploymentManifest: data.Manifest.ValueString(), + RequiredOutboundConnections: outboundConnections, }, } @@ -187,11 +313,6 @@ func (r *AppResource) Update(ctx context.Context, req resource.UpdateRequest, re return } - data.Name = types.StringValue(updateInput.App.Key.Name) - data.Description = types.StringValue("") - data.Version = types.StringValue(updateInput.App.Key.Version) - data.Status = types.StringValue("updated") - resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) } @@ -205,12 +326,18 @@ func (r *AppResource) Delete(ctx context.Context, req resource.DeleteRequest, re } appKey := edgeclient.AppKey{ - Organization: "default", + Organization: data.Organization.ValueString(), Name: data.Id.ValueString(), - Version: data.Version.ValueString(), + Version: data.AppVersion.ValueString(), } - err := r.client.DeleteApp(ctx, appKey, "default") + // Use the first infra template's region for deletion + region := "default" + if len(data.InfraTemplate) > 0 { + region = data.InfraTemplate[0].Region.ValueString() + } + + err := r.client.DeleteApp(ctx, appKey, region) if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to delete app, got error: %s", err)) return