AppManager: cache helm charts and container images to local registry
Caching container images is disabled until we figure out how to run
container registry behind TLS.
Change-Id: I0253f2a862e5adddff18a82b102f67258151c070
diff --git a/core/installer/Makefile b/core/installer/Makefile
index ce9c184..ca0fa6b 100644
--- a/core/installer/Makefile
+++ b/core/installer/Makefile
@@ -31,14 +31,11 @@
create_env:
./pcloud --kubeconfig=../../priv/kubeconfig create-env --admin-priv-key=/Users/lekva/.ssh/id_rsa --name=lekva --ip=192.168.0.211 --admin-username=gio
-rpuppy:
- ./pcloud --kubeconfig=../../priv/kubeconfig install --ssh-key=/Users/lekva/.ssh/id_rsa --app=rpuppy --repo-addr=ssh://localhost:2222/lekva
-
appmanager:
- ./pcloud --kubeconfig=../../priv/kubeconfig-hetzner appmanager --ssh-key=/Users/lekva/.ssh/id_ed25519 --repo-addr=ssh://localhost:2222/config --port=9090 # --app-repo-addr=http://localhost:8080
+ ./pcloud --kubeconfig=../../priv/kubeconfig-hetzner appmanager --ssh-key=/Users/lekva/.ssh/id_ed25519 --repo-addr=ssh://10.43.196.174/config --port=9090 # --app-repo-addr=http://localhost:8080
dodo-app:
- ./pcloud --kubeconfig=../../priv/kubeconfig-hetzner dodo-app --ssh-key=/Users/lekva/.ssh/id_ed25519 --repo-addr=ssh://localhost:2222/test
+ ./pcloud --kubeconfig=../../priv/kubeconfig-hetzner dodo-app --ssh-key=/Users/lekva/.ssh/id_ed25519 --repo-addr=ssh://10.43.196.174/test
welc:
./pcloud --kubeconfig=../../priv/kubeconfig welcome --ssh-key=/Users/lekva/.ssh/id_rsa --repo-addr=ssh://192.168.0.210/config --port=9090
@@ -47,10 +44,10 @@
./pcloud --kubeconfig=../../priv/kubeconfig-hetzner envmanager --ssh-key=/Users/lekva/.ssh/id_rsa --repo-addr=192.168.100.210:22 --repo-name=config --port=9090
rewrite:
- ./pcloud rewrite --ssh-key=/Users/lekva/.ssh/id_ed25519 --repo-addr=ssh://localhost:2222/config
+ ./pcloud rewrite --ssh-key=/Users/lekva/.ssh/id_ed25519 --repo-addr=ssh://10.43.196.174/config
launcher:
- ./pcloud launcher --port=9090 --logout-url=http://localhost:8080 --ssh-key=/Users/lekva/.ssh/id_ed25519 --repo-addr=ssh://localhost:2222/config
+ ./pcloud launcher --port=9090 --logout-url=http://localhost:8080 --ssh-key=/Users/lekva/.ssh/id_ed25519 --repo-addr=ssh://10.43.196.174/config
## installer image
build_arm64: export CGO_ENABLED=0
diff --git a/core/installer/app.go b/core/installer/app.go
index 2a16c1e..86447fe 100644
--- a/core/installer/app.go
+++ b/core/installer/app.go
@@ -15,343 +15,32 @@
"cuelang.org/go/cue/cuecontext"
"cuelang.org/go/cue/load"
cueyaml "cuelang.org/go/encoding/yaml"
+ helmv2 "github.com/fluxcd/helm-controller/api/v2"
)
-//go:embed pcloud_app.cue
-var DodoAppCue []byte
+//go:embed app_configs/dodo_app.cue
+var dodoAppCue []byte
-// TODO(gio): import
-const cueEnvAppGlobal = `
-import (
- "net"
-)
+//go:embed app_configs/app_base.cue
+var cueBaseConfig []byte
-#Global: {
- id: string | *""
- pcloudEnvName: string | *""
- domain: string | *""
- privateDomain: string | *""
- contactEmail: string | *""
- adminPublicKey: string | *""
- publicIP: [...string] | *[]
- nameserverIP: [...string] | *[]
- namespacePrefix: string | *""
- network: #EnvNetwork
-}
+//go:embed app_configs/app_global_env.cue
+var cueEnvAppGlobal []byte
-#EnvNetwork: {
- dns: net.IPv4
- dnsInClusterIP: net.IPv4
- ingress: net.IPv4
- headscale: net.IPv4
- servicesFrom: net.IPv4
- servicesTo: net.IPv4
-}
-
-// TODO(gio): remove
-ingressPrivate: "\(global.id)-ingress-private"
-ingressPublic: "\(global.pcloudEnvName)-ingress-public"
-issuerPrivate: "\(global.id)-private"
-issuerPublic: "\(global.id)-public"
-
-#Ingress: {
- auth: #Auth
- network: #Network
- subdomain: string
- service: close({
- name: string
- port: close({ name: string }) | close({ number: int & > 0 })
- })
-
- _domain: "\(subdomain).\(network.domain)"
- _authProxyHTTPPortName: "http"
-
- out: {
- images: {
- authProxy: #Image & {
- repository: "giolekva"
- name: "auth-proxy"
- tag: "latest"
- pullPolicy: "Always"
- }
- }
- charts: {
- ingress: #Chart & {
- chart: "charts/ingress"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
- }
- authProxy: #Chart & {
- chart: "charts/auth-proxy"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
- }
- }
- helm: {
- if auth.enabled {
- "auth-proxy": {
- chart: charts.authProxy
- values: {
- image: {
- repository: images.authProxy.fullName
- tag: images.authProxy.tag
- pullPolicy: images.authProxy.pullPolicy
- }
- upstream: "\(service.name).\(release.namespace).svc.cluster.local"
- whoAmIAddr: "https://accounts.\(global.domain)/sessions/whoami"
- loginAddr: "https://accounts-ui.\(global.domain)/login"
- membershipAddr: "http://memberships-api.\(global.id)-core-auth-memberships.svc.cluster.local/api/user"
- groups: auth.groups
- portName: _authProxyHTTPPortName
- }
- }
- }
- ingress: {
- chart: charts.ingress
- _service: service
- values: {
- domain: _domain
- ingressClassName: network.ingressClass
- certificateIssuer: network.certificateIssuer
- service: {
- if auth.enabled {
- name: "auth-proxy"
- port: name: _authProxyHTTPPortName
- }
- if !auth.enabled {
- name: _service.name
- if _service.port.name != _|_ {
- port: name: _service.port.name
- }
- if _service.port.number != _|_ {
- port: number: _service.port.number
- }
- }
- }
- }
- }
- }
- }
-}
-
-ingress: {}
-
-_ingressValidate: {
- for key, value in ingress {
- "\(key)": #Ingress & value
- }
-}
-`
-
-const cueInfraAppGlobal = `
-#Global: {
- pcloudEnvName: string | *""
- publicIP: [...string] | *[]
- namespacePrefix: string | *""
- infraAdminPublicKey: string | *""
-}
-
-// TODO(gio): remove
-ingressPublic: "\(global.pcloudEnvName)-ingress-public"
-
-ingress: {}
-_ingressValidate: {}
-`
-
-const cueBaseConfig = `
-name: string | *""
-description: string | *""
-readme: string | *""
-icon: string | *""
-namespace: string | *""
-
-help: [...#HelpDocument] | *[]
-
-#HelpDocument: {
- title: string
- contents: string
- children: [...#HelpDocument] | *[]
-}
-
-url: string | *""
-
-#AppType: "infra" | "env"
-appType: #AppType | *"env"
-
-#Release: {
- appInstanceId: string
- namespace: string
- repoAddr: string
- appDir: string
-}
-
-#Network: {
- name: string
- ingressClass: string
- certificateIssuer: string | *""
- domain: string
- allocatePortAddr: string
-}
-
-#Auth: {
- enabled: bool | *false // TODO(gio): enabled by default?
- groups: string | *"" // TODO(gio): []string
-}
-
-#Image: {
- registry: string | *"docker.io"
- repository: string
- name: string
- tag: string
- pullPolicy: string | *"IfNotPresent"
- imageName: "\(repository)/\(name)"
- fullName: "\(registry)/\(imageName)"
- fullNameWithTag: "\(fullName):\(tag)"
-}
-
-#Chart: {
- chart: string
- sourceRef: #SourceRef
-}
-
-#SourceRef: {
- kind: "GitRepository" | "HelmRepository"
- name: string
- namespace: string // TODO(gio): default global.id
-}
-
-#PortForward: {
- allocator: string
- protocol: "TCP" | "UDP" | *"TCP"
- sourcePort: int
- targetService: string
- targetPort: int
-}
-
-portForward: [...#PortForward] | *[]
-
-global: #Global
-release: #Release
-
-images: {
- for key, value in images {
- "\(key)": #Image & value
- }
- for _, value in _ingressValidate {
- for name, image in value.out.images {
- "\(name)": #Image & image
- }
- }
-}
-
-charts: {
- for key, value in charts {
- "\(key)": #Chart & value
- }
- for _, value in _ingressValidate {
- for name, chart in value.out.charts {
- "\(name)": #Chart & chart
- }
- }
-}
-
-#ResourceReference: {
- name: string
- namespace: string
-}
-
-#Helm: {
- name: string
- dependsOn: [...#ResourceReference] | *[]
- ...
-}
-
-_helmValidate: {
- for key, value in helm {
- "\(key)": #Helm & value & {
- name: key
- }
- }
- for key, value in _ingressValidate {
- for ing, ingValue in value.out.helm {
- // TODO(gio): support multiple ingresses
- // "\(key)-\(ing)": #Helm & ingValue & {
- "\(ing)": #Helm & ingValue & {
- // name: "\(key)-\(ing)"
- name: ing
- }
- }
- }
-}
-
-resources: {}
-
-#HelmRelease: {
- _name: string
- _chart: #Chart
- _values: _
- _dependencies: [...#ResourceReference] | *[]
-
- apiVersion: "helm.toolkit.fluxcd.io/v2beta1"
- kind: "HelmRelease"
- metadata: {
- name: _name
- namespace: release.namespace
- }
- spec: {
- interval: "1m0s"
- dependsOn: _dependencies
- chart: {
- spec: _chart
- }
- values: _values
- }
-}
-
-output: {
- for name, r in _helmValidate {
- "\(name)": #HelmRelease & {
- _name: name
- _chart: r.chart
- _values: r.values
- _dependencies: r.dependsOn
- }
- }
-}
-
-#SSHKey: {
- public: string
- private: string
-}
-
-#HelpDocument: {
- title: string
- contents: string
- children: [...#HelpDocument]
-}
-
-help: [...#HelpDocument] | *[]
-
-url: string | *""
-
-networks: {}
-`
+//go:embed app_configs/app_global_infra.cue
+var cueInfraAppGlobal []byte
type rendered struct {
- Name string
- Readme string
- Resources CueAppData
- Ports []PortForward
- Data CueAppData
- URL string
- Help []HelpDocument
- Icon string
+ Name string
+ Readme string
+ Resources CueAppData
+ HelmCharts HelmCharts
+ ContainerImages map[string]ContainerImage
+ Ports []PortForward
+ Data CueAppData
+ URL string
+ Help []HelpDocument
+ Icon string
}
type HelpDocument struct {
@@ -360,6 +49,27 @@
Children []HelpDocument
}
+type ContainerImage struct {
+ Registry string `json:"registry"`
+ Repository string `json:"repository"`
+ Name string `json:"name"`
+ Tag string `json:"tag"`
+}
+
+type helmChartRef struct {
+ Kind string `json:"kind"`
+}
+
+type HelmCharts struct {
+ Git map[string]HelmChartGitRepo
+}
+
+type HelmChartGitRepo struct {
+ Address string `json:"address"`
+ Branch string `json:"branch"`
+ Path string `json:"path"`
+}
+
type EnvAppRendered struct {
rendered
Config AppInstanceConfig
@@ -404,7 +114,7 @@
type InfraApp interface {
App
- Render(release Release, infra InfraConfig, values map[string]any) (InfraAppRendered, error)
+ Render(release Release, infra InfraConfig, values map[string]any, charts map[string]helmv2.HelmChartTemplateSpec) (InfraAppRendered, error)
}
type EnvNetwork struct {
@@ -459,7 +169,6 @@
}, nil
}
-// TODO(gio): rename to EnvConfig
type EnvConfig struct {
Id string `json:"id,omitempty"`
InfraName string `json:"pcloudEnvName,omitempty"`
@@ -475,7 +184,7 @@
type EnvApp interface {
App
- Render(release Release, env EnvConfig, values map[string]any) (EnvAppRendered, error)
+ Render(release Release, env EnvConfig, values map[string]any, charts map[string]helmv2.HelmChartTemplateSpec) (EnvAppRendered, error)
}
type cueApp struct {
@@ -583,8 +292,12 @@
ret := rendered{
Name: a.Slug(),
Resources: make(CueAppData),
- Ports: make([]PortForward, 0),
- Data: a.data,
+ HelmCharts: HelmCharts{
+ Git: make(map[string]HelmChartGitRepo),
+ },
+ ContainerImages: make(map[string]ContainerImage),
+ Ports: make([]PortForward, 0),
+ Data: a.data,
}
var buf bytes.Buffer
if err := json.NewEncoder(&buf).Encode(values); err != nil {
@@ -613,6 +326,40 @@
return rendered{}, err
}
{
+ charts := res.LookupPath(cue.ParsePath("charts"))
+ i, err := charts.Fields()
+ if err != nil {
+ return rendered{}, err
+ }
+ for i.Next() {
+ var chartRef helmChartRef
+ if err := i.Value().Decode(&chartRef); err != nil {
+ return rendered{}, err
+ }
+ if chartRef.Kind == "GitRepository" {
+ var chart HelmChartGitRepo
+ if err := i.Value().Decode(&chart); err != nil {
+ return rendered{}, err
+ }
+ ret.HelmCharts.Git[cleanName(i.Selector().String())] = chart
+ }
+ }
+ }
+ {
+ images := res.LookupPath(cue.ParsePath("images"))
+ i, err := images.Fields()
+ if err != nil {
+ return rendered{}, err
+ }
+ for i.Next() {
+ var img ContainerImage
+ if err := i.Value().Decode(&img); err != nil {
+ return rendered{}, err
+ }
+ ret.ContainerImages[cleanName(i.Selector().String())] = img
+ }
+ }
+ {
output := res.LookupPath(cue.ParsePath("output"))
i, err := output.Fields()
if err != nil {
@@ -677,7 +424,7 @@
return NewCueEnvApp(CueAppData{
"app.cue": appCfg,
"base.cue": []byte(cueBaseConfig),
- "pcloud_app.cue": DodoAppCue,
+ "pcloud_app.cue": dodoAppCue,
"env_app.cue": []byte(cueEnvAppGlobal),
})
}
@@ -686,17 +433,21 @@
return AppTypeEnv
}
-func (a cueEnvApp) Render(release Release, env EnvConfig, values map[string]any) (EnvAppRendered, error) {
+func (a cueEnvApp) Render(release Release, env EnvConfig, values map[string]any, charts map[string]helmv2.HelmChartTemplateSpec) (EnvAppRendered, error) {
networks := CreateNetworks(env)
derived, err := deriveValues(values, a.Schema(), networks)
if err != nil {
return EnvAppRendered{}, nil
}
+ if charts == nil {
+ charts = make(map[string]helmv2.HelmChartTemplateSpec)
+ }
ret, err := a.cueApp.render(map[string]any{
- "global": env,
- "release": release,
- "input": derived,
- "networks": networkMap(networks),
+ "global": env,
+ "release": release,
+ "input": derived,
+ "localCharts": charts,
+ "networks": networkMap(networks),
})
if err != nil {
return EnvAppRendered{}, err
@@ -732,11 +483,15 @@
return AppTypeInfra
}
-func (a cueInfraApp) Render(release Release, infra InfraConfig, values map[string]any) (InfraAppRendered, error) {
+func (a cueInfraApp) Render(release Release, infra InfraConfig, values map[string]any, charts map[string]helmv2.HelmChartTemplateSpec) (InfraAppRendered, error) {
+ if charts == nil {
+ charts = make(map[string]helmv2.HelmChartTemplateSpec)
+ }
ret, err := a.cueApp.render(map[string]any{
- "global": infra,
- "release": release,
- "input": values,
+ "global": infra,
+ "release": release,
+ "input": values,
+ "localCharts": charts,
})
if err != nil {
return InfraAppRendered{}, err
diff --git a/core/installer/app_configs/app_base.cue b/core/installer/app_configs/app_base.cue
new file mode 100644
index 0000000..d6332e3
--- /dev/null
+++ b/core/installer/app_configs/app_base.cue
@@ -0,0 +1,206 @@
+import (
+ "net"
+)
+
+name: string | *""
+description: string | *""
+readme: string | *""
+icon: string | *""
+namespace: string | *""
+
+help: [...#HelpDocument] | *[]
+
+#HelpDocument: {
+ title: string
+ contents: string
+ children: [...#HelpDocument] | *[]
+}
+
+url: string | *""
+
+#AppType: "infra" | "env"
+appType: #AppType | *"env"
+
+#Auth: {
+ enabled: bool | *false // TODO(gio): enabled by default?
+ groups: string | *"" // TODO(gio): []string
+}
+
+#Network: {
+ name: string
+ ingressClass: string
+ certificateIssuer: string | *""
+ domain: string
+ allocatePortAddr: string
+}
+
+#Image: {
+ registry: string | *release.imageRegistry
+ repository: string
+ name: string
+ tag: string
+ pullPolicy: string | *"IfNotPresent"
+ imageName: "\(repository)/\(name)"
+ fullName: "\(registry)/\(imageName)"
+ fullNameWithTag: "\(fullName):\(tag)"
+}
+
+#Chart: #GitRepositoryRef | #HelmRepositoryRef
+
+#GitRepositoryRef: {
+ name: string
+ kind: "GitRepository"
+ address: string
+ branch: string
+ path: string
+}
+
+#HelmRepositoryRef: {
+ name: string
+ kind: "HelmRepository"
+ repository: string
+ name: string
+ tag: string
+}
+
+#EnvNetwork: {
+ dns: net.IPv4
+ dnsInClusterIP: net.IPv4
+ ingress: net.IPv4
+ headscale: net.IPv4
+ servicesFrom: net.IPv4
+ servicesTo: net.IPv4
+}
+
+#Release: {
+ appInstanceId: string
+ namespace: string
+ repoAddr: string
+ appDir: string
+ imageRegistry: string | *"docker.io"
+}
+
+#PortForward: {
+ allocator: string
+ protocol: "TCP" | "UDP" | *"TCP"
+ sourcePort: int
+ targetService: string
+ targetPort: int
+}
+
+portForward: [...#PortForward] | *[]
+
+global: #Global
+release: #Release
+
+images: {}
+
+images: {
+ for key, value in images {
+ "\(key)": #Image & value
+ }
+ for _, value in _ingressValidate {
+ for name, image in value.out.images {
+ "\(name)": #Image & image
+ }
+ }
+}
+
+charts: {}
+
+charts: {
+ for key, value in charts {
+ "\(key)": #Chart & value & {
+ name: key
+ }
+ }
+ for _, value in _ingressValidate {
+ for name, chart in value.out.charts {
+ "\(name)": #Chart & chart & {
+ name: name
+ }
+ }
+ }
+}
+
+localCharts: {
+ for key, _ in charts {
+ "\(key)": {
+ }
+ }
+}
+
+#ResourceReference: {
+ name: string
+ namespace: string
+}
+
+#Helm: {
+ name: string
+ dependsOn: [...#ResourceReference] | *[]
+ ...
+}
+
+_helmValidate: {
+ for key, value in helm {
+ "\(key)": #Helm & value & {
+ name: key
+ }
+ }
+ for key, value in _ingressValidate {
+ for ing, ingValue in value.out.helm {
+ // TODO(gio): support multiple ingresses
+ // "\(key)-\(ing)": #Helm & ingValue & {
+ "\(ing)": #Helm & ingValue & {
+ // name: "\(key)-\(ing)"
+ name: ing
+ }
+ }
+ }
+}
+
+#HelmRelease: {
+ _name: string
+ _chart: _
+ _values: _
+ _dependencies: [...#ResourceReference] | *[]
+
+ apiVersion: "helm.toolkit.fluxcd.io/v2beta1"
+ kind: "HelmRelease"
+ metadata: {
+ name: _name
+ namespace: release.namespace
+ }
+ spec: {
+ interval: "1m0s"
+ dependsOn: _dependencies
+ chart: spec: _chart
+ values: _values
+ }
+}
+
+output: {
+ for name, r in _helmValidate {
+ "\(name)": #HelmRelease & {
+ _name: name
+ _chart: localCharts[r.chart.name]
+ _values: r.values
+ _dependencies: r.dependsOn
+ }
+ }
+}
+
+#SSHKey: {
+ public: string
+ private: string
+}
+
+#HelpDocument: {
+ title: string
+ contents: string
+ children: [...#HelpDocument]
+}
+
+help: [...#HelpDocument] | *[]
+
+url: string | *""
diff --git a/core/installer/app_configs/app_global_env.cue b/core/installer/app_configs/app_global_env.cue
new file mode 100644
index 0000000..1ae2f4d
--- /dev/null
+++ b/core/installer/app_configs/app_global_env.cue
@@ -0,0 +1,131 @@
+#Global: {
+ id: string | *""
+ pcloudEnvName: string | *""
+ domain: string | *""
+ privateDomain: string | *""
+ contactEmail: string | *""
+ adminPublicKey: string | *""
+ publicIP: [...string] | *[]
+ nameserverIP: [...string] | *[]
+ namespacePrefix: string | *""
+ network: #EnvNetwork
+}
+
+networks: {
+ public: #Network & {
+ name: "Public"
+ ingressClass: "\(global.pcloudEnvName)-ingress-public"
+ certificateIssuer: "\(global.id)-public"
+ domain: global.domain
+ allocatePortAddr: "http://port-allocator.\(global.pcloudEnvName)-ingress-public.svc.cluster.local/api/allocate"
+ }
+ private: #Network & {
+ name: "Private"
+ ingressClass: "\(global.id)-ingress-private"
+ domain: global.privateDomain
+ allocatePortAddr: "http://port-allocator.\(global.id)-ingress-private.svc.cluster.local/api/allocate"
+ }
+}
+
+// TODO(gio): remove
+ingressPrivate: "\(global.id)-ingress-private"
+ingressPublic: "\(global.pcloudEnvName)-ingress-public"
+issuerPrivate: "\(global.id)-private"
+issuerPublic: "\(global.id)-public"
+
+#Ingress: {
+ auth: #Auth
+ network: #Network
+ subdomain: string
+ service: close({
+ name: string
+ port: close({ name: string }) | close({ number: int & > 0 })
+ })
+
+ _domain: "\(subdomain).\(network.domain)"
+ _authProxyHTTPPortName: "http"
+
+ out: {
+ images: {
+ authProxy: #Image & {
+ repository: "giolekva"
+ name: "auth-proxy"
+ tag: "latest"
+ pullPolicy: "Always"
+ }
+ }
+ charts: {
+ ingress: #Chart & {
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/ingress"
+ }
+ authProxy: #Chart & {
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/auth-proxy"
+ }
+ }
+ charts: {
+ for key, value in charts {
+ "\(key)": #Chart & value & {
+ name: key
+ }
+ }
+ }
+ helm: {
+ if auth.enabled {
+ "auth-proxy": {
+ chart: charts.authProxy
+ values: {
+ image: {
+ repository: images.authProxy.fullName
+ tag: images.authProxy.tag
+ pullPolicy: images.authProxy.pullPolicy
+ }
+ upstream: "\(service.name).\(release.namespace).svc.cluster.local"
+ whoAmIAddr: "https://accounts.\(global.domain)/sessions/whoami"
+ loginAddr: "https://accounts-ui.\(global.domain)/login"
+ membershipAddr: "http://memberships-api.\(global.id)-core-auth-memberships.svc.cluster.local/api/user"
+ groups: auth.groups
+ portName: _authProxyHTTPPortName
+ }
+ }
+ }
+ ingress: {
+ chart: charts.ingress
+ _service: service
+ values: {
+ domain: _domain
+ ingressClassName: network.ingressClass
+ certificateIssuer: network.certificateIssuer
+ service: {
+ if auth.enabled {
+ name: "auth-proxy"
+ port: name: _authProxyHTTPPortName
+ }
+ if !auth.enabled {
+ name: _service.name
+ if _service.port.name != _|_ {
+ port: name: _service.port.name
+ }
+ if _service.port.number != _|_ {
+ port: number: _service.port.number
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+ingress: {}
+
+_ingressValidate: {
+ for key, value in ingress {
+ "\(key)": #Ingress & value
+ }
+}
diff --git a/core/installer/app_configs/app_global_infra.cue b/core/installer/app_configs/app_global_infra.cue
new file mode 100644
index 0000000..578a30b
--- /dev/null
+++ b/core/installer/app_configs/app_global_infra.cue
@@ -0,0 +1,13 @@
+#Global: {
+ pcloudEnvName: string | *""
+ publicIP: [...string] | *[]
+ namespacePrefix: string | *""
+ infraAdminPublicKey: string | *""
+}
+
+// TODO(gio): remove
+ingressPublic: "\(global.pcloudEnvName)-ingress-public"
+
+ingress: {}
+_ingressValidate: {}
+
diff --git a/core/installer/pcloud_app.cue b/core/installer/app_configs/dodo_app.cue
similarity index 91%
rename from core/installer/pcloud_app.cue
rename to core/installer/app_configs/dodo_app.cue
index d453747..409b5d4 100644
--- a/core/installer/pcloud_app.cue
+++ b/core/installer/app_configs/dodo_app.cue
@@ -73,12 +73,10 @@
charts: {
app: {
- chart: "charts/app-runner"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/app-runner"
}
}
diff --git a/core/installer/testapp.cue b/core/installer/app_configs/testapp.cue
similarity index 100%
rename from core/installer/testapp.cue
rename to core/installer/app_configs/testapp.cue
diff --git a/core/installer/app_manager.go b/core/installer/app_manager.go
index ae18ff8..9ee9671 100644
--- a/core/installer/app_manager.go
+++ b/core/installer/app_manager.go
@@ -9,10 +9,12 @@
"net/http"
"path"
"path/filepath"
+ "strings"
"github.com/giolekva/pcloud/core/installer/io"
"github.com/giolekva/pcloud/core/installer/soft"
+ helmv2 "github.com/fluxcd/helm-controller/api/v2"
"sigs.k8s.io/yaml"
)
@@ -23,14 +25,24 @@
type AppManager struct {
repoIO soft.RepoIO
- nsCreator NamespaceCreator
+ nsc NamespaceCreator
+ jc JobCreator
+ hf HelmFetcher
appDirRoot string
}
-func NewAppManager(repoIO soft.RepoIO, nsCreator NamespaceCreator, appDirRoot string) (*AppManager, error) {
+func NewAppManager(
+ repoIO soft.RepoIO,
+ nsc NamespaceCreator,
+ jc JobCreator,
+ hf HelmFetcher,
+ appDirRoot string,
+) (*AppManager, error) {
return &AppManager{
repoIO,
- nsCreator,
+ nsc,
+ jc,
+ hf,
appDirRoot,
}, nil
}
@@ -108,14 +120,32 @@
return nil, ErrorNotFound
}
-func (m *AppManager) AppConfig(name string) (AppInstanceConfig, error) {
- var cfg AppInstanceConfig
- if err := soft.ReadJson(m.repoIO, filepath.Join(m.appDirRoot, name, "config.json"), &cfg); err != nil {
- return AppInstanceConfig{}, err
+func GetCueAppData(fs soft.RepoFS, dir string) (CueAppData, error) {
+ files, err := fs.ListDir(dir)
+ if err != nil {
+ return nil, err
+ }
+ cfg := CueAppData{}
+ for _, f := range files {
+ if !f.IsDir() && strings.HasSuffix(f.Name(), ".cue") {
+ contents, err := soft.ReadFile(fs, filepath.Join(dir, f.Name()))
+ if err != nil {
+ return nil, err
+ }
+ cfg[f.Name()] = contents
+ }
}
return cfg, nil
}
+func (m *AppManager) GetInstanceApp(id string) (EnvApp, error) {
+ cfg, err := GetCueAppData(m.repoIO, filepath.Join(m.appDirRoot, id))
+ if err != nil {
+ return nil, err
+ }
+ return NewCueEnvApp(cfg)
+}
+
type allocatePortReq struct {
Protocol string `json:"protocol"`
SourcePort int `json:"sourcePort"`
@@ -186,8 +216,20 @@
ports []PortForward,
resources CueAppData,
data CueAppData,
- opts ...soft.DoOption,
+ opts ...InstallOption,
) (ReleaseResources, error) {
+ var o installOptions
+ for _, i := range opts {
+ i(&o)
+ }
+ dopts := []soft.DoOption{}
+ if o.Branch != "" {
+ dopts = append(dopts, soft.WithForce())
+ dopts = append(dopts, soft.WithCommitToBranch(o.Branch))
+ }
+ if o.NoPublish {
+ dopts = append(dopts, soft.WithNoCommit())
+ }
return ReleaseResources{}, repo.Do(func(r soft.RepoFS) (string, error) {
if err := r.RemoveDir(appDir); err != nil {
return "", err
@@ -238,11 +280,18 @@
}
}
return fmt.Sprintf("install: %s", name), nil
- }, opts...)
+ }, dopts...)
}
// TODO(gio): commit instanceId -> appDir mapping as well
-func (m *AppManager) Install(app EnvApp, instanceId string, appDir string, namespace string, values map[string]any, opts ...InstallOption) (ReleaseResources, error) {
+func (m *AppManager) Install(
+ app EnvApp,
+ instanceId string,
+ appDir string,
+ namespace string,
+ values map[string]any,
+ opts ...InstallOption,
+) (ReleaseResources, error) {
o := &installOptions{}
for _, i := range opts {
i(o)
@@ -251,7 +300,7 @@
if err := m.repoIO.Pull(); err != nil {
return ReleaseResources{}, err
}
- if err := m.nsCreator.Create(namespace); err != nil {
+ if err := m.nsc.Create(namespace); err != nil {
return ReleaseResources{}, err
}
var env EnvConfig
@@ -264,22 +313,47 @@
return ReleaseResources{}, err
}
}
+ var lg LocalChartGenerator
+ if o.LG != nil {
+ lg = o.LG
+ } else {
+ lg = GitRepositoryLocalChartGenerator{env.Id, env.Id}
+ }
release := Release{
AppInstanceId: instanceId,
Namespace: namespace,
RepoAddr: m.repoIO.FullAddress(),
AppDir: appDir,
}
- rendered, err := app.Render(release, env, values)
+ rendered, err := app.Render(release, env, values, nil)
if err != nil {
return ReleaseResources{}, err
}
- dopts := []soft.DoOption{}
- if o.Branch != "" {
- dopts = append(dopts, soft.WithForce())
- dopts = append(dopts, soft.WithCommitToBranch(o.Branch))
+ imageRegistry := fmt.Sprintf("zot.%s", env.PrivateDomain)
+ if o.FetchContainerImages {
+ if err := pullContainerImages(instanceId, rendered.ContainerImages, imageRegistry, namespace, m.jc); err != nil {
+ return ReleaseResources{}, err
+ }
}
- if _, err := installApp(m.repoIO, appDir, rendered.Name, rendered.Config, rendered.Ports, rendered.Resources, rendered.Data, dopts...); err != nil {
+ var localCharts map[string]helmv2.HelmChartTemplateSpec
+ if err := m.repoIO.Do(func(rfs soft.RepoFS) (string, error) {
+ charts, err := pullHelmCharts(m.hf, rendered.HelmCharts, rfs, "/helm-charts")
+ if err != nil {
+ return "", err
+ }
+ localCharts = generateLocalCharts(lg, charts)
+ return "pull helm charts", nil
+ }); err != nil {
+ return ReleaseResources{}, err
+ }
+ if o.FetchContainerImages {
+ release.ImageRegistry = imageRegistry
+ }
+ rendered, err = app.Render(release, env, values, localCharts)
+ if err != nil {
+ return ReleaseResources{}, err
+ }
+ if _, err := installApp(m.repoIO, appDir, rendered.Name, rendered.Config, rendered.Ports, rendered.Resources, rendered.Data, opts...); err != nil {
return ReleaseResources{}, err
}
// TODO(gio): add ingress-nginx to release resources
@@ -316,7 +390,12 @@
return ret
}
-func (m *AppManager) Update(app EnvApp, instanceId string, values map[string]any, opts ...soft.DoOption) (ReleaseResources, error) {
+// TODO(gio): take app configuration from the repo
+func (m *AppManager) Update(
+ instanceId string,
+ values map[string]any,
+ opts ...InstallOption,
+) (ReleaseResources, error) {
if err := m.repoIO.Pull(); err != nil {
return ReleaseResources{}, err
}
@@ -325,18 +404,20 @@
return ReleaseResources{}, err
}
instanceDir := filepath.Join(m.appDirRoot, instanceId)
+ app, err := m.GetInstanceApp(instanceId)
+ if err != nil {
+ return ReleaseResources{}, err
+ }
instanceConfigPath := filepath.Join(instanceDir, "config.json")
config, err := m.appConfig(instanceConfigPath)
if err != nil {
return ReleaseResources{}, err
}
- release := Release{
- AppInstanceId: instanceId,
- Namespace: config.Release.Namespace,
- RepoAddr: m.repoIO.FullAddress(),
- AppDir: instanceDir,
+ localCharts, err := extractLocalCharts(m.repoIO, filepath.Join(instanceDir, "rendered.json"))
+ if err != nil {
+ return ReleaseResources{}, err
}
- rendered, err := app.Render(release, env, values)
+ rendered, err := app.Render(config.Release, env, values, localCharts)
if err != nil {
return ReleaseResources{}, err
}
@@ -379,16 +460,12 @@
}
}
-// InfraAppmanager
-
-type InfraAppManager struct {
- repoIO soft.RepoIO
- nsCreator NamespaceCreator
-}
-
type installOptions struct {
- Env *EnvConfig
- Branch string
+ NoPublish bool
+ Env *EnvConfig
+ Branch string
+ LG LocalChartGenerator
+ FetchContainerImages bool
}
type InstallOption func(*installOptions)
@@ -405,10 +482,44 @@
}
}
-func NewInfraAppManager(repoIO soft.RepoIO, nsCreator NamespaceCreator) (*InfraAppManager, error) {
+func WithLocalChartGenerator(lg LocalChartGenerator) InstallOption {
+ return func(o *installOptions) {
+ o.LG = lg
+ }
+}
+
+func WithFetchContainerImages() InstallOption {
+ return func(o *installOptions) {
+ o.FetchContainerImages = true
+ }
+}
+
+func WithNoPublish() InstallOption {
+ return func(o *installOptions) {
+ o.NoPublish = true
+ }
+}
+
+// InfraAppmanager
+
+type InfraAppManager struct {
+ repoIO soft.RepoIO
+ nsc NamespaceCreator
+ hf HelmFetcher
+ lg LocalChartGenerator
+}
+
+func NewInfraAppManager(
+ repoIO soft.RepoIO,
+ nsc NamespaceCreator,
+ hf HelmFetcher,
+ lg LocalChartGenerator,
+) (*InfraAppManager, error) {
return &InfraAppManager{
repoIO,
- nsCreator,
+ nsc,
+ hf,
+ lg,
}, nil
}
@@ -453,7 +564,7 @@
if err := m.repoIO.Pull(); err != nil {
return ReleaseResources{}, err
}
- if err := m.nsCreator.Create(namespace); err != nil {
+ if err := m.nsc.Create(namespace); err != nil {
return ReleaseResources{}, err
}
infra, err := m.Config()
@@ -465,14 +576,34 @@
RepoAddr: m.repoIO.FullAddress(),
AppDir: appDir,
}
- rendered, err := app.Render(release, infra, values)
+ rendered, err := app.Render(release, infra, values, nil)
+ if err != nil {
+ return ReleaseResources{}, err
+ }
+ var localCharts map[string]helmv2.HelmChartTemplateSpec
+ if err := m.repoIO.Do(func(rfs soft.RepoFS) (string, error) {
+ charts, err := pullHelmCharts(m.hf, rendered.HelmCharts, rfs, "/helm-charts")
+ if err != nil {
+ return "", err
+ }
+ localCharts = generateLocalCharts(m.lg, charts)
+ return "pull helm charts", nil
+ }); err != nil {
+ return ReleaseResources{}, err
+ }
+ rendered, err = app.Render(release, infra, values, localCharts)
if err != nil {
return ReleaseResources{}, err
}
return installApp(m.repoIO, appDir, rendered.Name, rendered.Config, rendered.Ports, rendered.Resources, rendered.Data)
}
-func (m *InfraAppManager) Update(app InfraApp, instanceId string, values map[string]any, opts ...soft.DoOption) (ReleaseResources, error) {
+// TODO(gio): take app configuration from the repo
+func (m *InfraAppManager) Update(
+ instanceId string,
+ values map[string]any,
+ opts ...InstallOption,
+) (ReleaseResources, error) {
if err := m.repoIO.Pull(); err != nil {
return ReleaseResources{}, err
}
@@ -481,20 +612,81 @@
return ReleaseResources{}, err
}
instanceDir := filepath.Join("/infrastructure", instanceId)
+ appCfg, err := GetCueAppData(m.repoIO, instanceDir)
+ if err != nil {
+ return ReleaseResources{}, err
+ }
+ app, err := NewCueInfraApp(appCfg)
+ if err != nil {
+ return ReleaseResources{}, err
+ }
instanceConfigPath := filepath.Join(instanceDir, "config.json")
config, err := m.appConfig(instanceConfigPath)
if err != nil {
return ReleaseResources{}, err
}
- release := Release{
- AppInstanceId: instanceId,
- Namespace: config.Release.Namespace,
- RepoAddr: m.repoIO.FullAddress(),
- AppDir: instanceDir,
+ localCharts, err := extractLocalCharts(m.repoIO, filepath.Join(instanceDir, "rendered.json"))
+ if err != nil {
+ return ReleaseResources{}, err
}
- rendered, err := app.Render(release, env, values)
+ rendered, err := app.Render(config.Release, env, values, localCharts)
if err != nil {
return ReleaseResources{}, err
}
return installApp(m.repoIO, instanceDir, rendered.Name, rendered.Config, rendered.Ports, rendered.Resources, rendered.Data, opts...)
}
+
+func pullHelmCharts(hf HelmFetcher, charts HelmCharts, rfs soft.RepoFS, root string) (map[string]string, error) {
+ ret := make(map[string]string)
+ for name, chart := range charts.Git {
+ chartRoot := filepath.Join(root, name)
+ ret[name] = chartRoot
+ if err := hf.Pull(chart, rfs, chartRoot); err != nil {
+ return nil, err
+ }
+ }
+ return ret, nil
+}
+
+func generateLocalCharts(g LocalChartGenerator, charts map[string]string) map[string]helmv2.HelmChartTemplateSpec {
+ ret := make(map[string]helmv2.HelmChartTemplateSpec)
+ for name, path := range charts {
+ ret[name] = g.Generate(path)
+ }
+ return ret
+}
+
+func pullContainerImages(appName string, imgs map[string]ContainerImage, registry, namespace string, jc JobCreator) error {
+ for _, img := range imgs {
+ name := fmt.Sprintf("copy-image-%s-%s-%s-%s", appName, img.Repository, img.Name, img.Tag)
+ if err := jc.Create(name, namespace, "giolekva/skopeo:latest", []string{
+ "skopeo",
+ "--insecure-policy",
+ "copy",
+ "--dest-tls-verify=false", // TODO(gio): enable
+ "--multi-arch=all",
+ fmt.Sprintf("docker://%s/%s/%s:%s", img.Registry, img.Repository, img.Name, img.Tag),
+ fmt.Sprintf("docker://%s/%s/%s:%s", registry, img.Repository, img.Name, img.Tag),
+ }); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+type renderedInstance struct {
+ LocalCharts map[string]helmv2.HelmChartTemplateSpec `json:"localCharts"`
+}
+
+func extractLocalCharts(fs soft.RepoFS, path string) (map[string]helmv2.HelmChartTemplateSpec, error) {
+ r, err := fs.Reader(path)
+ if err != nil {
+ return nil, err
+ }
+ defer r.Close()
+ var cfg renderedInstance
+ if err := json.NewDecoder(r).Decode(&cfg); err != nil {
+ return nil, err
+ }
+ return cfg.LocalCharts, nil
+}
diff --git a/core/installer/app_test.go b/core/installer/app_test.go
index a646425..74dfbdc 100644
--- a/core/installer/app_test.go
+++ b/core/installer/app_test.go
@@ -2,8 +2,13 @@
import (
_ "embed"
+ "fmt"
"net"
"testing"
+
+ "github.com/giolekva/pcloud/core/installer/soft"
+
+ "github.com/go-git/go-billy/v5/memfs"
)
var env = EnvConfig{
@@ -46,7 +51,7 @@
"groups": "a,b",
},
}
- rendered, err := a.Render(release, env, values)
+ rendered, err := a.Render(release, env, values, nil)
if err != nil {
t.Fatal(err)
}
@@ -76,7 +81,7 @@
"enabled": false,
},
}
- rendered, err := a.Render(release, env, values)
+ rendered, err := a.Render(release, env, values, nil)
if err != nil {
t.Fatal(err)
}
@@ -101,7 +106,7 @@
values := map[string]any{
"authGroups": "foo,bar",
}
- rendered, err := a.Render(release, env, values)
+ rendered, err := a.Render(release, env, values, nil)
if err != nil {
t.Fatal(err)
}
@@ -131,7 +136,7 @@
},
"sshPort": 22,
}
- rendered, err := a.Render(release, env, values)
+ rendered, err := a.Render(release, env, values, nil)
if err != nil {
t.Fatal(err)
}
@@ -156,7 +161,7 @@
"subdomain": "jenkins",
"network": "Private",
}
- rendered, err := a.Render(release, env, values)
+ rendered, err := a.Render(release, env, values, nil)
if err != nil {
t.Fatal(err)
}
@@ -186,7 +191,7 @@
values := map[string]any{
"sshPrivateKey": "private",
}
- rendered, err := a.Render(release, infra, values)
+ rendered, err := a.Render(release, infra, values, nil)
if err != nil {
t.Fatal(err)
}
@@ -215,7 +220,7 @@
},
"sshPrivateKey": "private",
}
- rendered, err := a.Render(release, env, values)
+ rendered, err := a.Render(release, env, values, nil)
if err != nil {
t.Fatal(err)
}
@@ -248,18 +253,40 @@
"groups": "a,b",
},
}
- rendered, err := app.Render(release, env, values)
+ rendered, err := app.Render(release, env, values, nil)
if err != nil {
t.Fatal(err)
}
for _, r := range rendered.Resources {
t.Log(string(r))
}
+ for _, r := range rendered.HelmCharts.Git {
+ t.Log(fmt.Sprintf("%+v\n", r))
+ }
for _, r := range rendered.Data {
t.Log(string(r))
}
}
+func TestPullGitHelmCharts(t *testing.T) {
+ charts := HelmCharts{
+ Git: map[string]HelmChartGitRepo{
+ "rpuppy": HelmChartGitRepo{
+ Address: "https://code.v1.dodo.cloud/pcloud",
+ Branch: "main",
+ Path: "charts/rpuppy",
+ },
+ },
+ }
+ fs := soft.NewBillyRepoFS(memfs.New())
+ hf := NewGitHelmFetcher()
+ pulled, err := pullHelmCharts(hf, charts, fs, "/helm-charts")
+ if err != nil {
+ t.Fatal(err)
+ }
+ fmt.Println(pulled)
+}
+
func TestDNSGateway(t *testing.T) {
contents, err := valuesTmpls.ReadFile("values-tmpl/dns-gateway.cue")
if err != nil {
@@ -288,7 +315,7 @@
values := map[string]any{
"servers": []EnvDNS{EnvDNS{"v1.dodo.cloud", "10.0.1.2"}},
}
- rendered, err := app.Render(release, infra, values)
+ rendered, err := app.Render(release, infra, values, nil)
if err != nil {
t.Fatal(err)
}
@@ -300,7 +327,7 @@
}
}
-//go:embed testapp.cue
+//go:embed app_configs/testapp.cue
var testAppCue []byte
type appInput struct {
diff --git a/core/installer/bootstrapper.go b/core/installer/bootstrapper.go
index c581f42..4b72556 100644
--- a/core/installer/bootstrapper.go
+++ b/core/installer/bootstrapper.go
@@ -90,7 +90,9 @@
fmt.Println("Failed to get config repo")
return err
}
- mgr, err := NewInfraAppManager(repoIO, b.ns)
+ hf := NewGitHelmFetcher()
+ lg := NewInfraLocalChartGenerator()
+ mgr, err := NewInfraAppManager(repoIO, b.ns, hf, lg)
if err != nil {
return err
}
diff --git a/core/installer/charts.go b/core/installer/charts.go
new file mode 100644
index 0000000..5f98d2e
--- /dev/null
+++ b/core/installer/charts.go
@@ -0,0 +1,36 @@
+package installer
+
+import (
+ "strings"
+
+ helmv2 "github.com/fluxcd/helm-controller/api/v2"
+)
+
+type LocalChartGenerator interface {
+ Generate(path string) helmv2.HelmChartTemplateSpec
+}
+
+type GitRepositoryLocalChartGenerator struct {
+ Name string
+ Namespace string
+}
+
+func (g GitRepositoryLocalChartGenerator) Generate(path string) helmv2.HelmChartTemplateSpec {
+ p, _ := strings.CutPrefix(path, "/")
+ return helmv2.HelmChartTemplateSpec{
+ Chart: p,
+ SourceRef: helmv2.CrossNamespaceObjectReference{
+ Kind: "GitRepository",
+ Name: g.Name,
+ Namespace: g.Namespace,
+ },
+ }
+}
+
+type InfraLocalChartGenerator struct {
+ GitRepositoryLocalChartGenerator
+}
+
+func NewInfraLocalChartGenerator() InfraLocalChartGenerator {
+ return InfraLocalChartGenerator{GitRepositoryLocalChartGenerator{"dodo-flux", "dodo-flux"}}
+}
diff --git a/core/installer/cmd/app_manager.go b/core/installer/cmd/app_manager.go
index 8cffeba..33f122d 100644
--- a/core/installer/cmd/app_manager.go
+++ b/core/installer/cmd/app_manager.go
@@ -76,11 +76,16 @@
if err != nil {
return err
}
- kube, err := newNSCreator()
+ nsc, err := newNSCreator()
if err != nil {
return err
}
- m, err := installer.NewAppManager(repoIO, kube, "/apps")
+ jc, err := newJobCreator()
+ if err != nil {
+ return err
+ }
+ hf := installer.NewGitHelmFetcher()
+ m, err := installer.NewAppManager(repoIO, nsc, jc, hf, "/apps")
if err != nil {
return err
}
diff --git a/core/installer/cmd/dodo_app.go b/core/installer/cmd/dodo_app.go
index d9f9a69..c45cd35 100644
--- a/core/installer/cmd/dodo_app.go
+++ b/core/installer/cmd/dodo_app.go
@@ -85,6 +85,10 @@
if err != nil {
return err
}
+ jc, err := newJobCreator()
+ if err != nil {
+ return err
+ }
if err := softClient.AddRepository("app"); err == nil {
repo, err := softClient.GetRepo("app")
if err != nil {
@@ -93,7 +97,7 @@
if err := initRepo(repo); err != nil {
return err
}
- if err := welcome.UpdateDodoApp(softClient, dodoAppFlags.namespace, string(sshKey), &env); err != nil {
+ if err := welcome.UpdateDodoApp(softClient, dodoAppFlags.namespace, string(sshKey), jc, &env); err != nil {
return err
}
if err := softClient.AddWebhook("app", fmt.Sprintf("http://%s/update", dodoAppFlags.self), "--active=true", "--events=push", "--content-type=json"); err != nil {
@@ -102,7 +106,7 @@
} else if !errors.Is(err, soft.ErrorAlreadyExists) {
return err
}
- s := welcome.NewDodoAppServer(dodoAppFlags.port, string(sshKey), softClient, dodoAppFlags.namespace, env)
+ s := welcome.NewDodoAppServer(dodoAppFlags.port, string(sshKey), softClient, dodoAppFlags.namespace, jc, env)
return s.Start()
}
diff --git a/core/installer/cmd/env_manager.go b/core/installer/cmd/env_manager.go
index aa33de8..c0ea8e8 100644
--- a/core/installer/cmd/env_manager.go
+++ b/core/installer/cmd/env_manager.go
@@ -72,6 +72,11 @@
if err != nil {
return err
}
+ jc, err := newJobCreator()
+ if err != nil {
+ return err
+ }
+ hf := installer.NewGitHelmFetcher()
dnsFetcher, err := newZoneFetcher()
if err != nil {
return err
@@ -83,6 +88,8 @@
repoIO,
repoClient,
nsCreator,
+ jc,
+ hf,
dnsFetcher,
installer.NewFixedLengthRandomNameGenerator(4),
httpClient,
diff --git a/core/installer/cmd/kube.go b/core/installer/cmd/kube.go
index 4c6ab59..f31ad8f 100644
--- a/core/installer/cmd/kube.go
+++ b/core/installer/cmd/kube.go
@@ -15,3 +15,11 @@
func newHelmReleaseMonitor() (installer.HelmReleaseMonitor, error) {
return installer.NewHelmReleaseMonitor(rootFlags.kubeConfig)
}
+
+func newJobCreator() (installer.JobCreator, error) {
+ clientset, err := installer.NewKubeConfig(rootFlags.kubeConfig)
+ if err != nil {
+ return nil, err
+ }
+ return installer.NewJobCreator(clientset.BatchV1()), nil
+}
diff --git a/core/installer/cmd/launcher.go b/core/installer/cmd/launcher.go
index 6a8ced5..0af1d1e 100644
--- a/core/installer/cmd/launcher.go
+++ b/core/installer/cmd/launcher.go
@@ -74,7 +74,7 @@
if err != nil {
return err
}
- appManager, err := installer.NewAppManager(repoIO, nil, "/apps")
+ appManager, err := installer.NewAppManager(repoIO, nil, nil, nil, "/apps")
if err != nil {
return err
}
diff --git a/core/installer/cmd/rewrite.go b/core/installer/cmd/rewrite.go
index c4bc7a8..3ebb390 100644
--- a/core/installer/cmd/rewrite.go
+++ b/core/installer/cmd/rewrite.go
@@ -61,7 +61,8 @@
}
log.Println("Creating repository")
r := installer.NewInMemoryAppRepository(installer.CreateAllApps())
- mgr, err := installer.NewAppManager(repoIO, nil, "/apps")
+ hf := installer.NewGitHelmFetcher()
+ mgr, err := installer.NewAppManager(repoIO, nil, nil, hf, "/apps")
if err != nil {
return err
}
@@ -84,7 +85,14 @@
return err
}
v := inst.InputToValues(app.Schema())
- if _, err := mgr.Update(app, inst.Id, v, soft.WithNoCommit()); err != nil {
+ if _, err := mgr.Install(
+ app,
+ inst.Id,
+ inst.Release.AppDir,
+ inst.Release.Namespace,
+ v,
+ installer.WithNoPublish(),
+ ); err != nil {
return err
}
}
diff --git a/core/installer/cmd/welcome.go b/core/installer/cmd/welcome.go
index 64820e6..db1ce4a 100644
--- a/core/installer/cmd/welcome.go
+++ b/core/installer/cmd/welcome.go
@@ -3,6 +3,7 @@
import (
"os"
+ "github.com/giolekva/pcloud/core/installer"
"github.com/giolekva/pcloud/core/installer/soft"
"github.com/giolekva/pcloud/core/installer/welcome"
"github.com/spf13/cobra"
@@ -91,6 +92,7 @@
welcomeFlags.port,
repoIO,
nsCreator,
+ installer.NewGitHelmFetcher(),
welcomeFlags.createAccountAddr,
welcomeFlags.loginAddr,
welcomeFlags.membershipsInitAddr,
diff --git a/core/installer/copy-image.yaml b/core/installer/copy-image.yaml
new file mode 100644
index 0000000..a5c27ef
--- /dev/null
+++ b/core/installer/copy-image.yaml
@@ -0,0 +1,26 @@
+apiVersion: batch/v1
+kind: Job
+metadata:
+ name: copy-image-{{ .Name }}
+ namespace: {{ .Namespace }}
+ # name: copy-image # -{{ .Name }}
+ # namespace: default # {{ .Namespace }}
+spec:
+ template:
+ spec:
+ containers:
+ - name: copy
+ image: giolekva/skopeo:amd64
+ imagePullPolicy: Always
+ command:
+ - skopeo
+ - --insecure-policy
+ - copy
+ - --dest-tls-verify=false # TODO(gio): enable
+ - --multi-arch=all
+ - {{ .From }}
+ - {{ .To }}
+ # - docker://docker.io/giolekva/skopeo:latest # {{ .From }}
+ # - docker://zot.p.v1.dodo.cloud/giolekva/skopeo:test # {{ .To }}
+ restartPolicy: Never
+ backoffLimit: 4
diff --git a/core/installer/derived.go b/core/installer/derived.go
index c3483cf..f623392 100644
--- a/core/installer/derived.go
+++ b/core/installer/derived.go
@@ -10,6 +10,7 @@
Namespace string `json:"namespace"`
RepoAddr string `json:"repoAddr"`
AppDir string `json:"appDir"`
+ ImageRegistry string `json:"imageRegistry,omitempty"`
}
type Network struct {
diff --git a/core/installer/go.mod b/core/installer/go.mod
index a17907f..73dd5bb 100644
--- a/core/installer/go.mod
+++ b/core/installer/go.mod
@@ -2,7 +2,9 @@
replace github.com/giolekva/pcloud/installer => /Users/lekva/dev/src/pcloud/core/installer
-go 1.21
+go 1.22.0
+
+toolchain go1.22.3
// toolchain go1.21.5
@@ -11,6 +13,7 @@
github.com/Masterminds/sprig/v3 v3.2.3
github.com/cenkalti/backoff/v4 v4.3.0
github.com/charmbracelet/keygen v0.5.0
+ github.com/fluxcd/helm-controller/api v1.0.1
github.com/go-git/go-billy/v5 v5.5.0
github.com/go-git/go-git/v5 v5.12.0
github.com/gomarkdown/markdown v0.0.0-20240328165702-4d01890c35c0
@@ -22,9 +25,9 @@
golang.org/x/crypto v0.22.0
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8
helm.sh/helm/v3 v3.14.3
- k8s.io/api v0.29.3
- k8s.io/apimachinery v0.29.3
- k8s.io/client-go v0.29.3
+ k8s.io/api v0.30.0
+ k8s.io/apimachinery v0.30.0
+ k8s.io/client-go v0.30.0
sigs.k8s.io/yaml v1.4.0
)
@@ -65,6 +68,8 @@
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect
github.com/fatih/color v1.16.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
+ github.com/fluxcd/pkg/apis/kustomize v1.5.0 // indirect
+ github.com/fluxcd/pkg/apis/meta v1.5.0 // indirect
github.com/go-errors/errors v1.5.1 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-gorp/gorp/v3 v3.1.0 // indirect
@@ -116,8 +121,6 @@
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
- github.com/onsi/ginkgo/v2 v2.14.0 // indirect
- github.com/onsi/gomega v1.30.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
@@ -150,7 +153,7 @@
go.opentelemetry.io/otel/trace v1.24.0 // indirect
go.starlark.net v0.0.0-20240329153429-e6e8e7ce1b7a // indirect
golang.org/x/mod v0.16.0 // indirect
- golang.org/x/net v0.23.0 // indirect
+ golang.org/x/net v0.24.0 // indirect
golang.org/x/oauth2 v0.18.0 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/sys v0.19.0 // indirect
@@ -167,15 +170,16 @@
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
- k8s.io/apiextensions-apiserver v0.29.3 // indirect
- k8s.io/apiserver v0.29.3 // indirect
+ k8s.io/apiextensions-apiserver v0.30.0 // indirect
+ k8s.io/apiserver v0.30.0 // indirect
k8s.io/cli-runtime v0.29.3 // indirect
- k8s.io/component-base v0.29.3 // indirect
+ k8s.io/component-base v0.30.0 // indirect
k8s.io/klog/v2 v2.120.1 // indirect
k8s.io/kube-openapi v0.0.0-20240403164606-bc84c2ddaf99 // indirect
k8s.io/kubectl v0.29.3 // indirect
k8s.io/utils v0.0.0-20240310230437-4693a0247e57 // indirect
oras.land/oras-go v1.2.5 // indirect
+ sigs.k8s.io/controller-runtime v0.18.1 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/kustomize/api v0.16.0 // indirect
sigs.k8s.io/kustomize/kyaml v0.16.0 // indirect
diff --git a/core/installer/go.sum b/core/installer/go.sum
index 21eb60a..e0033a2 100644
--- a/core/installer/go.sum
+++ b/core/installer/go.sum
@@ -122,6 +122,12 @@
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
+github.com/fluxcd/helm-controller/api v1.0.1 h1:Gn9qEVuif6D5+gHmVwTEZkR4+nmLOcOhKx4Sw2gL2EA=
+github.com/fluxcd/helm-controller/api v1.0.1/go.mod h1:/6AD5a2qjo/ttxVM8GR33syLZwqigta60DCLdy8GrME=
+github.com/fluxcd/pkg/apis/kustomize v1.5.0 h1:ah4sfqccnio+/5Edz/tVz6LetFhiBoDzXAElj6fFCzU=
+github.com/fluxcd/pkg/apis/kustomize v1.5.0/go.mod h1:nEzhnhHafhWOUUV8VMFLojUOH+HHDEsL75y54mt/c30=
+github.com/fluxcd/pkg/apis/meta v1.5.0 h1:/G82d2Az5D9op3F+wJUpD8jw/eTV0suM6P7+cSURoUM=
+github.com/fluxcd/pkg/apis/meta v1.5.0/go.mod h1:Y3u7JomuuKtr5fvP1Iji2/50FdRe5GcBug2jawNVkdM=
github.com/foxcpp/go-mockdns v1.0.0 h1:7jBqxd3WDWwi/6WhDvacvH1XsN3rOLXyHM1uhvIx6FI=
github.com/foxcpp/go-mockdns v1.0.0/go.mod h1:lgRN6+KxQBawyIghpnl5CezHFGS9VLzvtVlwxvzXTQ4=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
@@ -310,10 +316,10 @@
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
-github.com/onsi/ginkgo/v2 v2.14.0 h1:vSmGj2Z5YPb9JwCWT6z6ihcUvDhuXLc3sJiqd3jMKAY=
-github.com/onsi/ginkgo/v2 v2.14.0/go.mod h1:JkUdW7JkN0V6rFvsHcJ478egV3XH9NxpD27Hal/PhZw=
-github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
-github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
+github.com/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8=
+github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs=
+github.com/onsi/gomega v1.32.0 h1:JRYU78fJ1LPxlckP6Txi/EYqJvjtMrDC04/MM5XRHPk=
+github.com/onsi/gomega v1.32.0/go.mod h1:a4x4gW6Pz2yK1MAmvluYme5lvYTn61afQ2ETw/8n4Lg=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
@@ -456,8 +462,8 @@
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
-golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
-golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
+golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
+golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI=
golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -556,20 +562,20 @@
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
helm.sh/helm/v3 v3.14.3 h1:HmvRJlwyyt9HjgmAuxHbHv3PhMz9ir/XNWHyXfmnOP4=
helm.sh/helm/v3 v3.14.3/go.mod h1:v6myVbyseSBJTzhmeE39UcPLNv6cQK6qss3dvgAySaE=
-k8s.io/api v0.29.3 h1:2ORfZ7+bGC3YJqGpV0KSDDEVf8hdGQ6A03/50vj8pmw=
-k8s.io/api v0.29.3/go.mod h1:y2yg2NTyHUUkIoTC+phinTnEa3KFM6RZ3szxt014a80=
-k8s.io/apiextensions-apiserver v0.29.3 h1:9HF+EtZaVpFjStakF4yVufnXGPRppWFEQ87qnO91YeI=
-k8s.io/apiextensions-apiserver v0.29.3/go.mod h1:po0XiY5scnpJfFizNGo6puNU6Fq6D70UJY2Cb2KwAVc=
-k8s.io/apimachinery v0.29.3 h1:2tbx+5L7RNvqJjn7RIuIKu9XTsIZ9Z5wX2G22XAa5EU=
-k8s.io/apimachinery v0.29.3/go.mod h1:hx/S4V2PNW4OMg3WizRrHutyB5la0iCUbZym+W0EQIU=
-k8s.io/apiserver v0.29.3 h1:xR7ELlJ/BZSr2n4CnD3lfA4gzFivh0wwfNfz9L0WZcE=
-k8s.io/apiserver v0.29.3/go.mod h1:hrvXlwfRulbMbBgmWRQlFru2b/JySDpmzvQwwk4GUOs=
+k8s.io/api v0.30.0 h1:siWhRq7cNjy2iHssOB9SCGNCl2spiF1dO3dABqZ8niA=
+k8s.io/api v0.30.0/go.mod h1:OPlaYhoHs8EQ1ql0R/TsUgaRPhpKNxIMrKQfWUp8QSE=
+k8s.io/apiextensions-apiserver v0.30.0 h1:jcZFKMqnICJfRxTgnC4E+Hpcq8UEhT8B2lhBcQ+6uAs=
+k8s.io/apiextensions-apiserver v0.30.0/go.mod h1:N9ogQFGcrbWqAY9p2mUAL5mGxsLqwgtUce127VtRX5Y=
+k8s.io/apimachinery v0.30.0 h1:qxVPsyDM5XS96NIh9Oj6LavoVFYff/Pon9cZeDIkHHA=
+k8s.io/apimachinery v0.30.0/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
+k8s.io/apiserver v0.30.0 h1:QCec+U72tMQ+9tR6A0sMBB5Vh6ImCEkoKkTDRABWq6M=
+k8s.io/apiserver v0.30.0/go.mod h1:smOIBq8t0MbKZi7O7SyIpjPsiKJ8qa+llcFCluKyqiY=
k8s.io/cli-runtime v0.29.3 h1:r68rephmmytoywkw2MyJ+CxjpasJDQY7AGc3XY2iv1k=
k8s.io/cli-runtime v0.29.3/go.mod h1:aqVUsk86/RhaGJwDhHXH0jcdqBrgdF3bZWk4Z9D4mkM=
-k8s.io/client-go v0.29.3 h1:R/zaZbEAxqComZ9FHeQwOh3Y1ZUs7FaHKZdQtIc2WZg=
-k8s.io/client-go v0.29.3/go.mod h1:tkDisCvgPfiRpxGnOORfkljmS+UrW+WtXAy2fTvXJB0=
-k8s.io/component-base v0.29.3 h1:Oq9/nddUxlnrCuuR2K/jp6aflVvc0uDvxMzAWxnGzAo=
-k8s.io/component-base v0.29.3/go.mod h1:Yuj33XXjuOk2BAaHsIGHhCKZQAgYKhqIxIjIr2UXYio=
+k8s.io/client-go v0.30.0 h1:sB1AGGlhY/o7KCyCEQ0bPWzYDL0pwOZO4vAtTSh/gJQ=
+k8s.io/client-go v0.30.0/go.mod h1:g7li5O5256qe6TYdAMyX/otJqMhIiGgTapdLchhmOaY=
+k8s.io/component-base v0.30.0 h1:cj6bp38g0ainlfYtaOQuRELh5KSYjhKxM+io7AUIk4o=
+k8s.io/component-base v0.30.0/go.mod h1:V9x/0ePFNaKeKYA3bOvIbrNoluTSG+fSJKjLdjOoeXQ=
k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20240403164606-bc84c2ddaf99 h1:w6nThEmGo9zcL+xH1Tu6pjxJ3K1jXFW+V0u4peqN8ks=
@@ -580,6 +586,8 @@
k8s.io/utils v0.0.0-20240310230437-4693a0247e57/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
oras.land/oras-go v1.2.5 h1:XpYuAwAb0DfQsunIyMfeET92emK8km3W4yEzZvUbsTo=
oras.land/oras-go v1.2.5/go.mod h1:PuAwRShRZCsZb7g8Ar3jKKQR/2A/qN+pkYxIOd/FAoo=
+sigs.k8s.io/controller-runtime v0.18.1 h1:RpWbigmuiylbxOCLy0tGnq1cU1qWPwNIQzoJk+QeJx4=
+sigs.k8s.io/controller-runtime v0.18.1/go.mod h1:tuAt1+wbVsXIT8lPtk5RURxqAnq7xkpv2Mhttslg7Hw=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/kustomize/api v0.16.0 h1:/zAR4FOQDCkgSDmVzV2uiFbuy9bhu3jEzthrHCuvm1g=
diff --git a/core/installer/helm.go b/core/installer/helm.go
index 1f805b9..2c13b50 100644
--- a/core/installer/helm.go
+++ b/core/installer/helm.go
@@ -2,7 +2,17 @@
import (
"fmt"
+ "io"
+ "io/fs"
+ "path/filepath"
+ "github.com/giolekva/pcloud/core/installer/soft"
+
+ "github.com/go-git/go-billy/v5/memfs"
+ "github.com/go-git/go-billy/v5/util"
+ "github.com/go-git/go-git/v5"
+ "github.com/go-git/go-git/v5/plumbing"
+ "github.com/go-git/go-git/v5/storage/memory"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/kube"
)
@@ -30,3 +40,49 @@
}
return config, nil
}
+
+type HelmFetcher interface {
+ Pull(chart HelmChartGitRepo, rfs soft.RepoFS, root string) error
+}
+
+type gitHelmFetcher struct{}
+
+func NewGitHelmFetcher() *gitHelmFetcher {
+ return &gitHelmFetcher{}
+}
+
+func (f *gitHelmFetcher) Pull(chart HelmChartGitRepo, rfs soft.RepoFS, root string) error {
+ ref := fmt.Sprintf("refs/heads/%s", chart.Branch)
+ r, err := git.Clone(memory.NewStorage(), memfs.New(), &git.CloneOptions{
+ URL: chart.Address,
+ ReferenceName: plumbing.ReferenceName(ref),
+ SingleBranch: true,
+ Depth: 1,
+ })
+ if err != nil {
+ return err
+ }
+ wt, err := r.Worktree()
+ if err != nil {
+ return err
+ }
+ wtFS, err := wt.Filesystem.Chroot(chart.Path)
+ if err != nil {
+ return err
+ }
+ return util.Walk(wtFS, "/", func(path string, info fs.FileInfo, err error) error {
+ if info.IsDir() {
+ return nil
+ }
+ inp, err := wtFS.Open(path)
+ if err != nil {
+ return err
+ }
+ out, err := rfs.Writer(filepath.Join(root, path))
+ if err != nil {
+ return err
+ }
+ _, err = io.Copy(out, inp)
+ return err
+ })
+}
diff --git a/core/installer/job.go b/core/installer/job.go
new file mode 100644
index 0000000..265510c
--- /dev/null
+++ b/core/installer/job.go
@@ -0,0 +1,63 @@
+package installer
+
+import (
+ "context"
+
+ batchv1 "k8s.io/api/batch/v1"
+ corev1 "k8s.io/api/core/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/client-go/kubernetes/typed/batch/v1"
+)
+
+type JobCreator interface {
+ Create(name, namespace string, image string, cmd []string) error
+}
+
+type noOpJobCreator struct{}
+
+func (c noOpJobCreator) Create(name, namespace string, image string, cmd []string) error {
+ return nil
+}
+
+func NewNoOpJobCreator() noOpJobCreator {
+ return noOpJobCreator{}
+}
+
+type realJobCreator struct {
+ v1.BatchV1Interface
+}
+
+func NewJobCreator(batch v1.BatchV1Interface) *realJobCreator {
+ return &realJobCreator{batch}
+}
+
+var onFailure corev1.RestartPolicy = "OnFailure"
+
+func (c *realJobCreator) Create(name, namespace string, image string, cmd []string) error {
+ _, err := c.Jobs(namespace).Create(context.Background(), &batchv1.Job{
+ TypeMeta: metav1.TypeMeta{
+ Kind: "Job",
+ APIVersion: "batch/v1",
+ },
+ ObjectMeta: metav1.ObjectMeta{
+ Name: name,
+ Namespace: namespace,
+ },
+ Spec: batchv1.JobSpec{
+ Template: corev1.PodTemplateSpec{
+ Spec: corev1.PodSpec{
+ Containers: []corev1.Container{
+ corev1.Container{
+ Name: "job",
+ Image: image,
+ ImagePullPolicy: "Always",
+ Command: cmd,
+ },
+ },
+ RestartPolicy: onFailure,
+ },
+ },
+ },
+ }, metav1.CreateOptions{})
+ return err
+}
diff --git a/core/installer/soft/repoio.go b/core/installer/soft/repoio.go
index b916d24..b50bcaa 100644
--- a/core/installer/soft/repoio.go
+++ b/core/installer/soft/repoio.go
@@ -30,6 +30,7 @@
Writer(path string) (io.WriteCloser, error)
CreateDir(path string) error
RemoveDir(path string) error
+ ListDir(path string) ([]os.FileInfo, error)
}
type DoFn func(r RepoFS) (string, error)
@@ -120,6 +121,10 @@
return nil
}
+func (r *repoFS) ListDir(path string) ([]os.FileInfo, error) {
+ return r.fs.ReadDir(path)
+}
+
type repoIO struct {
*repoFS
repo *Repository
diff --git a/core/installer/tasks/dns.go b/core/installer/tasks/dns.go
index 0424cfe..7db2c3a 100644
--- a/core/installer/tasks/dns.go
+++ b/core/installer/tasks/dns.go
@@ -60,10 +60,6 @@
}
}
{
- app, err := installer.FindInfraApp(st.appsRepo, "dns-gateway")
- if err != nil {
- return err
- }
cfg, err := st.infraAppManager.FindInstance("dns-gateway")
if err != nil {
return err
@@ -84,7 +80,7 @@
env.Domain,
env.Network.DNSInClusterIP.String(),
})
- if _, err := st.infraAppManager.Update(app, "dns-gateway", map[string]any{
+ if _, err := st.infraAppManager.Update("dns-gateway", map[string]any{
"servers": servers,
}); err != nil {
return err
diff --git a/core/installer/tasks/env.go b/core/installer/tasks/env.go
index a38e5b1..20528a3 100644
--- a/core/installer/tasks/env.go
+++ b/core/installer/tasks/env.go
@@ -15,6 +15,8 @@
type state struct {
infoListener EnvInfoListener
nsCreator installer.NamespaceCreator
+ jc installer.JobCreator
+ hf installer.HelmFetcher
dnsFetcher installer.ZoneStatusFetcher
httpClient http.Client
dnsClient dns.Client
@@ -34,6 +36,8 @@
func NewCreateEnvTask(
env installer.EnvConfig,
nsCreator installer.NamespaceCreator,
+ jc installer.JobCreator,
+ hf installer.HelmFetcher,
dnsFetcher installer.ZoneStatusFetcher,
httpClient http.Client,
dnsClient dns.Client,
@@ -45,6 +49,8 @@
st := state{
infoListener: infoListener,
nsCreator: nsCreator,
+ jc: jc,
+ hf: hf,
dnsFetcher: dnsFetcher,
httpClient: httpClient,
dnsClient: dnsClient,
diff --git a/core/installer/tasks/infra.go b/core/installer/tasks/infra.go
index e71de44..6f02fa3 100644
--- a/core/installer/tasks/infra.go
+++ b/core/installer/tasks/infra.go
@@ -18,7 +18,7 @@
if err != nil {
return err
}
- appManager, err := installer.NewAppManager(r, st.nsCreator, "/apps")
+ appManager, err := installer.NewAppManager(r, st.nsCreator, st.jc, st.hf, "/apps")
if err != nil {
return err
}
@@ -57,31 +57,10 @@
if err := soft.WriteYaml(r, "config.yaml", env); err != nil {
return "", err
}
- out, err := r.Writer("pcloud-charts.yaml")
- if err != nil {
- return "", err
- }
- defer out.Close()
- _, err = fmt.Fprintf(out, `
-apiVersion: source.toolkit.fluxcd.io/v1
-kind: GitRepository
-metadata:
- name: pcloud
- namespace: %s
-spec:
- interval: 1m0s
- url: https://github.com/giolekva/pcloud
- ref:
- branch: main
-`, env.Id)
- if err != nil {
- return "", err
- }
rootKust, err := soft.ReadKustomization(r, "kustomization.yaml")
if err != nil {
return "", err
}
- rootKust.AddResources("pcloud-charts.yaml")
if err := soft.WriteYaml(r, "kustomization.yaml", rootKust); err != nil {
return "", err
}
diff --git a/core/installer/values-tmpl/appmanager.cue b/core/installer/values-tmpl/appmanager.cue
index 6e98d5e..7ce72e2 100644
--- a/core/installer/values-tmpl/appmanager.cue
+++ b/core/installer/values-tmpl/appmanager.cue
@@ -44,12 +44,10 @@
charts: {
appmanager: {
- chart: "charts/appmanager"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/appmanager"
}
}
diff --git a/core/installer/values-tmpl/cert-manager.cue b/core/installer/values-tmpl/cert-manager.cue
index 9f9b5d1..4b6154a 100644
--- a/core/installer/values-tmpl/cert-manager.cue
+++ b/core/installer/values-tmpl/cert-manager.cue
@@ -35,20 +35,16 @@
charts: {
certManager: {
- chart: "charts/cert-manager"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.pcloudEnvName
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/cert-manager"
}
dnsChallengeSolver: {
- chart: "charts/cert-manager-webhook-pcloud"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.pcloudEnvName
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/cert-manager-webhook-pcloud"
}
}
diff --git a/core/installer/values-tmpl/certificate-issuer-private.cue b/core/installer/values-tmpl/certificate-issuer-private.cue
index ee50b49..eef76d3 100644
--- a/core/installer/values-tmpl/certificate-issuer-private.cue
+++ b/core/installer/values-tmpl/certificate-issuer-private.cue
@@ -7,12 +7,10 @@
charts: {
"certificate-issuer-private": {
- chart: "charts/certificate-issuer-private"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ path: "charts/certificate-issuer-private"
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
}
}
diff --git a/core/installer/values-tmpl/certificate-issuer-public.cue b/core/installer/values-tmpl/certificate-issuer-public.cue
index 7a5d3ba..35242bf 100644
--- a/core/installer/values-tmpl/certificate-issuer-public.cue
+++ b/core/installer/values-tmpl/certificate-issuer-public.cue
@@ -7,12 +7,10 @@
charts: {
"certificate-issuer-public": {
- chart: "charts/certificate-issuer-public"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/certificate-issuer-public"
}
}
diff --git a/core/installer/values-tmpl/config-repo.cue b/core/installer/values-tmpl/config-repo.cue
index 8d6a52f..cff139e 100644
--- a/core/installer/values-tmpl/config-repo.cue
+++ b/core/installer/values-tmpl/config-repo.cue
@@ -18,12 +18,10 @@
charts: {
softserve: {
- chart: "charts/soft-serve"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.pcloudEnvName
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/soft-serve"
}
}
diff --git a/core/installer/values-tmpl/core-auth.cue b/core/installer/values-tmpl/core-auth.cue
index eb19493..e2f05c4 100644
--- a/core/installer/values-tmpl/core-auth.cue
+++ b/core/installer/values-tmpl/core-auth.cue
@@ -64,20 +64,16 @@
charts: {
auth: {
- chart: "charts/auth"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/auth"
}
postgres: {
- chart: "charts/postgresql"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/postgresql"
}
}
diff --git a/core/installer/values-tmpl/csi-driver-smb.cue b/core/installer/values-tmpl/csi-driver-smb.cue
index 6d93bbf..9dec7c2 100644
--- a/core/installer/values-tmpl/csi-driver-smb.cue
+++ b/core/installer/values-tmpl/csi-driver-smb.cue
@@ -30,12 +30,10 @@
charts: {
csiDriverSMB: {
- chart: "charts/csi-driver-smb"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.pcloudEnvName
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/csi-driver-smb"
}
}
diff --git a/core/installer/values-tmpl/dns-gateway.cue b/core/installer/values-tmpl/dns-gateway.cue
index 31b729c..739a4a9 100644
--- a/core/installer/values-tmpl/dns-gateway.cue
+++ b/core/installer/values-tmpl/dns-gateway.cue
@@ -21,12 +21,10 @@
charts: {
coredns: {
- chart: "charts/coredns"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.pcloudEnvName
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/coredns"
}
}
diff --git a/core/installer/values-tmpl/dodo-app.cue b/core/installer/values-tmpl/dodo-app.cue
index 5acc6db..db8989d 100644
--- a/core/installer/values-tmpl/dodo-app.cue
+++ b/core/installer/values-tmpl/dodo-app.cue
@@ -40,20 +40,16 @@
charts: {
softserve: {
- chart: "charts/soft-serve"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.pcloudEnvName
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/soft-serve"
}
dodoApp: {
- chart: "charts/dodo-app"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.pcloudEnvName
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/dodo-app"
}
}
diff --git a/core/installer/values-tmpl/env-dns.cue b/core/installer/values-tmpl/env-dns.cue
index 5c95a54..b4a80a9 100644
--- a/core/installer/values-tmpl/env-dns.cue
+++ b/core/installer/values-tmpl/env-dns.cue
@@ -27,44 +27,34 @@
charts: {
coredns: {
- chart: "charts/coredns"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/coredns"
}
api: {
- chart: "charts/dns-api"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/dns-api"
}
volume: {
- chart: "charts/volumes"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/volumes"
}
service: {
- chart: "charts/service"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/service"
}
ipAddressPool: {
- chart: "charts/metallb-ipaddresspool"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/metallb-ipaddresspool"
}
}
diff --git a/core/installer/values-tmpl/env-manager.cue b/core/installer/values-tmpl/env-manager.cue
index b93d918..43c22e8 100644
--- a/core/installer/values-tmpl/env-manager.cue
+++ b/core/installer/values-tmpl/env-manager.cue
@@ -23,12 +23,10 @@
charts: {
envManager: {
- chart: "charts/env-manager"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.pcloudEnvName
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/env-manager"
}
}
diff --git a/core/installer/values-tmpl/fluxcd-reconciler.cue b/core/installer/values-tmpl/fluxcd-reconciler.cue
index ebb6c4d..a4c2693 100644
--- a/core/installer/values-tmpl/fluxcd-reconciler.cue
+++ b/core/installer/values-tmpl/fluxcd-reconciler.cue
@@ -14,12 +14,10 @@
charts: {
fluxcdReconciler: {
- chart: "charts/fluxcd-reconciler"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.pcloudEnvName
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/fluxcd-reconciler"
}
}
diff --git a/core/installer/values-tmpl/gerrit.cue b/core/installer/values-tmpl/gerrit.cue
index 3cffb3b..529d0f3 100644
--- a/core/installer/values-tmpl/gerrit.cue
+++ b/core/installer/values-tmpl/gerrit.cue
@@ -51,44 +51,34 @@
charts: {
ingress: {
- chart: "charts/ingress"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/ingress"
}
volume: {
- chart: "charts/volumes"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/volumes"
}
gerrit: {
- chart: "charts/gerrit"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/gerrit"
}
oauth2Client: {
- chart: "charts/oauth2-client"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/oauth2-client"
}
resourceRenderer: {
- chart: "charts/resource-renderer"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/resource-renderer"
}
}
diff --git a/core/installer/values-tmpl/headscale-controller.cue b/core/installer/values-tmpl/headscale-controller.cue
index 86570ed..0abf6e4 100644
--- a/core/installer/values-tmpl/headscale-controller.cue
+++ b/core/installer/values-tmpl/headscale-controller.cue
@@ -21,12 +21,10 @@
charts: {
headscaleController: {
- chart: "charts/headscale-controller"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.pcloudEnvName
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/headscale-controller"
}
}
diff --git a/core/installer/values-tmpl/headscale-user.cue b/core/installer/values-tmpl/headscale-user.cue
index 893ca28..6ba12cb 100644
--- a/core/installer/values-tmpl/headscale-user.cue
+++ b/core/installer/values-tmpl/headscale-user.cue
@@ -8,16 +8,12 @@
name: "headscale-user"
namespace: "app-headscale"
-images: {}
-
charts: {
headscaleUser: {
- chart: "charts/headscale-user"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/headscale-user"
}
}
diff --git a/core/installer/values-tmpl/headscale.cue b/core/installer/values-tmpl/headscale.cue
index e0b8703..ec06383 100644
--- a/core/installer/values-tmpl/headscale.cue
+++ b/core/installer/values-tmpl/headscale.cue
@@ -24,20 +24,16 @@
charts: {
oauth2Client: {
- chart: "charts/oauth2-client"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/oauth2-client"
}
headscale: {
- chart: "charts/headscale"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/headscale"
}
}
diff --git a/core/installer/values-tmpl/hydra-maester.cue b/core/installer/values-tmpl/hydra-maester.cue
index 4f76f84..a48536b 100644
--- a/core/installer/values-tmpl/hydra-maester.cue
+++ b/core/installer/values-tmpl/hydra-maester.cue
@@ -14,12 +14,10 @@
charts: {
hydraMaester: {
- chart: "charts/hydra-maester"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.pcloudEnvName
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/hydra-maester"
}
}
diff --git a/core/installer/values-tmpl/ingress-public.cue b/core/installer/values-tmpl/ingress-public.cue
index 6823a7b..f0827e5 100644
--- a/core/installer/values-tmpl/ingress-public.cue
+++ b/core/installer/values-tmpl/ingress-public.cue
@@ -27,20 +27,16 @@
charts: {
ingressNginx: {
- chart: "charts/ingress-nginx"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.pcloudEnvName
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/ingress-nginx"
}
portAllocator: {
- chart: "charts/port-allocator"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.pcloudEnvName
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/port-allocator"
}
}
diff --git a/core/installer/values-tmpl/jellyfin.cue b/core/installer/values-tmpl/jellyfin.cue
index 2d5d45c..dd50adc 100644
--- a/core/installer/values-tmpl/jellyfin.cue
+++ b/core/installer/values-tmpl/jellyfin.cue
@@ -24,12 +24,10 @@
charts: {
jellyfin: {
- chart: "charts/jellyfin"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/jellyfin"
}
}
diff --git a/core/installer/values-tmpl/jenkins.cue b/core/installer/values-tmpl/jenkins.cue
index 673e544..5326c7a 100644
--- a/core/installer/values-tmpl/jenkins.cue
+++ b/core/installer/values-tmpl/jenkins.cue
@@ -37,28 +37,22 @@
charts: {
jenkins: {
- chart: "charts/jenkins"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/jenkins"
}
volume: {
- chart: "charts/volumes"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/volumes"
}
oauth2Client: {
- chart: "charts/oauth2-client"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/oauth2-client"
}
}
diff --git a/core/installer/values-tmpl/launcher.cue b/core/installer/values-tmpl/launcher.cue
index 8aabbd5..12e2246 100644
--- a/core/installer/values-tmpl/launcher.cue
+++ b/core/installer/values-tmpl/launcher.cue
@@ -41,12 +41,10 @@
charts: {
launcher: {
- chart: "charts/launcher"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/launcher"
}
}
diff --git a/core/installer/values-tmpl/matrix.cue b/core/installer/values-tmpl/matrix.cue
index 4b4ca06..37103b7 100644
--- a/core/installer/values-tmpl/matrix.cue
+++ b/core/installer/values-tmpl/matrix.cue
@@ -28,28 +28,22 @@
charts: {
oauth2Client: {
- chart: "charts/oauth2-client"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/oauth2-client"
}
matrix: {
- chart: "charts/matrix"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/matrix"
}
postgres: {
- chart: "charts/postgresql"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/postgresql"
}
}
diff --git a/core/installer/values-tmpl/memberships.cue b/core/installer/values-tmpl/memberships.cue
index c0ffaeb..9bf9b57 100644
--- a/core/installer/values-tmpl/memberships.cue
+++ b/core/installer/values-tmpl/memberships.cue
@@ -40,12 +40,10 @@
charts: {
memberships: {
- chart: "charts/memberships"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/memberships"
}
}
diff --git a/core/installer/values-tmpl/metallb-ipaddresspool.cue b/core/installer/values-tmpl/metallb-ipaddresspool.cue
index 603b4b3..d990a88 100644
--- a/core/installer/values-tmpl/metallb-ipaddresspool.cue
+++ b/core/installer/values-tmpl/metallb-ipaddresspool.cue
@@ -13,12 +13,10 @@
charts: {
metallbIPAddressPool: {
- chart: "charts/metallb-ipaddresspool"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.pcloudEnvName // TODO(gio): id ?
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/metallb-ipaddresspool"
}
}
diff --git a/core/installer/values-tmpl/open-project.cue b/core/installer/values-tmpl/open-project.cue
index 76f604b..8c2da74 100644
--- a/core/installer/values-tmpl/open-project.cue
+++ b/core/installer/values-tmpl/open-project.cue
@@ -42,28 +42,22 @@
charts: {
openProject: {
- chart: "charts/openproject"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/openproject"
}
volume: {
- chart: "charts/volumes"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ path: "charts/volumes"
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
}
postgres: {
- chart: "charts/postgresql"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/postgresql"
}
}
diff --git a/core/installer/values-tmpl/penpot.cue b/core/installer/values-tmpl/penpot.cue
index 4a8e66a..961cd30 100644
--- a/core/installer/values-tmpl/penpot.cue
+++ b/core/installer/values-tmpl/penpot.cue
@@ -41,28 +41,22 @@
charts: {
postgres: {
- chart: "charts/postgresql"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/postgresql"
}
oauth2Client: {
- chart: "charts/oauth2-client"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/oauth2-client"
}
penpot: {
- chart: "charts/penpot"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/penpot"
}
}
diff --git a/core/installer/values-tmpl/pihole.cue b/core/installer/values-tmpl/pihole.cue
index d1b9097..c5f740d 100644
--- a/core/installer/values-tmpl/pihole.cue
+++ b/core/installer/values-tmpl/pihole.cue
@@ -38,12 +38,10 @@
charts: {
pihole: {
- chart: "charts/pihole"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/pihole"
}
}
diff --git a/core/installer/values-tmpl/private-network.cue b/core/installer/values-tmpl/private-network.cue
index 65bfeaf..fe78f32 100644
--- a/core/installer/values-tmpl/private-network.cue
+++ b/core/installer/values-tmpl/private-network.cue
@@ -38,28 +38,22 @@
charts: {
"ingress-nginx": {
- chart: "charts/ingress-nginx"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.pcloudEnvName
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/ingress-nginx"
}
"tailscale-proxy": {
- chart: "charts/tailscale-proxy"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.pcloudEnvName
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/tailscale-proxy"
}
portAllocator: {
- chart: "charts/port-allocator"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/port-allocator"
}
}
diff --git a/core/installer/values-tmpl/qbittorrent.cue b/core/installer/values-tmpl/qbittorrent.cue
index 179a718..c4d7ca2 100644
--- a/core/installer/values-tmpl/qbittorrent.cue
+++ b/core/installer/values-tmpl/qbittorrent.cue
@@ -24,12 +24,10 @@
charts: {
qbittorrent: {
- chart: "charts/qbittorrent"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/qbittorrent"
}
}
diff --git a/core/installer/values-tmpl/resource-renderer-controller.cue b/core/installer/values-tmpl/resource-renderer-controller.cue
index e596ba3..6c1bcd2 100644
--- a/core/installer/values-tmpl/resource-renderer-controller.cue
+++ b/core/installer/values-tmpl/resource-renderer-controller.cue
@@ -21,12 +21,10 @@
charts: {
resourceRenderer: {
- chart: "charts/resource-renderer-controller"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.pcloudEnvName
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/resource-renderer-controller"
}
}
diff --git a/core/installer/values-tmpl/rpuppy.cue b/core/installer/values-tmpl/rpuppy.cue
index 05f40c7..0e40fe9 100644
--- a/core/installer/values-tmpl/rpuppy.cue
+++ b/core/installer/values-tmpl/rpuppy.cue
@@ -37,12 +37,10 @@
charts: {
rpuppy: {
- chart: "charts/rpuppy"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/rpuppy"
}
}
diff --git a/core/installer/values-tmpl/soft-serve.cue b/core/installer/values-tmpl/soft-serve.cue
index 1bae24f..e2e351b 100644
--- a/core/installer/values-tmpl/soft-serve.cue
+++ b/core/installer/values-tmpl/soft-serve.cue
@@ -25,12 +25,10 @@
charts: {
softserve: {
- chart: "charts/soft-serve"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/soft-serve"
}
}
diff --git a/core/installer/values-tmpl/url-shortener.cue b/core/installer/values-tmpl/url-shortener.cue
index 84a0edf..06ac7e1 100644
--- a/core/installer/values-tmpl/url-shortener.cue
+++ b/core/installer/values-tmpl/url-shortener.cue
@@ -38,12 +38,10 @@
charts: {
urlShortener: {
- chart: "charts/url-shortener"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/url-shortener"
}
}
diff --git a/core/installer/values-tmpl/vaultwarden.cue b/core/installer/values-tmpl/vaultwarden.cue
index fd2962c..7d11904 100644
--- a/core/installer/values-tmpl/vaultwarden.cue
+++ b/core/installer/values-tmpl/vaultwarden.cue
@@ -23,12 +23,10 @@
charts: {
vaultwarden: {
- chart: "charts/vaultwarden"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/vaultwarden"
}
}
diff --git a/core/installer/values-tmpl/welcome.cue b/core/installer/values-tmpl/welcome.cue
index 30c6980..2abd8b2 100644
--- a/core/installer/values-tmpl/welcome.cue
+++ b/core/installer/values-tmpl/welcome.cue
@@ -21,12 +21,10 @@
charts: {
welcome: {
- chart: "charts/welcome"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/welcome"
}
}
diff --git a/core/installer/values-tmpl/zot.cue b/core/installer/values-tmpl/zot.cue
index ac0674e..f83c671 100644
--- a/core/installer/values-tmpl/zot.cue
+++ b/core/installer/values-tmpl/zot.cue
@@ -41,20 +41,16 @@
charts: {
zot: {
- chart: "charts/zot"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/zot"
}
volume: {
- chart: "charts/volumes"
- sourceRef: {
- kind: "GitRepository"
- name: "pcloud"
- namespace: global.id
- }
+ kind: "GitRepository"
+ address: "https://github.com/giolekva/pcloud.git"
+ branch: "main"
+ path: "charts/volumes"
}
}
@@ -100,7 +96,7 @@
}
})
}
- persistnce: true
+ persistence: true
pvc: {
create: false
name: volumes.zot.name
diff --git a/core/installer/welcome/appmanager.go b/core/installer/welcome/appmanager.go
index 5da26ef..a4430b8 100644
--- a/core/installer/welcome/appmanager.go
+++ b/core/installer/welcome/appmanager.go
@@ -247,11 +247,6 @@
http.Error(w, "empty slug", http.StatusBadRequest)
return
}
- appConfig, err := s.m.AppConfig(slug)
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
contents, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
@@ -262,16 +257,11 @@
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
- a, err := installer.FindEnvApp(s.r, appConfig.AppId)
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
if _, ok := s.tasks[slug]; ok {
http.Error(w, "Update already in progress", http.StatusBadRequest)
return
}
- rr, err := s.m.Update(a, slug, values)
+ rr, err := s.m.Update(slug, values)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
@@ -452,7 +442,7 @@
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
- a, err := installer.FindEnvApp(s.r, instance.AppId)
+ a, err := s.m.GetInstanceApp(instance.Id)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
diff --git a/core/installer/welcome/dodo_app.go b/core/installer/welcome/dodo_app.go
index 5eb2f58..251a379 100644
--- a/core/installer/welcome/dodo_app.go
+++ b/core/installer/welcome/dodo_app.go
@@ -18,6 +18,7 @@
client soft.Client
namespace string
env installer.EnvConfig
+ jc installer.JobCreator
workers map[string]struct{}
}
@@ -26,6 +27,7 @@
sshKey string,
client soft.Client,
namespace string,
+ jc installer.JobCreator,
env installer.EnvConfig,
) *DodoAppServer {
return &DodoAppServer{
@@ -34,6 +36,7 @@
client,
namespace,
env,
+ jc,
map[string]struct{}{},
}
}
@@ -64,7 +67,7 @@
}
go func() {
time.Sleep(20 * time.Second)
- if err := UpdateDodoApp(s.client, s.namespace, s.sshKey, &s.env); err != nil {
+ if err := UpdateDodoApp(s.client, s.namespace, s.sshKey, s.jc, &s.env); err != nil {
fmt.Println(err)
}
}()
@@ -90,16 +93,17 @@
fmt.Printf("registered worker: %s\n", req.Address)
}
-func UpdateDodoApp(client soft.Client, namespace string, sshKey string, env *installer.EnvConfig) error {
+func UpdateDodoApp(client soft.Client, namespace string, sshKey string, jc installer.JobCreator, env *installer.EnvConfig) error {
repo, err := client.GetRepo("app")
if err != nil {
return err
}
- nsCreator := installer.NewNoOpNamespaceCreator()
+ nsc := installer.NewNoOpNamespaceCreator()
if err != nil {
return err
}
- m, err := installer.NewAppManager(repo, nsCreator, "/.dodo")
+ hf := installer.NewGitHelmFetcher()
+ m, err := installer.NewAppManager(repo, nsc, jc, hf, "/.dodo")
if err != nil {
return err
}
@@ -112,10 +116,11 @@
if err != nil {
return err
}
+ lg := installer.GitRepositoryLocalChartGenerator{"app", namespace}
if _, err := m.Install(app, "app", "/.dodo/app", namespace, map[string]any{
"repoAddr": repo.FullAddress(),
"sshPrivateKey": sshKey,
- }, installer.WithConfig(env), installer.WithBranch("dodo")); err != nil {
+ }, installer.WithConfig(env), installer.WithBranch("dodo"), installer.WithLocalChartGenerator(lg)); err != nil {
return err
}
return nil
diff --git a/core/installer/welcome/env.go b/core/installer/welcome/env.go
index 5d2206f..949cbe0 100644
--- a/core/installer/welcome/env.go
+++ b/core/installer/welcome/env.go
@@ -84,6 +84,8 @@
repo soft.RepoIO
repoClient soft.ClientGetter
nsCreator installer.NamespaceCreator
+ jc installer.JobCreator
+ hf installer.HelmFetcher
dnsFetcher installer.ZoneStatusFetcher
nameGenerator installer.NameGenerator
httpClient phttp.Client
@@ -100,6 +102,8 @@
repo soft.RepoIO,
repoClient soft.ClientGetter,
nsCreator installer.NamespaceCreator,
+ jc installer.JobCreator,
+ hf installer.HelmFetcher,
dnsFetcher installer.ZoneStatusFetcher,
nameGenerator installer.NameGenerator,
httpClient phttp.Client,
@@ -112,6 +116,8 @@
repo,
repoClient,
nsCreator,
+ jc,
+ hf,
dnsFetcher,
nameGenerator,
httpClient,
@@ -333,7 +339,9 @@
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
- mgr, err := installer.NewInfraAppManager(s.repo, s.nsCreator)
+ hf := installer.NewGitHelmFetcher()
+ lg := installer.NewInfraLocalChartGenerator()
+ mgr, err := installer.NewInfraAppManager(s.repo, s.nsCreator, hf, lg)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
@@ -403,6 +411,8 @@
t, dns := tasks.NewCreateEnvTask(
env,
s.nsCreator,
+ s.jc,
+ s.hf,
s.dnsFetcher,
s.httpClient,
s.dnsClient,
diff --git a/core/installer/welcome/env_test.go b/core/installer/welcome/env_test.go
index 0803e64..35e968d 100644
--- a/core/installer/welcome/env_test.go
+++ b/core/installer/welcome/env_test.go
@@ -34,6 +34,24 @@
return nil
}
+type fakeJobCreator struct {
+ t *testing.T
+}
+
+func (f fakeJobCreator) Create(name, namespace string, image string, cmd []string) error {
+ f.t.Logf("Create job: %s/%s %s \"%s\"", namespace, name, image, strings.Join(cmd, " "))
+ return nil
+}
+
+type fakeHelmFetcher struct {
+ t *testing.T
+}
+
+func (f fakeHelmFetcher) Pull(chart installer.HelmChartGitRepo, rfs soft.RepoFS, root string) error {
+ f.t.Logf("Helm pull: %+v", chart)
+ return nil
+}
+
type fakeZoneStatusFetcher struct {
t *testing.T
}
@@ -213,8 +231,11 @@
infraFS := memfs.New()
envFS := memfs.New()
nsCreator := fakeNSCreator{t}
+ jc := fakeJobCreator{t}
+ hf := fakeHelmFetcher{t}
+ lg := installer.GitRepositoryLocalChartGenerator{"foo", "bar"}
infraRepo := mockRepoIO{soft.NewBillyRepoFS(infraFS), "foo.bar", t, &sync.Mutex{}}
- infraMgr, err := installer.NewInfraAppManager(infraRepo, nsCreator)
+ infraMgr, err := installer.NewInfraAppManager(infraRepo, nsCreator, hf, lg)
if err != nil {
t.Fatal(err)
}
@@ -254,6 +275,8 @@
infraRepo,
cg,
nsCreator,
+ jc,
+ hf,
fakeZoneStatusFetcher{t},
fixedNameGenerator{},
httpClient,
diff --git a/core/installer/welcome/welcome.go b/core/installer/welcome/welcome.go
index 64f4cf1..7688d50 100644
--- a/core/installer/welcome/welcome.go
+++ b/core/installer/welcome/welcome.go
@@ -30,6 +30,7 @@
port int
repo soft.RepoIO
nsCreator installer.NamespaceCreator
+ hf installer.HelmFetcher
createAccountAddr string
loginAddr string
membershipsInitAddr string
@@ -39,6 +40,7 @@
port int,
repo soft.RepoIO,
nsCreator installer.NamespaceCreator,
+ hf installer.HelmFetcher,
createAccountAddr string,
loginAddr string,
membershipsInitAddr string,
@@ -47,6 +49,7 @@
port,
repo,
nsCreator,
+ hf,
createAccountAddr,
loginAddr,
membershipsInitAddr,
@@ -205,8 +208,9 @@
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
+ // TODO(gio): remove this once auto user sync is implemented
{
- appManager, err := installer.NewAppManager(s.repo, s.nsCreator, "/apps")
+ appManager, err := installer.NewAppManager(s.repo, s.nsCreator, nil, s.hf, "/apps")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return