- dev-vm.sh: rewrite for macOS (bsdtar ISO extraction, Homebrew mkfs.ext4 detection, direct kernel boot, TCG acceleration, port 8080 forwarding) - inject-kubesolo.sh: add CA certificates bundle from builder so containerd can verify TLS when pulling from registries (Docker Hub, etc.) - 50-network.sh: add DNS fallback (10.0.2.3 + 8.8.8.8) when DHCP client doesn't populate /etc/resolv.conf - 90-kubesolo.sh: serve kubeconfig via HTTP on port 8080 for reliable retrieval from host, add 127.0.0.1 and 10.0.2.15 to API server SANs - portainer.go: add headless Service to Edge Agent manifest (required for agent peer discovery DNS lookup) - 10-parse-cmdline.sh + init.sh: add kubesolo.edge_id/edge_key boot params - 20-persistent-mount.sh: auto-format unformatted data disks on first boot - hack/fix-portainer-service.sh: helper to patch running cluster Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
152 lines
5.6 KiB
Go
152 lines
5.6 KiB
Go
package cloudinit
|
|
|
|
import (
|
|
"fmt"
|
|
"log/slog"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
// ApplyPortainer writes the Portainer Edge Agent deployment manifest
|
|
// based on cloud-init config. The manifest is applied by KubeSolo after
|
|
// the cluster is ready.
|
|
func ApplyPortainer(cfg *Config, manifestDir string) error {
|
|
if !cfg.Portainer.EdgeAgent.Enabled {
|
|
slog.Info("portainer edge agent not enabled, skipping")
|
|
return nil
|
|
}
|
|
|
|
ea := cfg.Portainer.EdgeAgent
|
|
if ea.EdgeID == "" || ea.EdgeKey == "" {
|
|
return fmt.Errorf("portainer edge-agent enabled but edge-id and edge-key are required")
|
|
}
|
|
if ea.PortainerURL == "" {
|
|
return fmt.Errorf("portainer edge-agent enabled but portainer-url is required")
|
|
}
|
|
|
|
image := ea.Image
|
|
if image == "" {
|
|
image = "portainer/agent:latest"
|
|
}
|
|
|
|
if err := os.MkdirAll(manifestDir, 0o755); err != nil {
|
|
return fmt.Errorf("creating manifest dir: %w", err)
|
|
}
|
|
|
|
manifest := buildEdgeAgentManifest(ea.EdgeID, ea.EdgeKey, ea.PortainerURL, image)
|
|
dest := filepath.Join(manifestDir, "portainer-edge-agent.yaml")
|
|
if err := os.WriteFile(dest, []byte(manifest), 0o644); err != nil {
|
|
return fmt.Errorf("writing edge agent manifest: %w", err)
|
|
}
|
|
|
|
slog.Info("portainer edge agent manifest written", "path", dest)
|
|
return nil
|
|
}
|
|
|
|
func buildEdgeAgentManifest(edgeID, edgeKey, portainerURL, image string) string {
|
|
var sb strings.Builder
|
|
|
|
sb.WriteString("# Auto-generated by KubeSolo OS cloud-init\n")
|
|
sb.WriteString("# Portainer Edge Agent deployment\n")
|
|
sb.WriteString("---\n")
|
|
sb.WriteString("apiVersion: v1\n")
|
|
sb.WriteString("kind: Namespace\n")
|
|
sb.WriteString("metadata:\n")
|
|
sb.WriteString(" name: portainer\n")
|
|
sb.WriteString(" labels:\n")
|
|
sb.WriteString(" app.kubernetes.io/name: portainer-agent\n")
|
|
sb.WriteString(" app.kubernetes.io/component: edge-agent\n")
|
|
sb.WriteString("---\n")
|
|
sb.WriteString("apiVersion: v1\n")
|
|
sb.WriteString("kind: ServiceAccount\n")
|
|
sb.WriteString("metadata:\n")
|
|
sb.WriteString(" name: portainer-sa-clusteradmin\n")
|
|
sb.WriteString(" namespace: portainer\n")
|
|
sb.WriteString("---\n")
|
|
sb.WriteString("apiVersion: rbac.authorization.k8s.io/v1\n")
|
|
sb.WriteString("kind: ClusterRoleBinding\n")
|
|
sb.WriteString("metadata:\n")
|
|
sb.WriteString(" name: portainer-crb-clusteradmin\n")
|
|
sb.WriteString("roleRef:\n")
|
|
sb.WriteString(" apiGroup: rbac.authorization.k8s.io\n")
|
|
sb.WriteString(" kind: ClusterRole\n")
|
|
sb.WriteString(" name: cluster-admin\n")
|
|
sb.WriteString("subjects:\n")
|
|
sb.WriteString(" - kind: ServiceAccount\n")
|
|
sb.WriteString(" name: portainer-sa-clusteradmin\n")
|
|
sb.WriteString(" namespace: portainer\n")
|
|
sb.WriteString("---\n")
|
|
sb.WriteString("apiVersion: v1\n")
|
|
sb.WriteString("kind: Service\n")
|
|
sb.WriteString("metadata:\n")
|
|
sb.WriteString(" name: portainer-agent\n")
|
|
sb.WriteString(" namespace: portainer\n")
|
|
sb.WriteString("spec:\n")
|
|
sb.WriteString(" clusterIP: None\n")
|
|
sb.WriteString(" selector:\n")
|
|
sb.WriteString(" app: portainer-agent\n")
|
|
sb.WriteString(" ports:\n")
|
|
sb.WriteString(" - name: agent\n")
|
|
sb.WriteString(" port: 9001\n")
|
|
sb.WriteString(" targetPort: 9001\n")
|
|
sb.WriteString(" protocol: TCP\n")
|
|
sb.WriteString("---\n")
|
|
sb.WriteString("apiVersion: apps/v1\n")
|
|
sb.WriteString("kind: Deployment\n")
|
|
sb.WriteString("metadata:\n")
|
|
sb.WriteString(" name: portainer-agent\n")
|
|
sb.WriteString(" namespace: portainer\n")
|
|
sb.WriteString(" labels:\n")
|
|
sb.WriteString(" app.kubernetes.io/name: portainer-agent\n")
|
|
sb.WriteString(" app.kubernetes.io/component: edge-agent\n")
|
|
sb.WriteString("spec:\n")
|
|
sb.WriteString(" replicas: 1\n")
|
|
sb.WriteString(" selector:\n")
|
|
sb.WriteString(" matchLabels:\n")
|
|
sb.WriteString(" app: portainer-agent\n")
|
|
sb.WriteString(" template:\n")
|
|
sb.WriteString(" metadata:\n")
|
|
sb.WriteString(" labels:\n")
|
|
sb.WriteString(" app: portainer-agent\n")
|
|
sb.WriteString(" spec:\n")
|
|
sb.WriteString(" serviceAccountName: portainer-sa-clusteradmin\n")
|
|
sb.WriteString(" containers:\n")
|
|
sb.WriteString(" - name: agent\n")
|
|
sb.WriteString(fmt.Sprintf(" image: %s\n", image))
|
|
sb.WriteString(" env:\n")
|
|
sb.WriteString(" - name: EDGE\n")
|
|
sb.WriteString(" value: \"1\"\n")
|
|
sb.WriteString(" - name: EDGE_ID\n")
|
|
sb.WriteString(fmt.Sprintf(" value: \"%s\"\n", edgeID))
|
|
sb.WriteString(" - name: EDGE_KEY\n")
|
|
sb.WriteString(fmt.Sprintf(" value: \"%s\"\n", edgeKey))
|
|
sb.WriteString(" - name: EDGE_INSECURE_POLL\n")
|
|
sb.WriteString(" value: \"1\"\n")
|
|
sb.WriteString(" - name: KUBERNETES_POD_IP\n")
|
|
sb.WriteString(" valueFrom:\n")
|
|
sb.WriteString(" fieldRef:\n")
|
|
sb.WriteString(" fieldPath: status.podIP\n")
|
|
sb.WriteString(" ports:\n")
|
|
sb.WriteString(" - containerPort: 9001\n")
|
|
sb.WriteString(" protocol: TCP\n")
|
|
sb.WriteString(" resources:\n")
|
|
sb.WriteString(" requests:\n")
|
|
sb.WriteString(" memory: 64Mi\n")
|
|
sb.WriteString(" cpu: 50m\n")
|
|
sb.WriteString(" limits:\n")
|
|
sb.WriteString(" memory: 256Mi\n")
|
|
sb.WriteString(" cpu: 500m\n")
|
|
sb.WriteString(" volumeMounts:\n")
|
|
sb.WriteString(" - name: docker-certs\n")
|
|
sb.WriteString(" mountPath: /certs\n")
|
|
sb.WriteString(" readOnly: true\n")
|
|
sb.WriteString(" volumes:\n")
|
|
sb.WriteString(" - name: docker-certs\n")
|
|
sb.WriteString(" emptyDir: {}\n")
|
|
sb.WriteString(" tolerations:\n")
|
|
sb.WriteString(" - operator: Exists\n")
|
|
|
|
return sb.String()
|
|
}
|