Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion deployment/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ services:
networks:
- default
container_name: s3
image: bitnami/minio:2025.4.3
image: bitnamilegacy/minio:2025.7.23
ports:
- 3902:3902
- 2903:2903
Expand Down
86 changes: 45 additions & 41 deletions go/apps/ctrl/services/deployment/deploy_workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"github.com/unkeyed/unkey/go/gen/proto/metald/v1/metaldv1connect"
partitionv1 "github.com/unkeyed/unkey/go/gen/proto/partition/v1"
"github.com/unkeyed/unkey/go/pkg/db"
"github.com/unkeyed/unkey/go/pkg/git"
"github.com/unkeyed/unkey/go/pkg/hydra"
"github.com/unkeyed/unkey/go/pkg/otel/logging"
partitiondb "github.com/unkeyed/unkey/go/pkg/partition/db"
Expand Down Expand Up @@ -40,6 +39,12 @@ type DeployWorkflowConfig struct {

// NewDeployWorkflow creates a new deploy workflow instance
func NewDeployWorkflow(cfg DeployWorkflowConfig) *DeployWorkflow {

cfg.Logger.Info("Initializing deploy workflow",
"metald_backend", cfg.MetaldBackend,
"default_domain", cfg.DefaultDomain,
"is_running_docker", cfg.IsRunningDocker,
)
// Create the appropriate deployment backend
deploymentBackend, err := NewDeploymentBackend(cfg.MetalD, cfg.MetaldBackend, cfg.Logger, cfg.IsRunningDocker)
if err != nil {
Expand Down Expand Up @@ -88,8 +93,34 @@ func (w *DeployWorkflow) Run(ctx hydra.WorkflowContext, req *DeployRequest) erro
"workspace_id", req.WorkspaceID,
"project_id", req.ProjectID,
)

workspace, err := hydra.Step(ctx, "get-workspace", func(stepCtx context.Context) (db.Workspace, error) {
return db.Query.FindWorkspaceByID(stepCtx, w.db.RW(), req.WorkspaceID)
})
if err != nil {
return err
}
project, err := hydra.Step(ctx, "get-project", func(stepCtx context.Context) (db.FindProjectByIdRow, error) {
return db.Query.FindProjectById(stepCtx, w.db.RW(), req.ProjectID)
})
if err != nil {
return err
}
environment, err := hydra.Step(ctx, "get-environment", func(stepCtx context.Context) (db.FindEnvironmentByIdRow, error) {
return db.Query.FindEnvironmentById(stepCtx, w.db.RW(), req.EnvironmentID)
})
if err != nil {
return err
}
deployment, err := hydra.Step(ctx, "get-deployment", func(stepCtx context.Context) (db.FindDeploymentByIdRow, error) {
return db.Query.FindDeploymentById(stepCtx, w.db.RW(), req.DeploymentID)
})
if err != nil {
return err
}

// Log deployment pending
err := hydra.StepVoid(ctx, "log-deployment-pending", func(stepCtx context.Context) error {
err = hydra.StepVoid(ctx, "log-deployment-pending", func(stepCtx context.Context) error {
return db.Query.InsertDeploymentStep(stepCtx, w.db.RW(), db.InsertDeploymentStepParams{
WorkspaceID: req.WorkspaceID,
ProjectID: req.ProjectID,
Expand Down Expand Up @@ -119,7 +150,7 @@ func (w *DeployWorkflow) Run(ctx hydra.WorkflowContext, req *DeployRequest) erro
return err
}

deployment, err := hydra.Step(ctx, "create-deployment", func(stepCtx context.Context) (*metaldv1.CreateDeploymentResponse, error) {
metaldDeployment, err := hydra.Step(ctx, "create-deployment", func(stepCtx context.Context) (*metaldv1.CreateDeploymentResponse, error) {
if w.deploymentBackend == nil {
return nil, fmt.Errorf("deployment backend not initialized")
}
Expand All @@ -146,7 +177,7 @@ func (w *DeployWorkflow) Run(ctx hydra.WorkflowContext, req *DeployRequest) erro
return err
}

w.logger.Info("deployment created", "deployment_id", req.DeploymentID, "vm_count", len(deployment.GetVmIds()))
w.logger.Info("deployment created", "deployment_id", req.DeploymentID, "vm_count", len(metaldDeployment.GetVmIds()))

// Update version status to deploying
_, err = hydra.Step(ctx, "update-version-deploying", func(stepCtx context.Context) (*struct{}, error) {
Expand Down Expand Up @@ -243,46 +274,19 @@ func (w *DeployWorkflow) Run(ctx hydra.WorkflowContext, req *DeployRequest) erro
if err != nil {
return err
}

allDomains, err := hydra.Step(ctx, "generate-all-domains", func(stepCtx context.Context) ([]string, error) {
var domains []string

// Generate auto-generated hostname for this deployment
gitInfo := git.GetInfo()
branch := "main" // Default branch
identifier := req.DeploymentID // Use full version ID as identifier

if gitInfo.IsRepo {
if gitInfo.Branch != "" {
branch = gitInfo.Branch
}
if gitInfo.CommitSHA != "" {
identifier = gitInfo.CommitSHA
}
}

// Generate primary hostname: branch-identifier-workspace.domain
cleanIdentifier := strings.ToLower(strings.ReplaceAll(identifier, "_", "-"))
cleanBranch := strings.ToLower(strings.ReplaceAll(branch, "/", "-"))
cleanWorkspaceID := strings.ToLower(req.WorkspaceID)
autoGeneratedHostname := fmt.Sprintf("%s-%s-%s.%s", cleanBranch, cleanIdentifier, cleanWorkspaceID, w.defaultDomain)
domains = append(domains, autoGeneratedHostname)

w.logger.Info("generated all domains",
"deployment_id", req.DeploymentID,
"total_domains", len(domains),
"domains", domains,
)

return domains, nil
})
if err != nil {
return err
}
allDomains := buildDomains(
workspace.Slug,
project.Slug,
environment.Slug,
deployment.GitCommitSha.String,
deployment.GitBranch.String,
w.defaultDomain,
)

// Create database entries for all domains
err = hydra.StepVoid(ctx, "create-domain-entries", func(stepCtx context.Context) error {
// Prepare bulk insert parameters
//
domainParams := make([]db.InsertDomainParams, 0, len(allDomains))
currentTime := time.Now().UnixMilli()

Expand All @@ -296,7 +300,7 @@ func (w *DeployWorkflow) Run(ctx hydra.WorkflowContext, req *DeployRequest) erro
DeploymentID: sql.NullString{Valid: true, String: req.DeploymentID},
CreatedAt: currentTime,
UpdatedAt: sql.NullInt64{Valid: true, Int64: currentTime},
Type: db.DomainsTypeCustom,
Type: db.DomainsTypeWildcard,
})
}

Expand Down
75 changes: 75 additions & 0 deletions go/apps/ctrl/services/deployment/domains.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package deployment

import (
"fmt"
"regexp"
"strings"
)

// buildDomains looks at the deployment and returns a list of domains
// that should be assigned to the deployment.
//
// We want these domains per deployment
// - `<projectslug>-git-<gitsha>-<workspaceslug>.unkey.app` (this never gets reassigned)
// - `<projectslug>-git-<branchname>-<workspaceslug>.unkey.app` (this needs to point to the latest deployment of that branch, sluggify the branch name )
// - `<projectslug>-<environmentslug>-<workspaceslug>.unkey.app` (this needs to point to the latest deployment of that environment and be rolled back)
func buildDomains(workspaceSlug, projectSlug, environmentSlug, gitSha, branchName, apex string) []string {

var domains []string

if gitSha != "" {
short := gitSha
if len(short) > 7 {
short = short[:7]
}
domains = append(domains,
fmt.Sprintf("%s-git-%s-%s.%s", projectSlug, short, workspaceSlug, apex),
)
}

if branchName != "" {
domains = append(
domains,
fmt.Sprintf("%s-git-%s-%s.%s", projectSlug, sluggify(branchName), workspaceSlug, apex),
)
}

domains = append(
domains,
fmt.Sprintf("%s-%s-%s.%s", projectSlug, environmentSlug, workspaceSlug, apex),
)
return domains

}

var nonAlphanumericRegex = regexp.MustCompile(`[^a-zA-Z0-9\s]`)
var multipleSpacesRegex = regexp.MustCompile(`\s+`)

func sluggify(s string) string {
// Trim whitespace
s = strings.TrimSpace(s)

// Remove all non-alphanumeric characters except spaces
s = nonAlphanumericRegex.ReplaceAllString(s, " ")
s = strings.ReplaceAll(s, "-", " ")
s = strings.ReplaceAll(s, "_", " ")

// Replace multiple spaces with single space
s = multipleSpacesRegex.ReplaceAllString(s, " ")

// Replace spaces with hyphens
s = strings.ReplaceAll(s, " ", "-")

// Convert to lowercase
s = strings.ToLower(s)

// Limit to 80 characters
if len(s) > 80 {
s = s[:80]
}

// Remove trailing hyphen if present
s = strings.TrimSuffix(s, "-")

return s
}
2 changes: 2 additions & 0 deletions go/k8s/manifests/ctrl.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ spec:
value: "false"
- name: UNKEY_METALD_BACKEND
value: "k8s"
- name: UNKEY_DEFAULT_DOMAIN
value: "unkey.local"
command: ["/unkey", "run", "ctrl"]
initContainers:
- name: wait-for-dependencies
Expand Down
2 changes: 1 addition & 1 deletion go/k8s/manifests/s3.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ spec:
spec:
containers:
- name: minio
image: bitnami/minio:2025.7.23
image: bitnamilegacy/minio:2025.7.23
ports:
- containerPort: 9000
name: api
Expand Down
43 changes: 43 additions & 0 deletions go/pkg/db/environment_find_by_id.sql_generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions go/pkg/db/querier_generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions go/pkg/db/queries/environment_find_by_id.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- name: FindEnvironmentById :one
SELECT id, workspace_id, project_id, slug, description
FROM environments
WHERE id = sqlc.arg(id);