Compare commits
10 commits
d0b95d9d34
...
da3c4a3e4c
| Author | SHA1 | Date | |
|---|---|---|---|
| da3c4a3e4c | |||
| ce801f30d0 | |||
| ce2fb4208d | |||
| 6a91b556a5 | |||
| e04370a376 | |||
| b958acd94e | |||
| fdb931acc2 | |||
| 97517cacea | |||
| b712ae0869 | |||
| 20e3afdf0d |
18 changed files with 240 additions and 132 deletions
1
.envrc.example
Normal file
1
.envrc.example
Normal file
|
|
@ -0,0 +1 @@
|
|||
use flake
|
||||
23
.github/workflows/test.yaml
vendored
Normal file
23
.github/workflows/test.yaml
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
name: test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '*'
|
||||
tags-ignore:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: ">=1.25.1"
|
||||
- name: Test code
|
||||
run: make test
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -1,3 +1,7 @@
|
|||
edge-connect
|
||||
# Added by goreleaser init:
|
||||
dist/
|
||||
|
||||
### direnv ###
|
||||
.direnv
|
||||
.envrc
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ var (
|
|||
cloudletOrg string
|
||||
instanceName string
|
||||
flavorName string
|
||||
appId string
|
||||
)
|
||||
|
||||
var appInstanceCmd = &cobra.Command{
|
||||
|
|
@ -71,8 +72,9 @@ var showInstanceCmd = &cobra.Command{
|
|||
Name: cloudletName,
|
||||
},
|
||||
}
|
||||
appkey := edgeconnect.AppKey{Name: appId}
|
||||
|
||||
instance, err := c.ShowAppInstance(context.Background(), instanceKey, region)
|
||||
instance, err := c.ShowAppInstance(context.Background(), instanceKey, appkey, region)
|
||||
if err != nil {
|
||||
fmt.Printf("Error showing app instance: %v\n", err)
|
||||
os.Exit(1)
|
||||
|
|
@ -142,6 +144,7 @@ func init() {
|
|||
cmd.Flags().StringVarP(&cloudletName, "cloudlet", "c", "", "cloudlet name (required)")
|
||||
cmd.Flags().StringVarP(&cloudletOrg, "cloudlet-org", "", "", "cloudlet organization (required)")
|
||||
cmd.Flags().StringVarP(®ion, "region", "r", "", "region (required)")
|
||||
cmd.Flags().StringVarP(&appId, "app-id", "i", "", "application id")
|
||||
|
||||
cmd.MarkFlagRequired("org")
|
||||
cmd.MarkFlagRequired("name")
|
||||
|
|
|
|||
25
flake.lock
generated
Normal file
25
flake.lock
generated
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1759733170,
|
||||
"narHash": "sha256-TXnlsVb5Z8HXZ6mZoeOAIwxmvGHp1g4Dw89eLvIwKVI=",
|
||||
"rev": "8913c168d1c56dc49a7718685968f38752171c3b",
|
||||
"revCount": 873256,
|
||||
"type": "tarball",
|
||||
"url": "https://api.flakehub.com/f/pinned/NixOS/nixpkgs/0.1.873256%2Brev-8913c168d1c56dc49a7718685968f38752171c3b/0199bd36-8ae7-7817-b019-8688eb4f61ff/source.tar.gz"
|
||||
},
|
||||
"original": {
|
||||
"type": "tarball",
|
||||
"url": "https://flakehub.com/f/NixOS/nixpkgs/0.1"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
38
flake.nix
Normal file
38
flake.nix
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
description = "A Nix-flake-based Go development environment";
|
||||
|
||||
inputs.nixpkgs.url = "https://flakehub.com/f/NixOS/nixpkgs/0.1";
|
||||
|
||||
outputs = inputs:
|
||||
let
|
||||
goVersion = 25; # Change this to update the whole stack
|
||||
|
||||
supportedSystems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ];
|
||||
forEachSupportedSystem = f: inputs.nixpkgs.lib.genAttrs supportedSystems (system: f {
|
||||
pkgs = import inputs.nixpkgs {
|
||||
inherit system;
|
||||
overlays = [ inputs.self.overlays.default ];
|
||||
};
|
||||
});
|
||||
in
|
||||
{
|
||||
overlays.default = final: prev: {
|
||||
go = final."go_1_${toString goVersion}";
|
||||
};
|
||||
|
||||
devShells = forEachSupportedSystem ({ pkgs }: {
|
||||
default = pkgs.mkShell {
|
||||
packages = with pkgs; [
|
||||
# go (version is specified by overlay)
|
||||
go
|
||||
|
||||
# goimports, godoc, etc.
|
||||
gotools
|
||||
|
||||
# https://github.com/golangci/golangci-lint
|
||||
golangci-lint
|
||||
];
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
@ -265,7 +265,7 @@ func (rm *EdgeConnectResourceManager) rollbackInstance(ctx context.Context, acti
|
|||
for _, instanceAction := range plan.InstanceActions {
|
||||
if instanceAction.InstanceName == action.Target {
|
||||
instanceKey := edgeconnect.AppInstanceKey{
|
||||
Organization: instanceAction.Target.Organization,
|
||||
Organization: plan.AppAction.Desired.Organization,
|
||||
Name: instanceAction.InstanceName,
|
||||
CloudletKey: edgeconnect.CloudletKey{
|
||||
Organization: instanceAction.Target.CloudletOrg,
|
||||
|
|
|
|||
|
|
@ -110,7 +110,6 @@ func createTestDeploymentPlan() *DeploymentPlan {
|
|||
{
|
||||
Type: ActionCreate,
|
||||
Target: config.InfraTemplate{
|
||||
Organization: "testorg",
|
||||
Region: "US",
|
||||
CloudletOrg: "cloudletorg",
|
||||
CloudletName: "cloudlet1",
|
||||
|
|
@ -137,16 +136,16 @@ func createTestManagerConfig(t *testing.T) *config.EdgeConnectConfig {
|
|||
return &config.EdgeConnectConfig{
|
||||
Kind: "edgeconnect-deployment",
|
||||
Metadata: config.Metadata{
|
||||
Name: "test-app",
|
||||
Name: "test-app",
|
||||
AppVersion: "1.0.0",
|
||||
Organization: "testorg",
|
||||
},
|
||||
Spec: config.Spec{
|
||||
K8sApp: &config.K8sApp{
|
||||
AppVersion: "1.0.0",
|
||||
ManifestFile: manifestFile,
|
||||
},
|
||||
InfraTemplate: []config.InfraTemplate{
|
||||
{
|
||||
Organization: "testorg",
|
||||
Region: "US",
|
||||
CloudletOrg: "cloudletorg",
|
||||
CloudletName: "cloudlet1",
|
||||
|
|
@ -309,7 +308,6 @@ func TestApplyDeploymentMultipleInstances(t *testing.T) {
|
|||
{
|
||||
Type: ActionCreate,
|
||||
Target: config.InfraTemplate{
|
||||
Organization: "testorg",
|
||||
Region: "US",
|
||||
CloudletOrg: "cloudletorg1",
|
||||
CloudletName: "cloudlet1",
|
||||
|
|
@ -321,7 +319,6 @@ func TestApplyDeploymentMultipleInstances(t *testing.T) {
|
|||
{
|
||||
Type: ActionCreate,
|
||||
Target: config.InfraTemplate{
|
||||
Organization: "testorg",
|
||||
Region: "EU",
|
||||
CloudletOrg: "cloudletorg2",
|
||||
CloudletName: "cloudlet2",
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ type EdgeConnectClientInterface interface {
|
|||
CreateApp(ctx context.Context, input *edgeconnect.NewAppInput) error
|
||||
UpdateApp(ctx context.Context, input *edgeconnect.UpdateAppInput) error
|
||||
DeleteApp(ctx context.Context, appKey edgeconnect.AppKey, region string) error
|
||||
ShowAppInstance(ctx context.Context, instanceKey edgeconnect.AppInstanceKey, region string) (edgeconnect.AppInstance, error)
|
||||
ShowAppInstance(ctx context.Context, instanceKey edgeconnect.AppInstanceKey, appKey edgeconnect.AppKey, region string) (edgeconnect.AppInstance, error)
|
||||
CreateAppInstance(ctx context.Context, input *edgeconnect.NewAppInstanceInput) error
|
||||
UpdateAppInstance(ctx context.Context, input *edgeconnect.UpdateAppInstanceInput) error
|
||||
DeleteAppInstance(ctx context.Context, instanceKey edgeconnect.AppInstanceKey, region string) error
|
||||
|
|
@ -134,10 +134,10 @@ func (p *EdgeConnectPlanner) planAppAction(ctx context.Context, config *config.E
|
|||
// Build desired app state
|
||||
desired := &AppState{
|
||||
Name: config.Metadata.Name,
|
||||
Version: config.Spec.GetAppVersion(),
|
||||
Organization: config.Spec.InfraTemplate[0].Organization, // Use first infra template for org
|
||||
Region: config.Spec.InfraTemplate[0].Region, // Use first infra template for region
|
||||
Exists: false, // Will be set based on current state
|
||||
Version: config.Metadata.AppVersion,
|
||||
Organization: config.Metadata.Organization, // Use first infra template for org
|
||||
Region: config.Spec.InfraTemplate[0].Region, // Use first infra template for region
|
||||
Exists: false, // Will be set based on current state
|
||||
}
|
||||
|
||||
if config.Spec.IsK8sApp() {
|
||||
|
|
@ -204,6 +204,7 @@ func (p *EdgeConnectPlanner) planAppAction(ctx context.Context, config *config.E
|
|||
action.Type = ActionUpdate
|
||||
action.Changes = changes
|
||||
action.Reason = "Application configuration has changed"
|
||||
fmt.Printf("Changes: %v\n", changes)
|
||||
|
||||
if manifestChanged {
|
||||
warnings = append(warnings, "Manifest file has changed - instances may need to be recreated")
|
||||
|
|
@ -219,12 +220,12 @@ func (p *EdgeConnectPlanner) planInstanceActions(ctx context.Context, config *co
|
|||
var warnings []string
|
||||
|
||||
for _, infra := range config.Spec.InfraTemplate {
|
||||
instanceName := getInstanceName(config.Metadata.Name, config.Spec.GetAppVersion())
|
||||
instanceName := getInstanceName(config.Metadata.Name, config.Metadata.AppVersion)
|
||||
|
||||
desired := &InstanceState{
|
||||
Name: instanceName,
|
||||
AppVersion: config.Spec.GetAppVersion(),
|
||||
Organization: infra.Organization,
|
||||
AppVersion: config.Metadata.AppVersion,
|
||||
Organization: config.Metadata.Organization,
|
||||
Region: infra.Region,
|
||||
CloudletOrg: infra.CloudletOrg,
|
||||
CloudletName: infra.CloudletName,
|
||||
|
|
@ -346,8 +347,11 @@ func (p *EdgeConnectPlanner) getCurrentInstanceState(ctx context.Context, desire
|
|||
Name: desired.CloudletName,
|
||||
},
|
||||
}
|
||||
appKey := edgeconnect.AppKey{
|
||||
Name: desired.AppName,
|
||||
}
|
||||
|
||||
instance, err := p.client.ShowAppInstance(timeoutCtx, instanceKey, desired.Region)
|
||||
instance, err := p.client.ShowAppInstance(timeoutCtx, instanceKey, appKey, desired.Region)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -391,8 +395,13 @@ func (p *EdgeConnectPlanner) compareAppStates(current, desired *AppState) ([]str
|
|||
// Compare outbound connections
|
||||
outboundChanges := p.compareOutboundConnections(current.OutboundConnections, desired.OutboundConnections)
|
||||
if len(outboundChanges) > 0 {
|
||||
changes = append(changes, "Outbound connections changed:")
|
||||
changes = append(changes, outboundChanges...)
|
||||
sb := strings.Builder{}
|
||||
sb.WriteString("Outbound connections changed:\n")
|
||||
for _, change := range outboundChanges {
|
||||
sb.WriteString(change)
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
changes = append(changes, sb.String())
|
||||
}
|
||||
|
||||
return changes, manifestChanged
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ func (m *MockEdgeConnectClient) ShowApp(ctx context.Context, appKey edgeconnect.
|
|||
return args.Get(0).(edgeconnect.App), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockEdgeConnectClient) ShowAppInstance(ctx context.Context, instanceKey edgeconnect.AppInstanceKey, region string) (edgeconnect.AppInstance, error) {
|
||||
func (m *MockEdgeConnectClient) ShowAppInstance(ctx context.Context, instanceKey edgeconnect.AppInstanceKey, appKey edgeconnect.AppKey, region string) (edgeconnect.AppInstance, error) {
|
||||
args := m.Called(ctx, instanceKey, region)
|
||||
if args.Get(0) == nil {
|
||||
return edgeconnect.AppInstance{}, args.Error(1)
|
||||
|
|
@ -75,14 +75,6 @@ func (m *MockEdgeConnectClient) ShowApps(ctx context.Context, appKey edgeconnect
|
|||
return args.Get(0).([]edgeconnect.App), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockEdgeConnectClient) ShowAppInstances(ctx context.Context, instanceKey edgeconnect.AppInstanceKey, region string) ([]edgeconnect.AppInstance, error) {
|
||||
args := m.Called(ctx, instanceKey, region)
|
||||
if args.Get(0) == nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
return args.Get(0).([]edgeconnect.AppInstance), args.Error(1)
|
||||
}
|
||||
|
||||
func TestNewPlanner(t *testing.T) {
|
||||
mockClient := &MockEdgeConnectClient{}
|
||||
planner := NewPlanner(mockClient)
|
||||
|
|
@ -112,16 +104,16 @@ func createTestConfig(t *testing.T) *config.EdgeConnectConfig {
|
|||
return &config.EdgeConnectConfig{
|
||||
Kind: "edgeconnect-deployment",
|
||||
Metadata: config.Metadata{
|
||||
Name: "test-app",
|
||||
Name: "test-app",
|
||||
AppVersion: "1.0.0",
|
||||
Organization: "testorg",
|
||||
},
|
||||
Spec: config.Spec{
|
||||
K8sApp: &config.K8sApp{
|
||||
AppVersion: "1.0.0",
|
||||
ManifestFile: manifestFile,
|
||||
},
|
||||
InfraTemplate: []config.InfraTemplate{
|
||||
{
|
||||
Organization: "testorg",
|
||||
Region: "US",
|
||||
CloudletOrg: "TestCloudletOrg",
|
||||
CloudletName: "TestCloudlet",
|
||||
|
|
@ -185,13 +177,15 @@ func TestPlanExistingDeploymentNoChanges(t *testing.T) {
|
|||
// Note: We would calculate expected manifest hash here when API supports it
|
||||
|
||||
// Mock existing app with same manifest hash and outbound connections
|
||||
manifestContent := "apiVersion: v1\nkind: Pod\nmetadata:\n name: test\n"
|
||||
existingApp := &edgeconnect.App{
|
||||
Key: edgeconnect.AppKey{
|
||||
Organization: "testorg",
|
||||
Name: "test-app",
|
||||
Version: "1.0.0",
|
||||
},
|
||||
Deployment: "kubernetes",
|
||||
Deployment: "kubernetes",
|
||||
DeploymentManifest: manifestContent,
|
||||
RequiredOutboundConnections: []edgeconnect.SecurityRule{
|
||||
{
|
||||
Protocol: "tcp",
|
||||
|
|
@ -284,7 +278,6 @@ func TestPlanMultipleInfrastructures(t *testing.T) {
|
|||
|
||||
// Add a second infrastructure target
|
||||
testConfig.Spec.InfraTemplate = append(testConfig.Spec.InfraTemplate, config.InfraTemplate{
|
||||
Organization: "testorg",
|
||||
Region: "EU",
|
||||
CloudletOrg: "EUCloudletOrg",
|
||||
CloudletName: "EUCloudlet",
|
||||
|
|
|
|||
|
|
@ -408,7 +408,7 @@ func (r *RecreateStrategy) executeAppActionWithRetry(ctx context.Context, action
|
|||
// deleteInstance deletes an instance (reuse existing logic from manager.go)
|
||||
func (r *RecreateStrategy) deleteInstance(ctx context.Context, action InstanceAction) (bool, error) {
|
||||
instanceKey := edgeconnect.AppInstanceKey{
|
||||
Organization: action.Target.Organization,
|
||||
Organization: action.Desired.Organization,
|
||||
Name: action.InstanceName,
|
||||
CloudletKey: edgeconnect.CloudletKey{
|
||||
Organization: action.Target.CloudletOrg,
|
||||
|
|
@ -430,7 +430,7 @@ func (r *RecreateStrategy) createInstance(ctx context.Context, action InstanceAc
|
|||
Region: action.Target.Region,
|
||||
AppInst: edgeconnect.AppInstance{
|
||||
Key: edgeconnect.AppInstanceKey{
|
||||
Organization: action.Target.Organization,
|
||||
Organization: action.Desired.Organization,
|
||||
Name: action.InstanceName,
|
||||
CloudletKey: edgeconnect.CloudletKey{
|
||||
Organization: action.Target.CloudletOrg,
|
||||
|
|
@ -438,9 +438,9 @@ func (r *RecreateStrategy) createInstance(ctx context.Context, action InstanceAc
|
|||
},
|
||||
},
|
||||
AppKey: edgeconnect.AppKey{
|
||||
Organization: action.Target.Organization,
|
||||
Organization: action.Desired.Organization,
|
||||
Name: config.Metadata.Name,
|
||||
Version: config.Spec.GetAppVersion(),
|
||||
Version: config.Metadata.AppVersion,
|
||||
},
|
||||
Flavor: edgeconnect.Flavor{
|
||||
Name: action.Target.FlavorName,
|
||||
|
|
|
|||
|
|
@ -28,14 +28,13 @@ func TestParseExampleConfig(t *testing.T) {
|
|||
|
||||
// Check k8s app configuration
|
||||
require.NotNil(t, config.Spec.K8sApp)
|
||||
assert.Equal(t, "1.0.0", config.Spec.K8sApp.AppVersion)
|
||||
assert.Equal(t, "1.0.0", config.Metadata.AppVersion)
|
||||
// Note: ManifestFile path should be resolved to absolute path
|
||||
assert.Contains(t, config.Spec.K8sApp.ManifestFile, "k8s-deployment.yaml")
|
||||
|
||||
// Check infrastructure template
|
||||
require.Len(t, config.Spec.InfraTemplate, 1)
|
||||
infra := config.Spec.InfraTemplate[0]
|
||||
assert.Equal(t, "edp2", infra.Organization)
|
||||
assert.Equal(t, "EU", infra.Region)
|
||||
assert.Equal(t, "TelekomOP", infra.CloudletOrg)
|
||||
assert.Equal(t, "Munich", infra.CloudletName)
|
||||
|
|
@ -59,7 +58,6 @@ func TestParseExampleConfig(t *testing.T) {
|
|||
|
||||
// Test utility methods
|
||||
assert.Equal(t, "edge-app-demo", config.Metadata.Name)
|
||||
assert.Equal(t, "1.0.0", config.Spec.GetAppVersion())
|
||||
assert.Contains(t, config.Spec.GetManifestFile(), "k8s-deployment.yaml")
|
||||
assert.True(t, config.Spec.IsK8sApp())
|
||||
assert.False(t, config.Spec.IsDockerApp())
|
||||
|
|
@ -73,15 +71,15 @@ func TestValidateExampleStructure(t *testing.T) {
|
|||
Kind: "edgeconnect-deployment",
|
||||
Metadata: Metadata{
|
||||
Name: "edge-app-demo",
|
||||
AppVersion: "1.0.0",
|
||||
Organization: "edp2",
|
||||
},
|
||||
Spec: Spec{
|
||||
DockerApp: &DockerApp{ // Use DockerApp to avoid manifest file validation
|
||||
AppVersion: "1.0.0",
|
||||
Image: "nginx:latest",
|
||||
},
|
||||
InfraTemplate: []InfraTemplate{
|
||||
{
|
||||
Organization: "edp2",
|
||||
Region: "EU",
|
||||
CloudletOrg: "TelekomOP",
|
||||
CloudletName: "Munich",
|
||||
|
|
|
|||
|
|
@ -32,13 +32,13 @@ func TestConfigParser_ParseBytes(t *testing.T) {
|
|||
kind: edgeconnect-deployment
|
||||
metadata:
|
||||
name: "test-app"
|
||||
appVersion: "1.0.0"
|
||||
organization: "testorg"
|
||||
spec:
|
||||
k8sApp:
|
||||
appVersion: "1.0.0"
|
||||
manifestFile: "./test-manifest.yaml"
|
||||
infraTemplate:
|
||||
- organization: "testorg"
|
||||
region: "US"
|
||||
- region: "US"
|
||||
cloudletOrg: "TestOP"
|
||||
cloudletName: "TestCloudlet"
|
||||
flavorName: "small"
|
||||
|
|
@ -52,13 +52,13 @@ spec:
|
|||
kind: edgeconnect-deployment
|
||||
metadata:
|
||||
name: "test-app"
|
||||
appVersion: "1.0.0"
|
||||
organization: "testorg"
|
||||
spec:
|
||||
dockerApp:
|
||||
appVersion: "1.0.0"
|
||||
image: "nginx:latest"
|
||||
infraTemplate:
|
||||
- organization: "testorg"
|
||||
region: "US"
|
||||
- region: "US"
|
||||
cloudletOrg: "TestOP"
|
||||
cloudletName: "TestCloudlet"
|
||||
flavorName: "small"
|
||||
|
|
@ -70,13 +70,13 @@ spec:
|
|||
yaml: `
|
||||
metadata:
|
||||
name: "test-app"
|
||||
appVersion: "1.0.0"
|
||||
organization: "testorg"
|
||||
spec:
|
||||
k8sApp:
|
||||
appVersion: "1.0.0"
|
||||
manifestFile: "./test-manifest.yaml"
|
||||
infraTemplate:
|
||||
- organization: "testorg"
|
||||
region: "US"
|
||||
- region: "US"
|
||||
cloudletOrg: "TestOP"
|
||||
cloudletName: "TestCloudlet"
|
||||
flavorName: "small"
|
||||
|
|
@ -90,13 +90,13 @@ spec:
|
|||
kind: invalid-kind
|
||||
metadata:
|
||||
name: "test-app"
|
||||
appVersion: "1.0.0"
|
||||
organization: "testorg"
|
||||
spec:
|
||||
dockerApp:
|
||||
appVersion: "1.0.0"
|
||||
image: "nginx:latest"
|
||||
infraTemplate:
|
||||
- organization: "testorg"
|
||||
region: "US"
|
||||
- region: "US"
|
||||
cloudletOrg: "TestOP"
|
||||
cloudletName: "TestCloudlet"
|
||||
flavorName: "small"
|
||||
|
|
@ -110,10 +110,11 @@ spec:
|
|||
kind: edgeconnect-deployment
|
||||
metadata:
|
||||
name: "test-app"
|
||||
appVersion: "1.0.0"
|
||||
organization: "testorg"
|
||||
spec:
|
||||
infraTemplate:
|
||||
- organization: "testorg"
|
||||
region: "US"
|
||||
- region: "US"
|
||||
cloudletOrg: "TestOP"
|
||||
cloudletName: "TestCloudlet"
|
||||
flavorName: "small"
|
||||
|
|
@ -127,17 +128,16 @@ spec:
|
|||
kind: edgeconnect-deployment
|
||||
metadata:
|
||||
name: "test-app"
|
||||
appVersion: "1.0.0"
|
||||
organization: "testorg"
|
||||
spec:
|
||||
k8sApp:
|
||||
appVersion: "1.0.0"
|
||||
manifestFile: "./test-manifest.yaml"
|
||||
dockerApp:
|
||||
appName: "test-app"
|
||||
appVersion: "1.0.0"
|
||||
image: "nginx:latest"
|
||||
infraTemplate:
|
||||
- organization: "testorg"
|
||||
region: "US"
|
||||
- region: "US"
|
||||
cloudletOrg: "TestOP"
|
||||
cloudletName: "TestCloudlet"
|
||||
flavorName: "small"
|
||||
|
|
@ -151,9 +151,10 @@ spec:
|
|||
kind: edgeconnect-deployment
|
||||
metadata:
|
||||
name: "test-app"
|
||||
appVersion: "1.0.0"
|
||||
organization: "testorg"
|
||||
spec:
|
||||
dockerApp:
|
||||
appVersion: "1.0.0"
|
||||
image: "nginx:latest"
|
||||
infraTemplate: []
|
||||
`,
|
||||
|
|
@ -166,13 +167,13 @@ spec:
|
|||
kind: edgeconnect-deployment
|
||||
metadata:
|
||||
name: "test-app"
|
||||
appVersion: "1.0.0"
|
||||
organization: "testorg"
|
||||
spec:
|
||||
dockerApp:
|
||||
appVersion: "1.0.0"
|
||||
image: "nginx:latest"
|
||||
infraTemplate:
|
||||
- organization: "testorg"
|
||||
region: "US"
|
||||
- region: "US"
|
||||
cloudletOrg: "TestOP"
|
||||
cloudletName: "TestCloudlet"
|
||||
flavorName: "small"
|
||||
|
|
@ -222,13 +223,13 @@ func TestConfigParser_ParseFile(t *testing.T) {
|
|||
kind: edgeconnect-deployment
|
||||
metadata:
|
||||
name: "test-app"
|
||||
appVersion: "1.0.0"
|
||||
organization: "testorg"
|
||||
spec:
|
||||
dockerApp:
|
||||
appVersion: "1.0.0"
|
||||
image: "nginx:latest"
|
||||
infraTemplate:
|
||||
- organization: "testorg"
|
||||
region: "US"
|
||||
- region: "US"
|
||||
cloudletOrg: "TestOP"
|
||||
cloudletName: "TestCloudlet"
|
||||
flavorName: "small"
|
||||
|
|
@ -284,13 +285,13 @@ func TestConfigParser_RelativePathResolution(t *testing.T) {
|
|||
kind: edgeconnect-deployment
|
||||
metadata:
|
||||
name: "test-app"
|
||||
appVersion: "1.0.0"
|
||||
organization: "testorg"
|
||||
spec:
|
||||
k8sApp:
|
||||
appVersion: "1.0.0"
|
||||
manifestFile: "./manifest.yaml"
|
||||
infraTemplate:
|
||||
- organization: "testorg"
|
||||
region: "US"
|
||||
- region: "US"
|
||||
cloudletOrg: "TestOP"
|
||||
cloudletName: "TestCloudlet"
|
||||
flavorName: "small"
|
||||
|
|
@ -321,16 +322,16 @@ func TestEdgeConnectConfig_Validate(t *testing.T) {
|
|||
config: EdgeConnectConfig{
|
||||
Kind: "edgeconnect-deployment",
|
||||
Metadata: Metadata{
|
||||
Name: "test-app",
|
||||
Name: "test-app",
|
||||
AppVersion: "1.0.0",
|
||||
Organization: "testorg",
|
||||
},
|
||||
Spec: Spec{
|
||||
DockerApp: &DockerApp{
|
||||
AppVersion: "1.0.0",
|
||||
Image: "nginx:latest",
|
||||
Image: "nginx:latest",
|
||||
},
|
||||
InfraTemplate: []InfraTemplate{
|
||||
{
|
||||
Organization: "testorg",
|
||||
Region: "US",
|
||||
CloudletOrg: "TestOP",
|
||||
CloudletName: "TestCloudlet",
|
||||
|
|
@ -385,24 +386,60 @@ func TestMetadata_Validate(t *testing.T) {
|
|||
}{
|
||||
{
|
||||
name: "valid metadata",
|
||||
metadata: Metadata{Name: "test-app"},
|
||||
metadata: Metadata{Name: "test-app", AppVersion: "1.0.0", Organization: "testorg"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "empty name",
|
||||
metadata: Metadata{Name: ""},
|
||||
metadata: Metadata{Name: "", AppVersion: "1.0.0", Organization: "testorg"},
|
||||
wantErr: true,
|
||||
errMsg: "metadata.name is required",
|
||||
},
|
||||
{
|
||||
name: "name with leading whitespace",
|
||||
metadata: Metadata{Name: " test-app"},
|
||||
metadata: Metadata{Name: " test-app", AppVersion: "1.0.0", Organization: "testorg"},
|
||||
wantErr: true,
|
||||
errMsg: "cannot have leading/trailing whitespace",
|
||||
},
|
||||
{
|
||||
name: "name with trailing whitespace",
|
||||
metadata: Metadata{Name: "test-app "},
|
||||
metadata: Metadata{Name: "test-app ", AppVersion: "1.0.0", Organization: "testorg"},
|
||||
wantErr: true,
|
||||
errMsg: "cannot have leading/trailing whitespace",
|
||||
},
|
||||
{
|
||||
name: "empty app version",
|
||||
metadata: Metadata{Name: "test-app", AppVersion: "", Organization: "testorg"},
|
||||
wantErr: true,
|
||||
errMsg: "metadata.appVersion is required",
|
||||
},
|
||||
{
|
||||
name: "app version with leading whitespace",
|
||||
metadata: Metadata{Name: "test-app", AppVersion: " 1.0.0", Organization: "testorg"},
|
||||
wantErr: true,
|
||||
errMsg: "cannot have leading/trailing whitespace",
|
||||
},
|
||||
{
|
||||
name: "app version with trailing whitespace",
|
||||
metadata: Metadata{Name: "test-app", AppVersion: "1.0.0 ", Organization: "testorg"},
|
||||
wantErr: true,
|
||||
errMsg: "cannot have leading/trailing whitespace",
|
||||
},
|
||||
{
|
||||
name: "empty organization",
|
||||
metadata: Metadata{Name: "test-app", AppVersion: "1.0.0", Organization: ""},
|
||||
wantErr: true,
|
||||
errMsg: "metadata.organization is required",
|
||||
},
|
||||
{
|
||||
name: "organization with leading whitespace",
|
||||
metadata: Metadata{Name: "test-app", AppVersion: "1.0.0", Organization: " testorg"},
|
||||
wantErr: true,
|
||||
errMsg: "cannot have leading/trailing whitespace",
|
||||
},
|
||||
{
|
||||
name: "organization with trailing whitespace",
|
||||
metadata: Metadata{Name: "test-app", AppVersion: "1.0.0", Organization: "testorg "},
|
||||
wantErr: true,
|
||||
errMsg: "cannot have leading/trailing whitespace",
|
||||
},
|
||||
|
|
@ -526,24 +563,20 @@ func TestOutboundConnection_Validate(t *testing.T) {
|
|||
func TestSpec_GetMethods(t *testing.T) {
|
||||
k8sSpec := &Spec{
|
||||
K8sApp: &K8sApp{
|
||||
AppVersion: "1.0.0",
|
||||
ManifestFile: "k8s.yaml",
|
||||
},
|
||||
}
|
||||
|
||||
dockerSpec := &Spec{
|
||||
DockerApp: &DockerApp{
|
||||
AppVersion: "2.0.0",
|
||||
ManifestFile: "docker.yaml",
|
||||
},
|
||||
}
|
||||
|
||||
assert.Equal(t, "1.0.0", k8sSpec.GetAppVersion())
|
||||
assert.Equal(t, "k8s.yaml", k8sSpec.GetManifestFile())
|
||||
assert.True(t, k8sSpec.IsK8sApp())
|
||||
assert.False(t, k8sSpec.IsDockerApp())
|
||||
|
||||
assert.Equal(t, "2.0.0", dockerSpec.GetAppVersion())
|
||||
assert.Equal(t, "docker.yaml", dockerSpec.GetManifestFile())
|
||||
assert.False(t, dockerSpec.IsK8sApp())
|
||||
assert.True(t, dockerSpec.IsDockerApp())
|
||||
|
|
@ -564,13 +597,13 @@ func TestReadManifestFile(t *testing.T) {
|
|||
kind: edgeconnect-deployment
|
||||
metadata:
|
||||
name: "test-app"
|
||||
appVersion: "1.0.0"
|
||||
organization: "testorg"
|
||||
spec:
|
||||
k8sApp:
|
||||
appVersion: "1.0.0"
|
||||
manifestFile: "./manifest.yaml"
|
||||
infraTemplate:
|
||||
- organization: "testorg"
|
||||
region: "US"
|
||||
- region: "US"
|
||||
cloudletOrg: "TestOP"
|
||||
cloudletName: "TestCloudlet"
|
||||
flavorName: "small"
|
||||
|
|
|
|||
|
|
@ -18,7 +18,9 @@ type EdgeConnectConfig struct {
|
|||
|
||||
// Metadata contains configuration metadata
|
||||
type Metadata struct {
|
||||
Name string `yaml:"name"`
|
||||
Name string `yaml:"name"`
|
||||
AppVersion string `yaml:"appVersion"`
|
||||
Organization string `yaml:"organization"`
|
||||
}
|
||||
|
||||
// Spec defines the application and infrastructure specification
|
||||
|
|
@ -32,20 +34,17 @@ type Spec struct {
|
|||
|
||||
// K8sApp defines Kubernetes application configuration
|
||||
type K8sApp struct {
|
||||
AppVersion string `yaml:"appVersion"`
|
||||
ManifestFile string `yaml:"manifestFile"`
|
||||
}
|
||||
|
||||
// DockerApp defines Docker application configuration
|
||||
type DockerApp struct {
|
||||
AppVersion string `yaml:"appVersion"`
|
||||
ManifestFile string `yaml:"manifestFile"`
|
||||
Image string `yaml:"image"`
|
||||
}
|
||||
|
||||
// InfraTemplate defines infrastructure deployment targets
|
||||
type InfraTemplate struct {
|
||||
Organization string `yaml:"organization"`
|
||||
Region string `yaml:"region"`
|
||||
CloudletOrg string `yaml:"cloudletOrg"`
|
||||
CloudletName string `yaml:"cloudletName"`
|
||||
|
|
@ -113,6 +112,22 @@ func (m *Metadata) Validate() error {
|
|||
return fmt.Errorf("metadata.name cannot have leading/trailing whitespace")
|
||||
}
|
||||
|
||||
if m.AppVersion == "" {
|
||||
return fmt.Errorf("metadata.appVersion is required")
|
||||
}
|
||||
|
||||
if strings.TrimSpace(m.AppVersion) != m.AppVersion {
|
||||
return fmt.Errorf("metadata.appVersion cannot have leading/trailing whitespace")
|
||||
}
|
||||
|
||||
if m.Organization == "" {
|
||||
return fmt.Errorf("metadata.organization is required")
|
||||
}
|
||||
|
||||
if strings.TrimSpace(m.Organization) != m.Organization {
|
||||
return fmt.Errorf("metadata.Organization cannot have leading/trailing whitespace")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -171,10 +186,6 @@ func (s *Spec) Validate() error {
|
|||
|
||||
// Validate validates k8s app configuration
|
||||
func (k *K8sApp) Validate() error {
|
||||
if k.AppVersion == "" {
|
||||
return fmt.Errorf("appVersion is required")
|
||||
}
|
||||
|
||||
if k.ManifestFile == "" {
|
||||
return fmt.Errorf("manifestFile is required")
|
||||
}
|
||||
|
|
@ -184,29 +195,15 @@ func (k *K8sApp) Validate() error {
|
|||
return fmt.Errorf("manifestFile does not exist: %s", k.ManifestFile)
|
||||
}
|
||||
|
||||
// Validate version format
|
||||
if strings.TrimSpace(k.AppVersion) != k.AppVersion {
|
||||
return fmt.Errorf("appVersion cannot have leading/trailing whitespace")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate validates docker app configuration
|
||||
func (d *DockerApp) Validate() error {
|
||||
if d.AppVersion == "" {
|
||||
return fmt.Errorf("appVersion is required")
|
||||
}
|
||||
|
||||
if d.Image == "" {
|
||||
return fmt.Errorf("image is required")
|
||||
}
|
||||
|
||||
// Validate version format
|
||||
if strings.TrimSpace(d.AppVersion) != d.AppVersion {
|
||||
return fmt.Errorf("appVersion cannot have leading/trailing whitespace")
|
||||
}
|
||||
|
||||
// Check if manifest file exists if specified
|
||||
if d.ManifestFile != "" {
|
||||
if _, err := os.Stat(d.ManifestFile); os.IsNotExist(err) {
|
||||
|
|
@ -219,10 +216,6 @@ func (d *DockerApp) Validate() error {
|
|||
|
||||
// Validate validates infrastructure template configuration
|
||||
func (i *InfraTemplate) Validate() error {
|
||||
if i.Organization == "" {
|
||||
return fmt.Errorf("organization is required")
|
||||
}
|
||||
|
||||
if i.Region == "" {
|
||||
return fmt.Errorf("region is required")
|
||||
}
|
||||
|
|
@ -241,7 +234,6 @@ func (i *InfraTemplate) Validate() error {
|
|||
|
||||
// Validate no leading/trailing whitespace
|
||||
fields := map[string]string{
|
||||
"organization": i.Organization,
|
||||
"region": i.Region,
|
||||
"cloudletOrg": i.CloudletOrg,
|
||||
"cloudletName": i.CloudletName,
|
||||
|
|
@ -326,17 +318,6 @@ func (d *DockerApp) GetManifestPath(configDir string) string {
|
|||
return filepath.Join(configDir, d.ManifestFile)
|
||||
}
|
||||
|
||||
// GetAppVersion returns the application version from the active app type
|
||||
func (s *Spec) GetAppVersion() string {
|
||||
if s.K8sApp != nil {
|
||||
return s.K8sApp.AppVersion
|
||||
}
|
||||
if s.DockerApp != nil {
|
||||
return s.DockerApp.AppVersion
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetManifestFile returns the manifest file path from the active app type
|
||||
func (s *Spec) GetManifestFile() string {
|
||||
if s.K8sApp != nil {
|
||||
|
|
|
|||
|
|
@ -36,12 +36,12 @@ func (c *Client) CreateAppInstance(ctx context.Context, input *NewAppInstanceInp
|
|||
|
||||
// ShowAppInstance retrieves a single application instance by key and region
|
||||
// Maps to POST /auth/ctrl/ShowAppInst
|
||||
func (c *Client) ShowAppInstance(ctx context.Context, appInstKey AppInstanceKey, region string) (AppInstance, error) {
|
||||
func (c *Client) ShowAppInstance(ctx context.Context, appInstKey AppInstanceKey, appKey AppKey, region string) (AppInstance, error) {
|
||||
transport := c.getTransport()
|
||||
url := c.BaseURL + "/api/v1/auth/ctrl/ShowAppInst"
|
||||
|
||||
filter := AppInstanceFilter{
|
||||
AppInstance: AppInstance{Key: appInstKey},
|
||||
AppInstance: AppInstance{AppKey: appKey, Key: appInstKey},
|
||||
Region: region,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -101,6 +101,7 @@ func TestCreateAppInstance(t *testing.T) {
|
|||
func TestShowAppInstance(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
appKey AppKey
|
||||
appInstKey AppInstanceKey
|
||||
region string
|
||||
mockStatusCode int
|
||||
|
|
@ -118,6 +119,7 @@ func TestShowAppInstance(t *testing.T) {
|
|||
Name: "testcloudlet",
|
||||
},
|
||||
},
|
||||
appKey: AppKey{Name: "test-app-id"},
|
||||
region: "us-west",
|
||||
mockStatusCode: 200,
|
||||
mockResponse: `{"data": {"key": {"organization": "testorg", "name": "testinst", "cloudlet_key": {"organization": "cloudletorg", "name": "testcloudlet"}}, "state": "Ready"}}
|
||||
|
|
@ -135,6 +137,7 @@ func TestShowAppInstance(t *testing.T) {
|
|||
Name: "testcloudlet",
|
||||
},
|
||||
},
|
||||
appKey: AppKey{Name: "test-app-id"},
|
||||
region: "us-west",
|
||||
mockStatusCode: 404,
|
||||
mockResponse: "",
|
||||
|
|
@ -164,7 +167,7 @@ func TestShowAppInstance(t *testing.T) {
|
|||
|
||||
// Execute test
|
||||
ctx := context.Background()
|
||||
appInst, err := client.ShowAppInstance(ctx, tt.appInstKey, tt.region)
|
||||
appInst, err := client.ShowAppInstance(ctx, tt.appInstKey, tt.appKey, tt.region)
|
||||
|
||||
// Verify results
|
||||
if tt.expectError {
|
||||
|
|
|
|||
|
|
@ -3,17 +3,17 @@
|
|||
kind: edgeconnect-deployment
|
||||
metadata:
|
||||
name: "edge-app-demo" # name could be used for appName
|
||||
appVersion: "1.0.0"
|
||||
organization: "edp2"
|
||||
spec:
|
||||
# dockerApp: # Docker is OBSOLETE
|
||||
# appVersion: "1.0.0"
|
||||
# manifestFile: "./docker-compose.yaml"
|
||||
# image: "https://registry-1.docker.io/library/nginx:latest"
|
||||
k8sApp:
|
||||
appVersion: "1.0.0"
|
||||
manifestFile: "./k8s-deployment.yaml"
|
||||
infraTemplate:
|
||||
- organization: "edp2"
|
||||
region: "EU"
|
||||
- region: "EU"
|
||||
cloudletOrg: "TelekomOP"
|
||||
cloudletName: "Munich"
|
||||
flavorName: "EU.small"
|
||||
|
|
|
|||
|
|
@ -193,7 +193,7 @@ func runComprehensiveWorkflow(ctx context.Context, c *edgeconnect.Client, config
|
|||
},
|
||||
}
|
||||
|
||||
instanceDetails, err := waitForInstanceReady(ctx, c, instanceKey, config.Region, 5*time.Minute)
|
||||
instanceDetails, err := waitForInstanceReady(ctx, c, instanceKey, edgeconnect.AppKey{}, config.Region, 5*time.Minute)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to wait for instance ready: %w", err)
|
||||
}
|
||||
|
|
@ -306,7 +306,7 @@ func getEnvOrDefault(key, defaultValue string) string {
|
|||
}
|
||||
|
||||
// waitForInstanceReady polls the instance status until it's no longer "Creating" or timeout
|
||||
func waitForInstanceReady(ctx context.Context, c *edgeconnect.Client, instanceKey edgeconnect.AppInstanceKey, region string, timeout time.Duration) (edgeconnect.AppInstance, error) {
|
||||
func waitForInstanceReady(ctx context.Context, c *edgeconnect.Client, instanceKey edgeconnect.AppInstanceKey, appKey edgeconnect.AppKey, region string, timeout time.Duration) (edgeconnect.AppInstance, error) {
|
||||
timeoutCtx, cancel := context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
|
||||
|
|
@ -321,7 +321,7 @@ func waitForInstanceReady(ctx context.Context, c *edgeconnect.Client, instanceKe
|
|||
return edgeconnect.AppInstance{}, fmt.Errorf("timeout waiting for instance to be ready after %v", timeout)
|
||||
|
||||
case <-ticker.C:
|
||||
instance, err := c.ShowAppInstance(timeoutCtx, instanceKey, region)
|
||||
instance, err := c.ShowAppInstance(timeoutCtx, instanceKey, appKey, region)
|
||||
if err != nil {
|
||||
// Log error but continue polling
|
||||
fmt.Printf(" ⚠️ Error checking instance state: %v\n", err)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue