Skip to content

Commit bfdc3b4

Browse files
authored
Merge pull request #31 from xraph/feat/deployment-sources-and-variables
feat: polyglot deployment sources (helm, argocd, manifests/kustomize) + first-class template variables
2 parents 7f8e61d + 42db146 commit bfdc3b4

56 files changed

Lines changed: 4730 additions & 123 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

dispatch/dispatch.go

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
// Package dispatch routes a rendered deployment source to the appropriate
2+
// provider operation: container services go through the core Provider
3+
// lifecycle, while helm, manifests, and argocd sources are delegated to the
4+
// provider's optional engine interfaces (gated by capability). It is the
5+
// seam that lets one provisioning path serve every source type.
6+
package dispatch
7+
8+
import (
9+
"context"
10+
"fmt"
11+
12+
ctrlplane "github.com/xraph/ctrlplane"
13+
"github.com/xraph/ctrlplane/id"
14+
"github.com/xraph/ctrlplane/provider"
15+
)
16+
17+
// Request carries everything needed to provision an instance from a
18+
// rendered source, independent of source type.
19+
type Request struct {
20+
InstanceID id.ID
21+
TenantID string
22+
Name string
23+
Namespace string
24+
Kind provider.WorkloadKind
25+
Source provider.RenderedSource
26+
Labels map[string]string
27+
}
28+
29+
// Provision routes a rendered source to the right provider engine. Services
30+
// use the core Provision; other types require the provider to implement the
31+
// matching engine interface and advertise its capability, else
32+
// ctrlplane.ErrUnsupportedSource.
33+
func Provision(ctx context.Context, p provider.Provider, req Request) (*provider.ProvisionResult, error) {
34+
switch req.Source.Type {
35+
case provider.SourceServices:
36+
return p.Provision(ctx, provider.ProvisionRequest{
37+
InstanceID: req.InstanceID,
38+
TenantID: req.TenantID,
39+
Name: req.Name,
40+
Kind: req.Kind,
41+
Services: req.Source.Services,
42+
Labels: req.Labels,
43+
})
44+
case provider.SourceManifests:
45+
eng, ok := manifestEngine(p)
46+
if !ok {
47+
return nil, unsupported(req.Source.Type)
48+
}
49+
50+
var docs provider.RenderedManifests
51+
if req.Source.Manifests != nil {
52+
docs = *req.Source.Manifests
53+
}
54+
55+
return eng.ApplyManifests(ctx, provider.ManifestApplyRequest{
56+
InstanceID: req.InstanceID,
57+
TenantID: req.TenantID,
58+
Namespace: req.Namespace,
59+
Manifests: docs,
60+
Labels: req.Labels,
61+
})
62+
case provider.SourceHelm:
63+
eng, ok := helmEngine(p)
64+
if !ok {
65+
return nil, unsupported(req.Source.Type)
66+
}
67+
68+
var chart provider.RenderedHelm
69+
if req.Source.Helm != nil {
70+
chart = *req.Source.Helm
71+
}
72+
73+
return eng.HelmInstall(ctx, provider.HelmInstallRequest{
74+
InstanceID: req.InstanceID,
75+
TenantID: req.TenantID,
76+
Namespace: req.Namespace,
77+
Chart: chart,
78+
})
79+
case provider.SourceArgoCD:
80+
eng, ok := argoEngine(p)
81+
if !ok {
82+
return nil, unsupported(req.Source.Type)
83+
}
84+
85+
var app provider.ArgoCDSource
86+
if req.Source.ArgoCD != nil {
87+
app = *req.Source.ArgoCD
88+
}
89+
90+
return eng.ArgoApply(ctx, provider.ArgoApplyRequest{
91+
InstanceID: req.InstanceID,
92+
TenantID: req.TenantID,
93+
App: app,
94+
Labels: req.Labels,
95+
})
96+
default:
97+
return nil, fmt.Errorf("%w: %q", ctrlplane.ErrInvalidSource, req.Source.Type)
98+
}
99+
}
100+
101+
// Deprovision routes teardown by source type. An empty sourceType (legacy
102+
// instances that predate Source) is treated as services.
103+
func Deprovision(ctx context.Context, p provider.Provider, sourceType provider.SourceType, instanceID id.ID) error {
104+
switch sourceType {
105+
case provider.SourceServices, "":
106+
return p.Deprovision(ctx, instanceID)
107+
case provider.SourceManifests:
108+
eng, ok := manifestEngine(p)
109+
if !ok {
110+
return unsupported(sourceType)
111+
}
112+
113+
return eng.DeleteManifests(ctx, instanceID)
114+
case provider.SourceHelm:
115+
eng, ok := helmEngine(p)
116+
if !ok {
117+
return unsupported(sourceType)
118+
}
119+
120+
return eng.HelmUninstall(ctx, instanceID)
121+
case provider.SourceArgoCD:
122+
eng, ok := argoEngine(p)
123+
if !ok {
124+
return unsupported(sourceType)
125+
}
126+
127+
return eng.ArgoDelete(ctx, instanceID)
128+
default:
129+
return fmt.Errorf("%w: %q", ctrlplane.ErrInvalidSource, sourceType)
130+
}
131+
}
132+
133+
// Status routes a status read by source type. An empty sourceType is treated
134+
// as services.
135+
func Status(ctx context.Context, p provider.Provider, sourceType provider.SourceType, instanceID id.ID) (*provider.InstanceStatus, error) {
136+
switch sourceType {
137+
case provider.SourceServices, "":
138+
return p.Status(ctx, instanceID)
139+
case provider.SourceManifests:
140+
eng, ok := manifestEngine(p)
141+
if !ok {
142+
return nil, unsupported(sourceType)
143+
}
144+
145+
return eng.ManifestStatus(ctx, instanceID)
146+
case provider.SourceHelm:
147+
eng, ok := helmEngine(p)
148+
if !ok {
149+
return nil, unsupported(sourceType)
150+
}
151+
152+
return eng.HelmStatus(ctx, instanceID)
153+
case provider.SourceArgoCD:
154+
eng, ok := argoEngine(p)
155+
if !ok {
156+
return nil, unsupported(sourceType)
157+
}
158+
159+
return eng.ArgoStatus(ctx, instanceID)
160+
default:
161+
return nil, fmt.Errorf("%w: %q", ctrlplane.ErrInvalidSource, sourceType)
162+
}
163+
}
164+
165+
// unsupported builds the capability-gated error.
166+
func unsupported(t provider.SourceType) error {
167+
return fmt.Errorf("%w: %q", ctrlplane.ErrUnsupportedSource, t)
168+
}
169+
170+
// manifestEngine returns the provider as a ManifestEngine when it both
171+
// implements the interface and advertises the capability.
172+
func manifestEngine(p provider.Provider) (provider.ManifestEngine, bool) {
173+
eng, ok := p.(provider.ManifestEngine)
174+
175+
return eng, ok && provider.HasCapability(p, provider.CapManifests)
176+
}
177+
178+
// helmEngine returns the provider as a HelmEngine when supported.
179+
func helmEngine(p provider.Provider) (provider.HelmEngine, bool) {
180+
eng, ok := p.(provider.HelmEngine)
181+
182+
return eng, ok && provider.HasCapability(p, provider.CapHelm)
183+
}
184+
185+
// argoEngine returns the provider as an ArgoEngine when supported.
186+
func argoEngine(p provider.Provider) (provider.ArgoEngine, bool) {
187+
eng, ok := p.(provider.ArgoEngine)
188+
189+
return eng, ok && provider.HasCapability(p, provider.CapArgoCD)
190+
}

0 commit comments

Comments
 (0)