diff --git a/api/agent.go b/api/agent.go index 5f4111a8..d086813e 100644 --- a/api/agent.go +++ b/api/agent.go @@ -29,6 +29,7 @@ type DockerAgentDeployment struct { AgentDeployment ComposeFile []byte `json:"composeFile"` EnvFile []byte `json:"envFile"` + types.DockerType } type KubernetesAgentResource struct { diff --git a/cmd/agent/docker/agent_deployment.go b/cmd/agent/docker/agent_deployment.go index 1e8054df..ed4c26cb 100644 --- a/cmd/agent/docker/agent_deployment.go +++ b/cmd/agent/docker/agent_deployment.go @@ -8,13 +8,15 @@ import ( "path" "github.com/glasskube/distr/api" + "github.com/glasskube/distr/internal/types" "github.com/google/uuid" ) type AgentDeployment struct { - ID uuid.UUID `json:"id"` - RevisionID uuid.UUID `json:"revisionId"` - ProjectName string `json:"projectName"` + ID uuid.UUID `json:"id"` + RevisionID uuid.UUID `json:"revisionId"` + ProjectName string `json:"projectName"` + DockerType types.DockerType `json:"docker_type"` } func (d *AgentDeployment) FileName() string { @@ -29,7 +31,7 @@ func NewAgentDeployment(deployment api.DockerAgentDeployment) (*AgentDeployment, if name, err := getProjectName(deployment.ComposeFile); err != nil { return nil, err } else { - return &AgentDeployment{ID: deployment.ID, RevisionID: deployment.RevisionID, ProjectName: name}, nil + return &AgentDeployment{ID: deployment.ID, RevisionID: deployment.RevisionID, ProjectName: name, DockerType: deployment.DockerType}, nil } } @@ -42,8 +44,7 @@ func getProjectName(data []byte) (string, error) { return name, nil } } - -func GetExistingDeployments() ([]AgentDeployment, error) { +func GetExistingDeployments() (map[uuid.UUID]AgentDeployment, error) { if entries, err := os.ReadDir(agentDeploymentDir()); err != nil { if errors.Is(err, os.ErrNotExist) { return nil, nil @@ -62,13 +63,13 @@ func GetExistingDeployments() ([]AgentDeployment, error) { return &d, nil } } - result := make([]AgentDeployment, 0, len(entries)) + result := make(map[uuid.UUID]AgentDeployment, len(entries)) for _, entry := range entries { if !entry.IsDir() { if d, err := fn(entry.Name()); err != nil { return nil, err } else { - result = append(result, *d) + result[d.RevisionID] = *d } } } diff --git a/cmd/agent/docker/docker_actions.go b/cmd/agent/docker/docker_actions.go index 58fb3c41..123b3514 100644 --- a/cmd/agent/docker/docker_actions.go +++ b/cmd/agent/docker/docker_actions.go @@ -1,19 +1,68 @@ package main import ( + "bufio" "bytes" "context" "errors" "fmt" "os" "os/exec" + "strings" "github.com/glasskube/distr/api" "github.com/glasskube/distr/internal/agentauth" + "github.com/glasskube/distr/internal/types" "go.uber.org/zap" + "gopkg.in/yaml.v3" ) -func ApplyComposeFile(ctx context.Context, deployment api.DockerAgentDeployment) (*AgentDeployment, string, error) { +func DockerEngineApply( + ctx context.Context, + deployment api.DockerAgentDeployment, +) (*AgentDeployment, string, error) { + + if deployment.DockerType == types.DockerTypeSwarm { + + fmt.Println(deployment.RevisionID) + + // Step 1 Ensure Docker Swarm is initialized + initCmd := exec.CommandContext(ctx, "docker", "info", "--format", "{{.Swarm.LocalNodeState}}") + initOutput, err := initCmd.CombinedOutput() + if err != nil { + logger.Error("Failed to check Docker Swarm state", zap.Error(err)) + return nil, "", fmt.Errorf("failed to check Docker Swarm state: %w", err) + } + + if !strings.Contains(strings.TrimSpace(string(initOutput)), "active") { + logger.Error("Docker Swarm not initialized", zap.String("output", string(initOutput))) + return nil, "", fmt.Errorf("docker Swarm not initialized: %s", string(initOutput)) + } + // Step 2: Pull images before deployment + _, err = PullSwarmMode(ctx, deployment) + if err != nil { + logger.Error("Failed to Pull", zap.Error(err)) + return nil, "", err + } + return ApplyComposeFileSwarm(ctx, deployment) + + } + return ApplyComposeFile(ctx, deployment) + +} +func DockerEngineUninstall( + ctx context.Context, deployment AgentDeployment, +) error { + if deployment.DockerType == types.DockerTypeSwarm { + return UninstallDockerSwarm(ctx, deployment) + } + return UninstallDockerCompose(ctx, deployment) +} +func ApplyComposeFile( + ctx context.Context, + deployment api.DockerAgentDeployment, +) (*AgentDeployment, string, error) { + agentDeploymet, err := NewAgentDeployment(deployment) if err != nil { return nil, "", err @@ -60,7 +109,62 @@ func ApplyComposeFile(ctx context.Context, deployment api.DockerAgentDeployment) } } -func UninstallDockerCompose(ctx context.Context, deployment AgentDeployment) error { +func ApplyComposeFileSwarm( + ctx context.Context, + deployment api.DockerAgentDeployment, +) (*AgentDeployment, string, error) { + + agentDeployment, err := NewAgentDeployment(deployment) + if err != nil { + return nil, "", err + } + + // Read the Compose file without replacing environment variables + cleanedCompose := cleanComposeFile(deployment.ComposeFile) + + // Construct environment variables + envVars := os.Environ() + envVars = append(envVars, agentauth.DockerConfigEnv(deployment.AgentDeployment)...) + + // // If an env file is provided, load its values + if deployment.EnvFile != nil { + parsedEnv, err := parseEnvFile(deployment.EnvFile) + if err != nil { + logger.Error("Failed to parse env file", zap.Error(err)) + return nil, "", fmt.Errorf("failed to parse env file: %w", err) + } + for key, value := range parsedEnv { + envVars = append(envVars, fmt.Sprintf("%s=%s", key, value)) + } + } + + // Deploy the stack + composeArgs := []string{ + "stack", "deploy", + "--compose-file", "-", + "--with-registry-auth", + "--detach=true", + agentDeployment.ProjectName, + } + cmd := exec.CommandContext(ctx, "docker", composeArgs...) + cmd.Stdin = bytes.NewReader(cleanedCompose) + cmd.Env = envVars // Ensure the same env variables are used + + // Execute the command and capture output + cmdOut, err := cmd.CombinedOutput() + statusStr := string(cmdOut) + + if err != nil { + logger.Error("Docker stack deploy failed", zap.String("output", statusStr)) + return nil, "", errors.New(statusStr) + } + + return agentDeployment, statusStr, nil +} + +func UninstallDockerCompose( + ctx context.Context, deployment AgentDeployment, +) error { cmd := exec.CommandContext(ctx, "docker", "compose", "--project-name", deployment.ProjectName, "down", "--volumes") out, err := cmd.CombinedOutput() if err != nil { @@ -68,3 +172,110 @@ func UninstallDockerCompose(ctx context.Context, deployment AgentDeployment) err } return nil } + +func UninstallDockerSwarm( + ctx context.Context, deployment AgentDeployment, +) error { + + cmd := exec.CommandContext(ctx, "docker", "stack", "rm", deployment.ProjectName) + out, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("failed to remove Docker Swarm stack: %w: %v", err, string(out)) + } + + // Optional: Prune unused networks created by Swarm + pruneCmd := exec.CommandContext(ctx, "docker", "network", "prune", "-f") + pruneOut, pruneErr := pruneCmd.CombinedOutput() + if pruneErr != nil { + logger.Warn("Failed to prune networks", zap.String("output", string(pruneOut)), zap.Error(pruneErr)) + } + + return nil +} +func cleanComposeFile(composeData []byte) []byte { + lines := strings.Split(string(composeData), "\n") + cleanedLines := make([]string, 0, 50) + + for _, line := range lines { + // Skip lines that define `name:` + if strings.HasPrefix(strings.TrimSpace(line), "name:") { + continue + } + cleanedLines = append(cleanedLines, line) + } + return []byte(strings.Join(cleanedLines, "\n")) +} +func parseEnvFile(envData []byte) (map[string]string, error) { + envVars := make(map[string]string) + scanner := bufio.NewScanner(bytes.NewReader(envData)) + for scanner.Scan() { + line := scanner.Text() + if strings.TrimSpace(line) == "" || strings.HasPrefix(line, "#") { + continue // Skip empty lines and comments + } + parts := strings.SplitN(line, "=", 2) + if len(parts) != 2 { + return nil, fmt.Errorf("invalid environment variable: %s", line) + } + envVars[parts[0]] = parts[1] + } + return envVars, scanner.Err() +} + +type ComposeService struct { + Image string `yaml:"image"` +} + +// ComposeFile represents the structure of docker-compose.yml +type ComposeFile struct { + Services map[string]ComposeService `yaml:"services"` +} + +func PullSwarmMode( + ctx context.Context, deployment api.DockerAgentDeployment, +) (string, error) { + + // Parse the compose YAML file + var compose ComposeFile + err := yaml.Unmarshal(deployment.ComposeFile, &compose) + if err != nil { + return "", fmt.Errorf("failed to parse docker-compose.yml: %w", err) + } + + // Extract image names + var images []string + for _, service := range compose.Services { + if service.Image != "" { + images = append(images, service.Image) + } + } + + if len(images) == 0 { + return "", fmt.Errorf("no images found in the compose file") + } + + // Pull images using Docker CLI + var pullLogs bytes.Buffer + for _, image := range images { + fmt.Println("Pulling image:", image) + logger.Info("Pulling image:", zap.String("id", image)) + // Run `docker pull IMAGE_NAME` + cmd := exec.CommandContext(ctx, "docker", "pull", image) + var out bytes.Buffer + cmd.Stdout = &out + cmd.Stderr = &out + + err := cmd.Run() + if err != nil { + logger.Error("failed to pull image", zap.Error(err)) + return "", fmt.Errorf("failed to pull image %s: %w\nOutput: %s", image, err, out.String()) + } + + // Append logs + pullLogs.WriteString(out.String() + "\n") + fmt.Println(out.String()) + } + + fmt.Println("Image pulling complete.") + return pullLogs.String(), nil +} diff --git a/cmd/agent/docker/main.go b/cmd/agent/docker/main.go index ddcd30fe..8a7c284a 100644 --- a/cmd/agent/docker/main.go +++ b/cmd/agent/docker/main.go @@ -51,6 +51,7 @@ loop: if resource, err := client.DockerResource(ctx); err != nil { logger.Error("failed to get resource", zap.Error(err)) } else { + if agentVersionID != "" { if agentVersionID != resource.Version.ID.String() { logger.Info("agent version has changed. starting self-update") @@ -71,13 +72,14 @@ loop: } } - if deployments, err := GetExistingDeployments(); err != nil { + deployments, err := GetExistingDeployments() + if err != nil { logger.Error("could not get existing deployments", zap.Error(err)) } else { for _, deployment := range deployments { if resource.Deployment == nil || resource.Deployment.ID != deployment.ID { logger.Info("uninstalling old deployment", zap.String("id", deployment.ID.String())) - if err := UninstallDockerCompose(ctx, deployment); err != nil { + if err := DockerEngineUninstall(ctx, deployment); err != nil { logger.Error("could not uninstall deployment", zap.Error(err)) } else if err := DeleteDeployment(deployment); err != nil { logger.Error("could not delete deployment", zap.Error(err)) @@ -96,8 +98,15 @@ loop: _, err = agentauth.EnsureAuth(ctx, resource.Deployment.AgentDeployment) if err != nil { logger.Error("docker auth error", zap.Error(err)) - } else if agentDeployment, status, err = ApplyComposeFile(ctx, *resource.Deployment); err == nil { - multierr.AppendInto(&err, SaveDeployment(*agentDeployment)) + } + if _, exists := deployments[resource.Deployment.RevisionID]; !exists { + + agentDeployment, status, err = DockerEngineApply(ctx, *resource.Deployment) + + if err == nil { + multierr.AppendInto(&err, SaveDeployment(*agentDeployment)) + } + } if statusErr := client.Status(ctx, resource.Deployment.RevisionID, status, err); statusErr != nil { diff --git a/frontend/ui/src/app/applications/application-detail.component.html b/frontend/ui/src/app/applications/application-detail.component.html index 5e1b1a17..bef6f59b 100644 --- a/frontend/ui/src/app/applications/application-detail.component.html +++ b/frontend/ui/src/app/applications/application-detail.component.html @@ -244,6 +244,24 @@

New Ver +
+ + + @if ( + newVersionForm.controls.docker.controls.dockerType.invalid && + newVersionForm.controls.docker.controls.dockerType.touched + ) { +

Field is required.

+ } +
@if (application.type === 'kubernetes') {
diff --git a/frontend/ui/src/app/applications/application-detail.component.ts b/frontend/ui/src/app/applications/application-detail.component.ts index e4013fcd..2efe5a89 100644 --- a/frontend/ui/src/app/applications/application-detail.component.ts +++ b/frontend/ui/src/app/applications/application-detail.component.ts @@ -20,7 +20,7 @@ import { } from 'rxjs'; import {ApplicationsService} from '../services/applications.service'; import {AsyncPipe, DatePipe, NgOptimizedImage} from '@angular/common'; -import {Application, ApplicationVersion, HelmChartType} from '@glasskube/distr-sdk'; +import {Application, ApplicationVersion, DockerType, HelmChartType} from '@glasskube/distr-sdk'; import {FaIconComponent} from '@fortawesome/angular-fontawesome'; import { faArchive, @@ -111,7 +111,12 @@ export class ApplicationDetailComponent implements OnInit, OnDestroy { template: new FormControl(''), }), docker: new FormGroup({ - compose: new FormControl('', Validators.required), + dockerType: new FormControl('compose', { + nonNullable: true, + validators: Validators.required, + }), + compose: new FormControl(''), + swarm: new FormControl(''), template: new FormControl(''), }), }); @@ -137,6 +142,7 @@ export class ApplicationDetailComponent implements OnInit, OnDestroy { @ViewChild('nameInput') nameInputElem?: ElementRef; ngOnInit() { this.route.url.subscribe(() => this.breadcrumbDropdown.set(false)); + this.newVersionForm.controls.kubernetes.controls.chartType.valueChanges .pipe(takeUntil(this.destroyed$)) .subscribe((type) => { @@ -146,8 +152,19 @@ export class ApplicationDetailComponent implements OnInit, OnDestroy { this.newVersionForm.controls.kubernetes.controls.chartName.disable(); } }); - } + this.newVersionForm.controls.docker.controls.dockerType.valueChanges + .pipe(takeUntil(this.destroyed$)) + .subscribe((type) => { + if (type === 'compose') { + this.newVersionForm.controls.docker.controls.compose.enable(); + + } else { + this.newVersionForm.controls.docker.controls.compose.disable(); + + } + }); + } ngOnDestroy() { this.destroyed$.next(); this.destroyed$.complete(); @@ -201,9 +218,11 @@ export class ApplicationDetailComponent implements OnInit, OnDestroy { application, { name: this.newVersionForm.controls.versionName.value!, + dockerType: this.newVersionForm.controls.docker.controls.dockerType.value!, }, this.newVersionForm.controls.docker.controls.compose.value!, - this.newVersionForm.controls.docker.controls.template.value! + this.newVersionForm.controls.docker.controls.template.value!, + ); } else { const versionFormVal = this.newVersionForm.controls.kubernetes.value; diff --git a/frontend/ui/src/app/services/applications.service.ts b/frontend/ui/src/app/services/applications.service.ts index dc27ffc1..b025b804 100644 --- a/frontend/ui/src/app/services/applications.service.ts +++ b/frontend/ui/src/app/services/applications.service.ts @@ -73,6 +73,7 @@ export class ApplicationsService implements CrudService { if (template) { formData.append('templatefile', new Blob([template], {type: 'application/yaml'})); } + return this.doCreateVersion(application, applicationVersion, formData); } diff --git a/internal/db/applications.go b/internal/db/applications.go index 268b2c54..a21411f5 100644 --- a/internal/db/applications.go +++ b/internal/db/applications.go @@ -21,7 +21,7 @@ const ( applicationWithVersionsOutputExpr = applicationOutputExpr + `, coalesce(( SELECT array_agg(row(av.id, av.created_at, av.archived_at, av.name, av.application_id, - av.chart_type, av.chart_name, av.chart_url, av.chart_version) ORDER BY av.created_at ASC) + av.chart_type, av.chart_name, av.chart_url, av.chart_version, av.docker_type) ORDER BY av.created_at ASC) FROM ApplicationVersion av WHERE av.application_id = a.id ), array[]::record[]) AS versions ` @@ -29,7 +29,7 @@ const ( applicationWithLicensedVersionsOutputExpr = applicationOutputExpr + `, coalesce(( SELECT array_agg(row(av.id, av.created_at, av.archived_at, av.name, av.application_id, - av.chart_type, av.chart_name, av.chart_url, av.chart_version) ORDER BY av.created_at ASC) + av.chart_type, av.chart_name, av.chart_url, av.chart_version, av.docker_type) ORDER BY av.created_at ASC) FROM ApplicationVersion av WHERE av.application_id = a.id and ((av.id IN @@ -210,7 +210,12 @@ func GetApplicationForApplicationVersionID(ctx context.Context, id, orgID uuid.U } func CreateApplicationVersion(ctx context.Context, applicationVersion *types.ApplicationVersion) error { + // defaultDockerType := DockerTypeCompose + // av.Docker_Type = &defaultDockerType + fmt.Println("----------------->>>>>>>>>>>>", applicationVersion.Docker_Type) + db := internalctx.GetDb(ctx) + args := pgx.NamedArgs{ "name": applicationVersion.Name, "applicationId": applicationVersion.ApplicationID, @@ -218,6 +223,7 @@ func CreateApplicationVersion(ctx context.Context, applicationVersion *types.App "chartName": applicationVersion.ChartName, "chartUrl": applicationVersion.ChartUrl, "chartVersion": applicationVersion.ChartVersion, + "dockerType": applicationVersion.Docker_Type, } if applicationVersion.ComposeFileData != nil { args["composeFileData"] = applicationVersion.ComposeFileData @@ -231,11 +237,11 @@ func CreateApplicationVersion(ctx context.Context, applicationVersion *types.App row, err := db.Query(ctx, `INSERT INTO ApplicationVersion AS av (name, application_id, chart_type, chart_name, chart_url, chart_version, - compose_file_data, values_file_data, template_file_data) - VALUES (@name, @applicationId, @chartType, @chartName, @chartUrl, @chartVersion, @composeFileData::bytea, + docker_type, compose_file_data, values_file_data, template_file_data) + VALUES (@name, @applicationId, @chartType, @chartName, @chartUrl, @chartVersion, @dockerType, @composeFileData::bytea, @valuesFileData::bytea, @templateFileData::bytea) RETURNING av.id, av.created_at, av.archived_at, av.name, av.chart_type, av.chart_name, av.chart_url, - av.chart_version, av.values_file_data, av.template_file_data, av.compose_file_data, av.application_id`, + av.chart_version, av.docker_type, av.values_file_data, av.template_file_data, av.compose_file_data, av.application_id`, args) if err != nil { return fmt.Errorf("can not create ApplicationVersion: %w", err) @@ -255,13 +261,15 @@ func CreateApplicationVersion(ctx context.Context, applicationVersion *types.App func UpdateApplicationVersion(ctx context.Context, applicationVersion *types.ApplicationVersion) error { db := internalctx.GetDb(ctx) rows, err := db.Query(ctx, - `UPDATE ApplicationVersion AS av SET name = @name, archived_at = @archivedAt WHERE id = @id + `UPDATE ApplicationVersion AS av SET name = @name, archived_at = @archivedAt, docker_type = @dockerType + WHERE id = @id RETURNING av.id, av.created_at, av.archived_at, av.name, av.chart_type, av.chart_name, av.chart_url, av.chart_version, - av.values_file_data, av.template_file_data, av.compose_file_data, av.application_id`, + av.docker_type, av.values_file_data, av.template_file_data, av.compose_file_data, av.application_id`, pgx.NamedArgs{ "id": applicationVersion.ID, "name": applicationVersion.Name, "archivedAt": applicationVersion.ArchivedAt, + "dockerType": applicationVersion.Docker_Type, }) if err != nil { if pgerr := (*pgconn.PgError)(nil); errors.As(err, &pgerr) && pgerr.Code == pgerrcode.UniqueViolation { @@ -284,7 +292,7 @@ func GetApplicationVersion(ctx context.Context, applicationVersionID uuid.UUID) rows, err := db.Query( ctx, `SELECT av.id, av.created_at, av.archived_at, av.name, av.chart_type, av.chart_name, av.chart_url, av.chart_version, - av.values_file_data, av.template_file_data, av.compose_file_data, av.application_id + av.docker_type, av.values_file_data, av.template_file_data, av.compose_file_data, av.application_id FROM ApplicationVersion av WHERE id = @id`, pgx.NamedArgs{"id": applicationVersionID}, diff --git a/internal/handlers/agent.go b/internal/handlers/agent.go index caac8084..0ea548aa 100644 --- a/internal/handlers/agent.go +++ b/internal/handlers/agent.go @@ -183,6 +183,7 @@ func agentResourcesHandler(w http.ResponseWriter, r *http.Request) { AgentDeployment: baseDeployment, ComposeFile: patchedComposeFile, EnvFile: deployment.EnvFileData, + DockerType: *appVersion.Docker_Type, } } } else { diff --git a/internal/migrations/sql/20_add_docker_type_to_application_version.sql.down.sql b/internal/migrations/sql/20_add_docker_type_to_application_version.sql.down.sql new file mode 100644 index 00000000..f72dd28a --- /dev/null +++ b/internal/migrations/sql/20_add_docker_type_to_application_version.sql.down.sql @@ -0,0 +1,9 @@ +ALTER TABLE ApplicationVersion + DROP COLUMN IF EXISTS docker_type; + +DO $$ +BEGIN + IF EXISTS (SELECT 1 FROM pg_type WHERE typname = 'docker_type') THEN + DROP TYPE DOCKER_TYPE; + END IF; +END $$; diff --git a/internal/migrations/sql/20_add_docker_type_to_application_version.sql.up.sql b/internal/migrations/sql/20_add_docker_type_to_application_version.sql.up.sql new file mode 100644 index 00000000..4a34349c --- /dev/null +++ b/internal/migrations/sql/20_add_docker_type_to_application_version.sql.up.sql @@ -0,0 +1,10 @@ +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'docker_type') THEN + CREATE TYPE DOCKER_TYPE AS ENUM ('compose', 'swarm'); + END IF; +END $$; + +ALTER TABLE ApplicationVersion + ADD COLUMN IF NOT EXISTS docker_type DOCKER_TYPE DEFAULT 'compose'; + diff --git a/internal/types/application_version.go b/internal/types/application_version.go index 1f0ec259..1746421f 100644 --- a/internal/types/application_version.go +++ b/internal/types/application_version.go @@ -11,16 +11,16 @@ import ( type ApplicationVersion struct { // unfortunately Base nested type doesn't work when ApplicationVersion is a nested row in an SQL query - ID uuid.UUID `db:"id" json:"id"` - CreatedAt time.Time `db:"created_at" json:"createdAt"` - ArchivedAt *time.Time `db:"archived_at" json:"archivedAt,omitempty"` - Name string `db:"name" json:"name"` - ApplicationID uuid.UUID `db:"application_id" json:"applicationId"` - - ChartType *HelmChartType `db:"chart_type" json:"chartType,omitempty"` - ChartName *string `db:"chart_name" json:"chartName,omitempty"` - ChartUrl *string `db:"chart_url" json:"chartUrl,omitempty"` - ChartVersion *string `db:"chart_version" json:"chartVersion,omitempty"` + ID uuid.UUID `db:"id" json:"id"` + CreatedAt time.Time `db:"created_at" json:"createdAt"` + ArchivedAt *time.Time `db:"archived_at" json:"archivedAt,omitempty"` + Name string `db:"name" json:"name"` + ApplicationID uuid.UUID `db:"application_id" json:"applicationId"` + Docker_Type *DockerType `db:"docker_type" json:"dockerType,omitempty"` + ChartType *HelmChartType `db:"chart_type" json:"chartType,omitempty"` + ChartName *string `db:"chart_name" json:"chartName,omitempty"` + ChartUrl *string `db:"chart_url" json:"chartUrl,omitempty"` + ChartVersion *string `db:"chart_version" json:"chartVersion,omitempty"` // awful but relevant: the following must be defined after the ChartType, because somehow order matters // for pgx at collecting the subrows (relevant at getting application + list of its versions with these @@ -60,8 +60,10 @@ func (av ApplicationVersion) ParsedComposeFile() (result map[string]any, err err func (av ApplicationVersion) Validate(deplType DeploymentType) error { if deplType == DeploymentTypeDocker { - if av.ComposeFileData == nil { - return errors.New("missing compose file") + if av.Docker_Type == nil || *av.Docker_Type == "" { + return errors.New("missing docker type") + } else if *av.Docker_Type == DockerTypeCompose && av.ComposeFileData == nil { + return errors.New("missing compose file for compose deployment") } else if av.ChartType != nil || av.ChartName != nil || av.ChartUrl != nil || av.ChartVersion != nil || av.ValuesFileData != nil { return errors.New("unexpected kubernetes specifics in docker application") diff --git a/internal/types/types.go b/internal/types/types.go index dd371493..fb765ea6 100644 --- a/internal/types/types.go +++ b/internal/types/types.go @@ -12,6 +12,7 @@ type HelmChartType string type DeploymentStatusType string type DeploymentTargetScope string type Feature string +type DockerType string const ( DeploymentTypeDocker DeploymentType = "docker" @@ -23,6 +24,9 @@ const ( HelmChartTypeRepository HelmChartType = "repository" HelmChartTypeOCI HelmChartType = "oci" + DockerTypeCompose DockerType = "compose" + DockerTypeSwarm DockerType = "swarm" + DeploymentStatusTypeOK DeploymentStatusType = "ok" DeploymentStatusTypeError DeploymentStatusType = "error" diff --git a/sdk/js/src/types/application.ts b/sdk/js/src/types/application.ts index f0c96203..d5a1c1bb 100644 --- a/sdk/js/src/types/application.ts +++ b/sdk/js/src/types/application.ts @@ -1,5 +1,5 @@ import {BaseModel, Named} from './base'; -import {DeploymentType, HelmChartType} from './deployment'; +import {DeploymentType, HelmChartType, DockerType} from './deployment'; export interface Application extends BaseModel, Named { type: DeploymentType; @@ -13,6 +13,7 @@ export interface ApplicationVersion { archivedAt?: string; applicationId?: string; chartType?: HelmChartType; + dockerType?: DockerType; chartName?: string; chartUrl?: string; chartVersion?: string; diff --git a/sdk/js/src/types/deployment.ts b/sdk/js/src/types/deployment.ts index 7ef270fa..4346b0b7 100644 --- a/sdk/js/src/types/deployment.ts +++ b/sdk/js/src/types/deployment.ts @@ -37,6 +37,8 @@ export type DeploymentType = 'docker' | 'kubernetes'; export type HelmChartType = 'repository' | 'oci'; +export type DockerType = 'compose' | 'swarm'; + export type DeploymentStatusType = 'ok' | 'error'; export type DeploymentTargetScope = 'cluster' | 'namespace';