DodoApp: Support remote clusters
Change-Id: I6f4e6a0a32cc723b47c96518d83b1ffdb5169f14
diff --git a/charts/app-runner/templates/install.yaml b/charts/app-runner/templates/install.yaml
index 1e22086..f3b2a00 100644
--- a/charts/app-runner/templates/install.yaml
+++ b/charts/app-runner/templates/install.yaml
@@ -114,3 +114,6 @@
- name: volume-{{ .name }}
mountPath: {{ .mountPath }}
{{- end }}
+ {{- if .Values.extraContainers }}
+ {{ toYaml .Values.extraContainers | nindent 6 }}
+ {{- end }}
diff --git a/charts/app-runner/values.yaml b/charts/app-runner/values.yaml
index afc9481..9145c47 100644
--- a/charts/app-runner/values.yaml
+++ b/charts/app-runner/values.yaml
@@ -11,3 +11,4 @@
managerAddr: ""
volumes: []
runtimeClassName: ""
+extraContainers: []
diff --git a/core/installer/app.go b/core/installer/app.go
index f023b18..d7f6989 100644
--- a/core/installer/app.go
+++ b/core/installer/app.go
@@ -491,12 +491,16 @@
if charts == nil {
charts = make(map[string]helmv2.HelmChartTemplateSpec)
}
+ if clusters == nil {
+ clusters = []Cluster{}
+ }
ret, err := a.cueApp.render(map[string]any{
"global": env,
"release": release,
"input": derived,
"localCharts": charts,
"networks": NetworkMap(networks),
+ "clusters": clusters,
})
if err != nil {
return EnvAppRendered{}, err
diff --git a/core/installer/app_configs/app_base.cue b/core/installer/app_configs/app_base.cue
index 192716f..0bb511a 100644
--- a/core/installer/app_configs/app_base.cue
+++ b/core/installer/app_configs/app_base.cue
@@ -449,31 +449,33 @@
}
output: {
- images: out.images
- charts: out.charts
- clusterProxy: out.clusterProxy
- _lc: _localCharts & {
- for k, v in out.charts {
- "\(k)": {
- ...
+ for _, out in outs {
+ images: out.images
+ charts: out.charts
+ clusterProxy: out.clusterProxy
+ _lc: _localCharts & {
+ for k, v in out.charts {
+ "\(k)": {
+ ...
+ }
}
}
- }
- helm: {
- for name, r in out.helmR {
- "\(name)": #HelmRelease & {
- _name: name
- _chart: _lc[r.chart.name]
- _values: r.values
- _dependencies: r.dependsOn
- _info: r.info
- _annotations: r.annotations
- _namespace: release.namespace
- if r.cluster != _|_ {
- _cluster: r.cluster
- }
- if r.targetNamespace != _|_ {
- _targetNamespace: r.targetNamespace
+ helm: {
+ for name, r in out.helmR {
+ "\(name)": #HelmRelease & {
+ _name: name
+ _chart: _lc[r.chart.name]
+ _values: r.values
+ _dependencies: r.dependsOn
+ _info: r.info
+ _annotations: r.annotations
+ _namespace: release.namespace
+ if r.cluster != _|_ {
+ _cluster: r.cluster
+ }
+ if r.targetNamespace != _|_ {
+ _targetNamespace: r.targetNamespace
+ }
}
}
}
@@ -496,6 +498,11 @@
url: string | *""
#WithOut: {
+ cluster?: #Cluster
+ if input.cluster != _|_ {
+ cluster: #Cluster | *input.cluster
+ }
+ _cluster: cluster
images: {...}
charts: {...}
helm: {...}
@@ -527,8 +534,8 @@
helmR: {
for k, v in helm {
"\(k)": v & {
- if v.cluster == _|_ && input.cluster != _|_ {
- cluster: input.cluster
+ if v.cluster == _|_ && _cluster != _|_ {
+ cluster: _cluster
}
}
}
@@ -537,6 +544,39 @@
...
}
+outs: {
+ "out": out
+}
+if out.cluster != _|_ {
+ outs: kout: #WithOut & {
+ cluster: out.cluster
+ clusterName: cluster.name
+ clusterKubeconfig: cluster.kubeconfig
+ charts: {
+ secret: {
+ name: "secret"
+ kind: "GitRepository"
+ address: "https://code.v1.dodo.cloud/helm-charts"
+ branch: "main"
+ path: "charts/secret"
+ }
+ }
+ helm: {
+ "cluster-kubeconfig": {
+ chart: charts.secret
+ cluster: null
+ info: "Connecting to \(clusterName) cluster"
+ values: {
+ name: "cluster-kubeconfig"
+ key: "kubeconfig"
+ value: base64.Encode(null, clusterKubeconfig)
+ keep: true
+ }
+ }
+ }
+ }
+}
+
#WithOut: {
charts: {
volume: {
@@ -658,35 +698,10 @@
to: string
}
-if input.cluster != _|_ {
- {
- out: {
- charts: {
- secret: {
- kind: "GitRepository"
- address: "https://code.v1.dodo.cloud/helm-charts"
- branch: "main"
- path: "charts/secret"
- }
- }
- helm: {
- "cluster-kubeconfig": {
- chart: charts.secret
- cluster: null
- info: "Connecting to \(input.cluster.name) cluster"
- values: {
- name: "cluster-kubeconfig"
- key: "kubeconfig"
- value: base64.Encode(null, input.cluster.kubeconfig)
- keep: true
- }
- }
- }
- }
- }
-
+// TODO(gio): Move this inside #WithOut definition
+if out.cluster != _|_ {
namespaces: [{
name: release.namespace
- kubeconfig: input.cluster.kubeconfig
+ kubeconfig: out.cluster.kubeconfig
}]
}
diff --git a/core/installer/app_configs/app_global_env.cue b/core/installer/app_configs/app_global_env.cue
index 8d4a7db..e6e8de8 100644
--- a/core/installer/app_configs/app_global_env.cue
+++ b/core/installer/app_configs/app_global_env.cue
@@ -31,6 +31,13 @@
networks: #Networks
+clusters: [...#Cluster] | *[]
+clusterMap: {
+ for c in clusters {
+ "\(strings.ToLower(c.name))": c
+ }
+}
+
#Ingress: #WithOut & {
name: string
auth: #Auth
@@ -41,6 +48,8 @@
name: string
port: close({ name: string }) | close({ number: int & > 0 })
})
+ cluster?: #Cluster
+ _cluster: cluster
g?: #Global
_domain: "\(subdomain).\(network.domain)"
@@ -48,12 +57,12 @@
_authProxyName: "\(name)-auth-proxy"
_authProxyHTTPPortName: "http"
- if input.cluster != _|_ {
+ if _cluster != _|_ {
clusterProxy: {
"\(name)": {
from: _domain
_sanitizedDomain: strings.Replace(_domain, ".", "-", -1)
- to: "\(_sanitizedDomain).\(input.cluster.name).cluster.\(global.privateDomain)"
+ to: "\(_sanitizedDomain).\(_cluster.name).cluster.\(global.privateDomain)"
}
}
}
@@ -109,14 +118,14 @@
}
}
}
- if input.cluster != _|_ {
- "\(name)-ingress-\(input.cluster.name)": {
+ if _cluster != _|_ {
+ "\(name)-ingress-\(_cluster.name)": {
chart: charts.ingress
- cluster: input.cluster
+ cluster: _cluster
_service: service
_sanitizedDomain: strings.Replace(_domain, ".", "-", -1)
- _clusterDomain: "\(_sanitizedDomain).\(input.cluster.name).cluster.\(global.privateDomain)"
- info: "Configuring secure route to \(input.cluster.name) cluster"
+ _clusterDomain: "\(_sanitizedDomain).\(cluster.name).cluster.\(global.privateDomain)"
+ info: "Configuring secure route to \(cluster.name) cluster"
annotations: {
// TODO(gio): Change type to cluster-gateway or sth similar.
"dodo.cloud/resource-type": "ingress"
@@ -124,7 +133,7 @@
}
values: {
domain: _clusterDomain
- ingressClassName: input.cluster.ingressClassName
+ ingressClassName: cluster.ingressClassName
certificateIssuer: ""
annotations: {
"nginx.ingress.kubernetes.io/force-ssl-redirect": "false"
@@ -172,7 +181,7 @@
}
}
}
- if input.cluster == _|_ {
+ if _cluster == _|_ {
"\(name)-ingress": {
chart: charts.ingress
// NOTE(gio): Force to install in default cluster.
@@ -210,12 +219,17 @@
}
#WithOut: {
+ cluster?: #Cluster
+ _cluster: cluster
ingress: {...}
ingress: {
for k, v in ingress {
"\(k)": #Ingress & v & {
name: k
g: global
+ if _cluster != _|_ {
+ cluster: _cluster
+ }
}
}
...
diff --git a/core/installer/app_configs/dodo_app.cue b/core/installer/app_configs/dodo_app.cue
index b9357ec..a4bbf0c 100644
--- a/core/installer/app_configs/dodo_app.cue
+++ b/core/installer/app_configs/dodo_app.cue
@@ -21,9 +21,8 @@
if app.dev.enabled {
input: {
username?: string | *app.dev.username
- vpnAuthKey: string @role(VPNAuthKey) @usernameField(username)
+ vpnAuthKey: string @role(VPNAuthKey) @usernameField(username)
}
-
_devVM: {
username: app.dev.username
domain: global.domain
@@ -92,6 +91,7 @@
#AppTmpl: {
type: string
+ cluster?: string
ingress: #AppIngress
volumes: {
for k, v in volumes {
@@ -245,6 +245,11 @@
if !_app.dev.enabled {
{
+ if _app.cluster != _|_ {
+ input: {
+ appVPNAuthKey: string @role(VPNAuthKey) @username(private-network-proxy)
+ }
+ }
out: {
ingress: {
app: {
@@ -264,8 +269,20 @@
tag: strings.Replace(_app.type, ":", "-", -1)
pullPolicy: "Always"
}
+ "tailscale-proxy": {
+ repository: "tailscale"
+ name: "tailscale"
+ tag: "v1.42.0"
+ pullPolicy: "IfNotPresent"
+ }
}
charts: {
+ "access-secrets": {
+ kind: "GitRepository"
+ address: "https://code.v1.dodo.cloud/helm-charts"
+ branch: "main"
+ path: "charts/access-secrets"
+ }
app: {
kind: "GitRepository"
address: "https://code.v1.dodo.cloud/helm-charts"
@@ -274,6 +291,16 @@
}
}
helm: {
+ if _app.cluster != _|_ {
+ {
+ "access-secrets": {
+ chart: charts["access-secrets"]
+ values: {
+ serviceAccountName: "default"
+ }
+ }
+ }
+ }
app: {
chart: charts.app
values: {
@@ -282,11 +309,30 @@
tag: images.app.tag
pullPolicy: images.app.pullPolicy
}
- runtimeClassName: "untrusted-external" // TODO(gio): make this part of the infra config
+ // TODO(gio): install gvisor runtime during new remote cluster init
+ if _app.cluster == _|_ {
+ runtimeClassName: "untrusted-external" // TODO(gio): make this part of the infra config
+ }
+ if _app.cluster != _|_ {
+ extraContainers: [{
+ name: "proxy"
+ image: images["tailscale-proxy"].fullNameWithTag
+ env: [{
+ name: "TS_AUTHKEY"
+ value: input.appVPNAuthKey
+ }, {
+ name: "TS_HOSTNAME"
+ value: "dodo-app-\(input.appId)"
+ }, {
+ name: "TS_EXTRA_ARGS"
+ value: "--login-server=https://headscale.\(global.domain)"
+ }]
+ }]
+ }
appPort: _app.port
appDir: _app.rootDir
appId: input.appId
- repoAddr: input.repoAddr
+ repoAddr: "\(input.repoPublicAddr)/\(input.appId)"
sshPrivateKey: base64.Encode(null, input.sshPrivateKey)
runCfg: base64.Encode(null, json.Marshal(_app.runConfiguration))
managerAddr: input.managerAddr
@@ -333,6 +379,9 @@
_vmName: "\(input.appId)-\(input.branch)"
out: {
+ if app.cluster != _|_ {
+ cluster: clusterMap[strings.ToLower(app.cluster)]
+ }
volumes: app.volumes
postgresql: app.postgresql
vm: {
diff --git a/core/installer/app_manager.go b/core/installer/app_manager.go
index 0bddf29..9b28004 100644
--- a/core/installer/app_manager.go
+++ b/core/installer/app_manager.go
@@ -449,9 +449,15 @@
return ReleaseResources{}, err
}
}
- clusters, err := m.GetClusters()
- if err != nil {
- return ReleaseResources{}, err
+ var clusters []Cluster
+ if o.Clusters != nil {
+ clusters = o.Clusters
+ } else {
+ if cls, err := m.GetClusters(); err != nil {
+ return ReleaseResources{}, err
+ } else {
+ clusters = ToAccessConfigs(cls)
+ }
}
var lg LocalChartGenerator
if o.LG != nil {
@@ -465,7 +471,7 @@
RepoAddr: m.repo.FullAddress(),
AppDir: appDir,
}
- rendered, err := app.Render(release, env, networks, ToAccessConfigs(clusters), values, nil, m.vpnAPIClient)
+ rendered, err := app.Render(release, env, networks, clusters, values, nil, m.vpnAPIClient)
if err != nil {
return ReleaseResources{}, err
}
@@ -497,7 +503,7 @@
if o.FetchContainerImages {
release.ImageRegistry = imageRegistry
}
- rendered, err = app.Render(release, env, networks, ToAccessConfigs(clusters), values, localCharts, m.vpnAPIClient)
+ rendered, err = app.Render(release, env, networks, clusters, values, localCharts, m.vpnAPIClient)
if err != nil {
return ReleaseResources{}, err
}
@@ -796,6 +802,7 @@
NoPublish bool
Env *EnvConfig
Networks []Network
+ Clusters []Cluster
Branch string
LG LocalChartGenerator
FetchContainerImages bool
@@ -821,6 +828,12 @@
return WithNetworks([]Network{})
}
+func WithClusters(clusters []Cluster) InstallOption {
+ return func(o *installOptions) {
+ o.Clusters = clusters
+ }
+}
+
func WithBranch(branch string) InstallOption {
return func(o *installOptions) {
o.Branch = branch
diff --git a/core/installer/app_test.go b/core/installer/app_test.go
index 4f3f7dd..83cf701 100644
--- a/core/installer/app_test.go
+++ b/core/installer/app_test.go
@@ -59,6 +59,17 @@
DeallocatePortAddr: fmt.Sprintf("http://port-allocator.%s-ingress-private.svc.cluster.local/api/remove", env.Id),
},
}
+
+ clusters = []Cluster{
+ {
+ Name: "default",
+ },
+ {
+ Name: "io",
+ IngressClassName: "io",
+ Kubeconfig: "kubeconfig",
+ },
+ }
)
func TestAuthProxyEnabled(t *testing.T) {
@@ -297,6 +308,42 @@
}
}
+func TestAppPackagesRemoteCluster(t *testing.T) {
+ contents, err := valuesTmpls.ReadFile("values-tmpl/rpuppy.cue")
+ if err != nil {
+ t.Fatal(err)
+ }
+ app, err := NewCueEnvApp(CueAppData{
+ "base.cue": []byte(cueBaseConfig),
+ "app.cue": []byte(contents),
+ "global.cue": []byte(cueEnvAppGlobal),
+ })
+ if err != nil {
+ t.Fatal(err)
+ }
+ release := Release{
+ Namespace: "foo",
+ }
+ values := map[string]any{
+ "network": "Public",
+ "subdomain": "woof",
+ "auth": map[string]any{
+ "enabled": false,
+ },
+ "cluster": "io",
+ }
+ rendered, err := app.Render(release, env, networks, clusters, values, nil, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ for _, r := range rendered.Resources {
+ t.Log(string(r))
+ }
+ for _, r := range rendered.Data {
+ t.Log(string(r))
+ }
+}
+
func TestDNSGateway(t *testing.T) {
contents, err := valuesTmpls.ReadFile("values-tmpl/dns-gateway.cue")
if err != nil {
@@ -337,6 +384,58 @@
}
}
+var dodoAppRemoteClusterCue = `
+app: {
+ type: "golang:1.22.0"
+ run: "main.go"
+ ingress: {
+ network: "private"
+ subdomain: "testapp"
+ auth: enabled: false
+ }
+ dev: {
+ enabled: false
+ }
+ cluster: "io"
+}`
+
+func TestDodoAppRemoteCluster(t *testing.T) {
+ app, err := NewDodoApp([]byte(dodoAppRemoteClusterCue))
+ if err != nil {
+ for _, e := range errors.Errors(err) {
+ t.Log(e)
+ }
+ t.Fatal(err)
+ }
+
+ release := Release{
+ Namespace: "foo",
+ AppInstanceId: "foo-bar",
+ RepoAddr: "ssh://192.168.100.210:22/config",
+ AppDir: "/foo/bar",
+ }
+ keyGen := testKeyGen{}
+ r, err := app.Render(release, env, networks, clusters, map[string]any{
+ "repoAddr": "1",
+ "repoPublicAddr": "2",
+ "managerAddr": "3",
+ "appId": "4",
+ "branch": "5",
+ "sshPrivateKey": "6",
+ }, nil, keyGen)
+ if err != nil {
+ for _, e := range errors.Errors(err) {
+ for _, f := range errors.Errors(e) {
+ for _, g := range errors.Errors(f) {
+ t.Log(g)
+ }
+ }
+ }
+ t.Fatal(err)
+ }
+ t.Log(string(r.Raw))
+}
+
var dodoAppDevDisabledCue = `
app: {
type: "golang:1.22.0"
diff --git a/core/installer/cluster.go b/core/installer/cluster.go
index 9ebffcf..9ac89da 100644
--- a/core/installer/cluster.go
+++ b/core/installer/cluster.go
@@ -93,8 +93,8 @@
if err != nil {
return "", err
}
- if v, ok := cfg.Proxies[src]; ok {
- return "", fmt.Errorf("mapping from %s already exists (%s)", src, v)
+ if v, ok := cfg.Proxies[src]; ok && v != dst {
+ return "", fmt.Errorf("wrong mapping %s already exists (%s)", src, v)
}
cfg.Proxies[src] = dst
w, err := fs.Writer(c.NginxConfigPath)
@@ -167,8 +167,8 @@
if err != nil {
return "", err
}
- if v, ok := cfg.Proxies[src]; !ok || v != dst {
- return "", fmt.Errorf("mapping does not exist: %s %s", src, dst)
+ if v, ok := cfg.Proxies[src]; ok || v != dst {
+ return "", fmt.Errorf("wrong mapping %s already exists (%s)", src, v)
}
delete(cfg.Proxies, src)
w, err := fs.Writer(c.NginxConfigPath)
diff --git a/core/installer/cmd/dodo_app.go b/core/installer/cmd/dodo_app.go
index 8512691..bcd8a68 100644
--- a/core/installer/cmd/dodo_app.go
+++ b/core/installer/cmd/dodo_app.go
@@ -1,9 +1,14 @@
package main
import (
+ "bytes"
"database/sql"
"encoding/json"
+ "fmt"
+ "io"
"log"
+ "net"
+ "net/http"
"os"
"github.com/giolekva/pcloud/core/installer"
@@ -201,8 +206,7 @@
},
}
vpnKeyGen := installer.NewHeadscaleAPIClient(dodoAppFlags.headscaleAPIAddr)
- // TOOD(gio): implement
- var cnc installer.ClusterNetworkConfigurator
+ cnc := &proxyConfigurator{dodoAppFlags.envAppManagerAddr}
s, err := welcome.NewDodoAppServer(
st,
nf,
@@ -230,3 +234,54 @@
}
return s.Start()
}
+
+type proxyConfigurator struct {
+ apiAddr string
+}
+
+func (pc *proxyConfigurator) AddCluster(name string, ingressIP net.IP) error {
+ return fmt.Errorf("NOT IMPLEMENTED")
+}
+
+func (pc *proxyConfigurator) RemoveCluster(name string, ingressIP net.IP) error {
+ return fmt.Errorf("NOT IMPLEMENTED")
+}
+
+type proxyPair struct {
+ From string `json:"from"`
+ To string `json:"to"`
+}
+
+func (pc *proxyConfigurator) AddProxy(src, dst string) error {
+ var buf bytes.Buffer
+ if err := json.NewEncoder(&buf).Encode(proxyPair{src, dst}); err != nil {
+ return err
+ }
+ resp, err := http.Post(fmt.Sprintf("%s/api/proxy/add", pc.apiAddr), "application/json", &buf)
+ if err != nil {
+ return err
+ }
+ if resp.StatusCode != http.StatusOK {
+ var buf bytes.Buffer
+ io.Copy(&buf, resp.Body)
+ return fmt.Errorf(buf.String())
+ }
+ return nil
+}
+
+func (pc *proxyConfigurator) RemoveProxy(src, dst string) error {
+ var buf bytes.Buffer
+ if err := json.NewEncoder(&buf).Encode(proxyPair{src, dst}); err != nil {
+ return err
+ }
+ resp, err := http.Post(fmt.Sprintf("%s/api/proxy/remove", pc.apiAddr), "application/json", &buf)
+ if err != nil {
+ return err
+ }
+ if resp.StatusCode != http.StatusOK {
+ var buf bytes.Buffer
+ io.Copy(&buf, resp.Body)
+ return fmt.Errorf(buf.String())
+ }
+ return nil
+}
diff --git a/core/installer/go.mod b/core/installer/go.mod
index c58ffaf..2aa88b5 100644
--- a/core/installer/go.mod
+++ b/core/installer/go.mod
@@ -5,7 +5,7 @@
go 1.22.0
require (
- cuelang.org/go v0.8.1
+ cuelang.org/go v0.10.0
github.com/Masterminds/sprig/v3 v3.2.3
github.com/cenkalti/backoff/v4 v4.3.0
github.com/charmbracelet/keygen v0.5.0
@@ -19,18 +19,19 @@
github.com/libdns/libdns v0.2.2
github.com/miekg/dns v1.1.58
github.com/ncruces/go-sqlite3 v0.17.0
- github.com/spf13/cobra v1.8.0
- golang.org/x/crypto v0.24.0
+ github.com/spf13/cobra v1.8.1
+ golang.org/x/crypto v0.26.0
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8
helm.sh/helm/v3 v3.14.3
k8s.io/api v0.30.0
k8s.io/apimachinery v0.30.0
k8s.io/client-go v0.30.0
+ k8s.io/kubectl v0.29.3
sigs.k8s.io/yaml v1.4.0
)
require (
- cuelabs.dev/go/oci/ociregistry v0.0.0-20240314152124-224736b49f2e // indirect
+ cuelabs.dev/go/oci/ociregistry v0.0.0-20240807094312-a32ad29eed79 // indirect
dario.cat/mergo v1.0.0 // indirect
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
@@ -60,7 +61,7 @@
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-metrics v0.0.1 // indirect
github.com/emicklei/go-restful/v3 v3.12.0 // indirect
- github.com/emicklei/proto v1.10.0 // indirect
+ github.com/emicklei/proto v1.13.2 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/evanphx/json-patch v5.9.0+incompatible // indirect
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect
@@ -122,6 +123,7 @@
github.com/ncruces/julianday v1.0.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
+ github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
@@ -132,7 +134,7 @@
github.com/prometheus/procfs v0.13.0 // indirect
github.com/protocolbuffers/txtpbfmt v0.0.0-20230328191034-3462fbc510c0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
- github.com/rogpeppe/go-internal v1.12.0 // indirect
+ github.com/rogpeppe/go-internal v1.12.1-0.20240709150035-ccf4b4329d21 // indirect
github.com/rubenv/sql-migrate v1.6.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
@@ -152,16 +154,15 @@
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
go.starlark.net v0.0.0-20240329153429-e6e8e7ce1b7a // indirect
- golang.org/x/mod v0.17.0 // indirect
- golang.org/x/net v0.25.0 // indirect
- golang.org/x/oauth2 v0.18.0 // indirect
- golang.org/x/sync v0.7.0 // indirect
- golang.org/x/sys v0.22.0 // indirect
- golang.org/x/term v0.21.0 // indirect
- golang.org/x/text v0.16.0 // indirect
+ golang.org/x/mod v0.20.0 // indirect
+ golang.org/x/net v0.28.0 // indirect
+ golang.org/x/oauth2 v0.22.0 // indirect
+ golang.org/x/sync v0.8.0 // indirect
+ golang.org/x/sys v0.23.0 // indirect
+ golang.org/x/term v0.23.0 // indirect
+ golang.org/x/text v0.17.0 // indirect
golang.org/x/time v0.5.0 // indirect
- golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
- google.golang.org/appengine v1.6.8 // indirect
+ golang.org/x/tools v0.24.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect
google.golang.org/grpc v1.63.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
@@ -176,7 +177,6 @@
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
diff --git a/core/installer/go.sum b/core/installer/go.sum
index fc743fe..8c5107a 100644
--- a/core/installer/go.sum
+++ b/core/installer/go.sum
@@ -1,7 +1,7 @@
-cuelabs.dev/go/oci/ociregistry v0.0.0-20240314152124-224736b49f2e h1:GwCVItFUPxwdsEYnlUcJ6PJxOjTeFFCKOh6QWg4oAzQ=
-cuelabs.dev/go/oci/ociregistry v0.0.0-20240314152124-224736b49f2e/go.mod h1:ApHceQLLwcOkCEXM1+DyCXTHEJhNGDpJ2kmV6axsx24=
-cuelang.org/go v0.8.1 h1:VFYsxIFSPY5KgSaH1jQ2GxHOrbu6Ga3kEI70yCZwnOg=
-cuelang.org/go v0.8.1/go.mod h1:CoDbYolfMms4BhWUlhD+t5ORnihR7wvjcfgyO9lL5FI=
+cuelabs.dev/go/oci/ociregistry v0.0.0-20240807094312-a32ad29eed79 h1:EceZITBGET3qHneD5xowSTY/YHbNybvMWGh62K2fG/M=
+cuelabs.dev/go/oci/ociregistry v0.0.0-20240807094312-a32ad29eed79/go.mod h1:5A4xfTzHTXfeVJBU6RAUf+QrlfTCW+017q/QiW+sMLg=
+cuelang.org/go v0.10.0 h1:Y1Pu4wwga5HkXfLFK1sWAYaSWIBdcsr5Cb5AWj2pOuE=
+cuelang.org/go v0.10.0/go.mod h1:HzlaqqqInHNiqE6slTP6+UtxT9hN6DAzgJgdbNxXvX8=
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
@@ -77,7 +77,7 @@
github.com/containerd/errdefs v0.1.0/go.mod h1:YgWiiHtLmSeBrvpw+UfPijzbLaB77mEG1WwJTDETIV0=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
-github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
+github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
@@ -110,8 +110,8 @@
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
github.com/emicklei/go-restful/v3 v3.12.0 h1:y2DdzBAURM29NFF94q6RaY4vjIH1rtwDapwQtU84iWk=
github.com/emicklei/go-restful/v3 v3.12.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
-github.com/emicklei/proto v1.10.0 h1:pDGyFRVV5RvV+nkBK9iy3q67FBy9Xa7vwrOTE+g5aGw=
-github.com/emicklei/proto v1.10.0/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
+github.com/emicklei/proto v1.13.2 h1:z/etSFO3uyXeuEsVPzfl56WNgzcvIr42aQazXaQmFZY=
+github.com/emicklei/proto v1.13.2/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls=
@@ -177,8 +177,6 @@
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
-github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/gomarkdown/markdown v0.0.0-20240328165702-4d01890c35c0 h1:4gjrh/PN2MuWCCElk8/I4OCKRKWCCo2zEct3VKCbibU=
@@ -190,7 +188,6 @@
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 h1:0VpGH+cDhbDtdcweoyCVsF3fhN8kejK6rFe/2FFX2nU=
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49/go.mod h1:BkkQ4L1KS1xMt2aWSPStnn55ChGC0DPOn2FQYj+f25M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
@@ -330,6 +327,8 @@
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=
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
+github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
+github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=
@@ -367,8 +366,8 @@
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
-github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
-github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
+github.com/rogpeppe/go-internal v1.12.1-0.20240709150035-ccf4b4329d21 h1:igWZJluD8KtEtAgRyF4x6lqcxDry1ULztksMJh2mnQE=
+github.com/rogpeppe/go-internal v1.12.1-0.20240709150035-ccf4b4329d21/go.mod h1:RMRJLmBOqWacUkmJHRMiPKh1S1m3PA7Zh4W80/kWPpg=
github.com/rubenv/sql-migrate v1.6.1 h1:bo6/sjsan9HaXAsNxYP/jCEDUGibHp8JmOBw7NTGRos=
github.com/rubenv/sql-migrate v1.6.1/go.mod h1:tPzespupJS0jacLfhbwto/UjSX+8h2FdWB7ar+QlHa0=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
@@ -387,20 +386,25 @@
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
-github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
-github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
+github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
+github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tetratelabs/wazero v1.7.3 h1:PBH5KVahrt3S2AHgEjKu4u+LlDbbk+nsGE3KLucy6Rw=
@@ -448,16 +452,16 @@
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
-golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
-golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
+golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
+golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw=
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
-golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
+golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -470,10 +474,10 @@
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.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
-golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
-golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI=
-golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8=
+golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
+golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
+golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA=
+golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -481,8 +485,8 @@
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
-golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
+golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -503,25 +507,24 @@
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
-golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
+golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
-golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
-golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
+golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
+golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
-golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
-golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
+golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
+golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -530,20 +533,16 @@
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
-golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
-golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
+golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
+golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
-google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda h1:LI5DOvAxUPMv/50agcLLoo+AdWc1irS9Rzz4vPuD1V4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
google.golang.org/grpc v1.63.0 h1:WjKe+dnvABXyPJMD7KDNLxtoGk5tgk+YFWN6cBWjZE8=
google.golang.org/grpc v1.63.0/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
-google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
-google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
diff --git a/core/installer/schema.go b/core/installer/schema.go
index 5a70519..3c9e237 100644
--- a/core/installer/schema.go
+++ b/core/installer/schema.go
@@ -6,6 +6,7 @@
"cuelang.org/go/cue"
"cuelang.org/go/cue/cuecontext"
+ "cuelang.org/go/cue/format"
)
type Kind int
@@ -64,6 +65,7 @@
ingressClassName: string
}
+value: #Cluster
value: { %s }
`
@@ -71,18 +73,23 @@
if v.Value().Kind() != cue.StructKind {
return false
}
- s := fmt.Sprintf(clusterSchema, fmt.Sprintf("%#v", v))
+ vb, err := format.Node(v.Syntax(cue.All()), format.TabIndent(true))
+ if err != nil {
+ return false
+ }
+ s := fmt.Sprintf(clusterSchema, string(vb))
c := cuecontext.New()
u := c.CompileString(s)
+ if err := u.Err(); err != nil {
+ return false
+ }
if err := u.Validate(); err != nil {
return false
}
- cluster := u.LookupPath(cue.ParsePath("#Cluster"))
- vv := u.LookupPath(cue.ParsePath("value"))
- if err := cluster.Subsume(vv); err == nil {
- return true
+ if err := u.Eval().Err(); err != nil {
+ return false
}
- return false
+ return true
}
const networkSchema = `
@@ -96,6 +103,7 @@
deallocatePortAddr: string
}
+value: #Network
value: { %s }
`
@@ -103,15 +111,23 @@
if v.Value().Kind() != cue.StructKind {
return false
}
- s := fmt.Sprintf(networkSchema, fmt.Sprintf("%#v", v))
+ vb, err := format.Node(v.Syntax(cue.All()), format.TabIndent(true))
+ if err != nil {
+ return false
+ }
+ s := fmt.Sprintf(networkSchema, string(vb))
c := cuecontext.New()
u := c.CompileString(s)
- network := u.LookupPath(cue.ParsePath("#Network"))
- vv := u.LookupPath(cue.ParsePath("value"))
- if err := network.Subsume(vv); err == nil {
- return true
+ if err := u.Err(); err != nil {
+ return false
}
- return false
+ if err := u.Validate(); err != nil {
+ return false
+ }
+ if err := u.Eval().Err(); err != nil {
+ return false
+ }
+ return true
}
const multiNetworkSchema = `
@@ -127,6 +143,7 @@
#Networks: [...#Network]
+value: #Networks
value: %s
`
@@ -134,15 +151,23 @@
if v.Value().IncompleteKind() != cue.ListKind {
return false
}
- s := fmt.Sprintf(multiNetworkSchema, fmt.Sprintf("%#v", v))
+ vb, err := format.Node(v.Syntax(cue.All()), format.TabIndent(true))
+ if err != nil {
+ return false
+ }
+ s := fmt.Sprintf(multiNetworkSchema, string(vb))
c := cuecontext.New()
u := c.CompileString(s)
- networks := u.LookupPath(cue.ParsePath("#Networks"))
- vv := u.LookupPath(cue.ParsePath("value"))
- if err := networks.Subsume(vv); err == nil {
- return true
+ if err := u.Err(); err != nil {
+ return false
}
- return false
+ if err := u.Validate(); err != nil {
+ return false
+ }
+ if err := u.Eval().Err(); err != nil {
+ return false
+ }
+ return true
}
const authSchema = `
@@ -151,6 +176,7 @@
groups: string | *""
}
+value: #Auth
value: { %s }
`
@@ -158,15 +184,23 @@
if v.Value().Kind() != cue.StructKind {
return false
}
- s := fmt.Sprintf(authSchema, fmt.Sprintf("%#v", v))
+ vb, err := format.Node(v.Syntax(cue.All()), format.TabIndent(true))
+ if err != nil {
+ return false
+ }
+ s := fmt.Sprintf(authSchema, string(vb))
c := cuecontext.New()
u := c.CompileString(s)
- auth := u.LookupPath(cue.ParsePath("#Auth"))
- vv := u.LookupPath(cue.ParsePath("value"))
- if err := auth.Subsume(vv); err == nil {
- return true
+ if err := u.Err(); err != nil {
+ return false
}
- return false
+ if err := u.Validate(); err != nil {
+ return false
+ }
+ if err := u.Eval().Err(); err != nil {
+ return false
+ }
+ return true
}
const sshKeySchema = `
@@ -175,6 +209,7 @@
private: string
}
+value: #SSHKey
value: { %s }
`
@@ -182,15 +217,23 @@
if v.Value().Kind() != cue.StructKind {
return false
}
- s := fmt.Sprintf(sshKeySchema, fmt.Sprintf("%#v", v))
+ vb, err := format.Node(v.Syntax(cue.All()), format.TabIndent(true))
+ if err != nil {
+ return false
+ }
+ s := fmt.Sprintf(sshKeySchema, string(vb))
c := cuecontext.New()
u := c.CompileString(s)
- sshKey := u.LookupPath(cue.ParsePath("#SSHKey"))
- vv := u.LookupPath(cue.ParsePath("value"))
- if err := sshKey.Subsume(vv); err == nil {
- return true
+ if err := u.Err(); err != nil {
+ return false
}
- return false
+ if err := u.Validate(); err != nil {
+ return false
+ }
+ if err := u.Eval().Err(); err != nil {
+ return false
+ }
+ return true
}
type basicSchema struct {
@@ -318,7 +361,7 @@
}
return s, nil
default:
- return nil, fmt.Errorf("SHOULD NOT REACH!")
+ return nil, fmt.Errorf("SHOULD NOT REACH! field: %s, value: %s", name, v)
}
}
diff --git a/core/installer/schema_test.go b/core/installer/schema_test.go
index 61739fb..04375bf 100644
--- a/core/installer/schema_test.go
+++ b/core/installer/schema_test.go
@@ -2,6 +2,8 @@
import (
"testing"
+
+ "cuelang.org/go/cue"
)
func TestFindPortFields(t *testing.T) {
@@ -34,3 +36,56 @@
t.Fatalf("expected 'z' and 'w.z' port fields, %v", p)
}
}
+
+const isNotNetwork = `
+input: {
+ repoAddr: string
+ repoPublicAddr: string
+ managerAddr: string
+ appId: string
+ branch: string
+ sshPrivateKey: string
+ username?: string
+ cluster: #Cluster
+ username?: string | *"test"
+ vpnAuthKey: string @role(VPNAuthKey) @usernameField(username)
+}
+
+#Cluster: {
+ name: string
+ kubeconfig: string
+ ingressClassName: string
+}
+`
+
+func TestIsNotNetwork(t *testing.T) {
+ v, err := ParseCueAppConfig(CueAppData{"/test.cue": []byte(isNotNetwork)})
+ if err != nil {
+ t.Fatal(err)
+ }
+ if isNetwork(v.LookupPath(cue.ParsePath("input"))) {
+ t.Fatal("not really network")
+ }
+}
+
+const inputIsNetwork = `
+input: {
+ name: string
+ ingressClass: string
+ certificateIssuer: string | *""
+ domain: string
+ allocatePortAddr: string
+ reservePortAddr: string
+ deallocatePortAddr: string
+}
+`
+
+func TestIsNetwork(t *testing.T) {
+ v, err := ParseCueAppConfig(CueAppData{"/test.cue": []byte(inputIsNetwork)})
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !isNetwork(v.LookupPath(cue.ParsePath("input"))) {
+ t.Fatal("is network")
+ }
+}
diff --git a/core/installer/welcome/appmanager.go b/core/installer/welcome/appmanager.go
index 3016190..808bf7e 100644
--- a/core/installer/welcome/appmanager.go
+++ b/core/installer/welcome/appmanager.go
@@ -137,6 +137,9 @@
r := mux.NewRouter()
r.PathPrefix("/stat/").Handler(cachingHandler{http.FileServer(http.FS(statAssets))})
r.HandleFunc("/api/networks", s.handleNetworks).Methods(http.MethodGet)
+ r.HandleFunc("/api/clusters", s.handleClusters).Methods(http.MethodGet)
+ r.HandleFunc("/api/proxy/add", s.handleProxyAdd).Methods(http.MethodPost)
+ r.HandleFunc("/api/proxy/remove", s.handleProxyRemove).Methods(http.MethodPost)
r.HandleFunc("/api/app-repo", s.handleAppRepo)
r.HandleFunc("/api/app/{slug}/install", s.handleAppInstall).Methods(http.MethodPost)
r.HandleFunc("/api/app/{slug}", s.handleApp).Methods(http.MethodGet)
@@ -158,14 +161,6 @@
return http.ListenAndServe(fmt.Sprintf(":%d", s.port), r)
}
-type app struct {
- Name string `json:"name"`
- Icon template.HTML `json:"icon"`
- ShortDescription string `json:"shortDescription"`
- Slug string `json:"slug"`
- Instances []installer.AppInstanceConfig `json:"instances,omitempty"`
-}
-
func (s *AppManagerServer) handleNetworks(w http.ResponseWriter, r *http.Request) {
env, err := s.m.Config()
if err != nil {
@@ -183,6 +178,55 @@
}
}
+func (s *AppManagerServer) handleClusters(w http.ResponseWriter, r *http.Request) {
+ clusters, err := s.m.GetClusters()
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ if err := json.NewEncoder(w).Encode(installer.ToAccessConfigs(clusters)); err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+}
+
+type proxyPair struct {
+ From string `json:"from"`
+ To string `json:"to"`
+}
+
+func (s *AppManagerServer) handleProxyAdd(w http.ResponseWriter, r *http.Request) {
+ var req proxyPair
+ if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+ if err := s.cnc.AddProxy(req.From, req.To); err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+}
+
+func (s *AppManagerServer) handleProxyRemove(w http.ResponseWriter, r *http.Request) {
+ var req proxyPair
+ if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+ if err := s.cnc.RemoveProxy(req.From, req.To); err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+}
+
+type app struct {
+ Name string `json:"name"`
+ Icon template.HTML `json:"icon"`
+ ShortDescription string `json:"shortDescription"`
+ Slug string `json:"slug"`
+ Instances []installer.AppInstanceConfig `json:"instances,omitempty"`
+}
+
func (s *AppManagerServer) handleAppRepo(w http.ResponseWriter, r *http.Request) {
all, err := s.r.GetAll()
if err != nil {
diff --git a/core/installer/welcome/dodo_app.go b/core/installer/welcome/dodo_app.go
index 9a93afd..4307866 100644
--- a/core/installer/welcome/dodo_app.go
+++ b/core/installer/welcome/dodo_app.go
@@ -748,6 +748,11 @@
if err != nil {
return
}
+ // TODO(gio): get only available ones by owner
+ clusters, err := s.getClusters()
+ if err != nil {
+ return
+ }
apps := installer.NewInMemoryAppRepository(installer.CreateAllApps())
instanceAppStatus, err := installer.FindEnvApp(apps, "dodo-app-instance-status")
if err != nil {
@@ -768,7 +773,7 @@
}
s.l.Lock()
defer s.l.Unlock()
- resources, err := s.updateDodoApp(instanceAppStatus, req.Repository.Name, branch, s.getAppConfig(req.Repository.Name, branch).Namespace, networks, owner)
+ resources, err := s.updateDodoApp(instanceAppStatus, req.Repository.Name, branch, s.getAppConfig(req.Repository.Name, branch).Namespace, networks, clusters, owner)
if err = s.createCommit(req.Repository.Name, branch, req.After, commitMsg, err, resources); err != nil {
fmt.Printf("Error: %s\n", err.Error())
return
@@ -1046,6 +1051,11 @@
if err != nil {
return err
}
+ // TODO(gio): get only available ones by owner
+ clusters, err := s.getClusters()
+ if err != nil {
+ return err
+ }
apps := installer.NewInMemoryAppRepository(installer.CreateAllApps())
instanceApp, err := installer.FindEnvApp(apps, "dodo-app-instance")
if err != nil {
@@ -1062,7 +1072,7 @@
}
namespace := fmt.Sprintf("%s%s%s", s.env.NamespacePrefix, instanceApp.Namespace(), suffix)
s.setAppConfig(appName, branch, appConfig{namespace, network})
- resources, err := s.updateDodoApp(instanceAppStatus, appName, branch, namespace, networks, user)
+ resources, err := s.updateDodoApp(instanceAppStatus, appName, branch, namespace, networks, clusters, user)
if err != nil {
fmt.Printf("Error: %s\n", err.Error())
return err
@@ -1216,6 +1226,7 @@
branch string,
namespace string,
networks []installer.Network,
+ clusters []installer.Cluster,
owner string,
) (installer.ReleaseResources, error) {
repo, err := s.client.GetRepoBranch(name, branch)
@@ -1256,6 +1267,7 @@
installer.WithNoPublish(),
installer.WithConfig(&s.env),
installer.WithNetworks(networks),
+ installer.WithClusters(clusters),
installer.WithLocalChartGenerator(lg),
installer.WithNoLock(),
)
@@ -1280,6 +1292,7 @@
installer.WithNoPublish(),
installer.WithConfig(&s.env),
installer.WithNetworks(networks),
+ installer.WithClusters(clusters),
installer.WithLocalChartGenerator(lg),
installer.WithNoLock(),
); err != nil {
@@ -1323,6 +1336,20 @@
return s.nf.Filter(user, networks)
}
+func (s *DodoAppServer) getClusters() ([]installer.Cluster, error) {
+ addr := fmt.Sprintf("%s/api/clusters", s.envAppManagerAddr)
+ resp, err := http.Get(addr)
+ if err != nil {
+ return nil, err
+ }
+ clusters := []installer.Cluster{}
+ if json.NewDecoder(resp.Body).Decode(&clusters); err != nil {
+ return nil, err
+ }
+ fmt.Printf("CLUSTERS %+v\n", clusters)
+ return clusters, nil
+}
+
type publicNetworkData struct {
Name string `json:"name"`
Domain string `json:"domain"`