From 287e0f44c9567339fedab9ff6e7217e7c8948e03 Mon Sep 17 00:00:00 2001 From: Darren Shepherd Date: Tue, 22 Jan 2019 14:14:58 -0700 Subject: [PATCH] Prepare for initial release --- .dockerignore | 7 +- .gitignore | 1 + Dockerfile.dapper | 12 +- cmd/k3s/main.go | 4 +- docker-compose.yml | 28 ++ manifests/coredns.yaml | 175 +++++++ pkg/agent/config/config.go | 3 +- pkg/agent/run.go | 7 +- pkg/cli/agent/agent.go | 2 +- pkg/cli/cmds/agent.go | 23 +- pkg/cli/cmds/server.go | 39 +- pkg/cli/server/server.go | 18 +- pkg/daemons/agent/agent.go | 38 +- pkg/daemons/config/types.go | 6 +- pkg/daemons/control/server.go | 54 ++- pkg/deploy/controller.go | 281 +++++++++++ pkg/deploy/stage.go | 29 ++ pkg/deploy/zz_generated_bindata.go | 235 ++++++++++ pkg/server/server.go | 39 +- scripts/dev-agent.sh | 2 +- scripts/dev-server.sh | 1 + scripts/package-cli | 2 +- scripts/validate | 1 + types/apis/k3s.cattle.io/v1/schema.go | 3 +- types/apis/k3s.cattle.io/v1/types.go | 20 + .../v1/zz_generated_addon_controller.go | 440 ++++++++++++++++++ .../zz_generated_addon_lifecycle_adapter.go | 62 +++ .../k3s.cattle.io/v1/zz_generated_deepcopy.go | 100 ++++ .../v1/zz_generated_k8s_client.go | 20 + .../k3s.cattle.io/v1/zz_generated_scheme.go | 2 + types/codegen/main.go | 16 + 31 files changed, 1619 insertions(+), 51 deletions(-) create mode 100644 docker-compose.yml create mode 100644 manifests/coredns.yaml create mode 100644 pkg/deploy/controller.go create mode 100644 pkg/deploy/stage.go create mode 100644 pkg/deploy/zz_generated_bindata.go create mode 100644 types/apis/k3s.cattle.io/v1/zz_generated_addon_controller.go create mode 100644 types/apis/k3s.cattle.io/v1/zz_generated_addon_lifecycle_adapter.go diff --git a/.dockerignore b/.dockerignore index e29bdc4e4a..bd0daaabad 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,10 +1,9 @@ ./bin +./build +./pkg/data/zz_generated_bindata.go +./package/data.tar.gz ./.vagrant ./.dapper ./data-dir ./dist ./.trash-cache -./image/root -./image/agent -./image/go_build_agent -./image/main.squashfs diff --git a/.gitignore b/.gitignore index e541bee4ca..d1f88d56ed 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ /image/go_build_agent /image/main.squashfs /package/k3s +/package/data.tar.gz /pkg/data/zz_generated_bindata.go __pycache__ /tests/.pytest_cache/ diff --git a/Dockerfile.dapper b/Dockerfile.dapper index 2d3422c67c..0e018939ba 100644 --- a/Dockerfile.dapper +++ b/Dockerfile.dapper @@ -1,4 +1,5 @@ -FROM golang:1.11-alpine3.8 +FROM golang:1.11.4-alpine3.8 +# FROM arm=golang@sha256:fe81149b4e7f07ecb558fd16cdbcdb11e739846a046e38cc6e170aa39a67e7ec arm64=golang@sha256:da9c2d140ed4bf911ef8f6d9437912b80497c256ef2235c65836eac83d1c0ce7 RUN apk -U --no-cache add bash git gcc musl-dev docker vim less file curl wget ca-certificates jq linux-headers zlib-dev tar zip squashfs-tools npm coreutils \ python3 py3-pip python3-dev openssl-dev libffi-dev libseccomp libseccomp-dev make @@ -6,14 +7,15 @@ RUN pip3 install 'tox==3.6.0' RUN apk -U --no-cache --repository http://dl-3.alpinelinux.org/alpine/edge/main/ add sqlite-dev sqlite-static RUN go get -d golang.org/x/lint/golint && \ git -C /go/src/golang.org/x/lint/golint checkout -b current 06c8688daad7faa9da5a0c2f163a3d14aac986ca && \ - go install golang.org/x/lint/golint && \ - rm -rf /go/src /go/pkg + go install golang.org/x/lint/golint RUN go get -d github.com/alecthomas/gometalinter && \ git -C /go/src/github.com/alecthomas/gometalinter checkout -b current v2.0.11 && \ go install github.com/alecthomas/gometalinter && \ - gometalinter --install && \ - rm -rf /go/src /go/pkg + gometalinter --install +RUN rm -rf /go/src /go/pkg +ARG DAPPER_HOST_ARCH +ENV ARCH $DAPPER_HOST_ARCH ENV DAPPER_RUN_ARGS --privileged ENV DAPPER_ENV REPO TAG DRONE_TAG IMAGE_NAME ENV DAPPER_SOURCE /go/src/github.com/rancher/k3s/ diff --git a/cmd/k3s/main.go b/cmd/k3s/main.go index 7eb159fc8e..4e63c5770c 100644 --- a/cmd/k3s/main.go +++ b/cmd/k3s/main.go @@ -72,13 +72,13 @@ func getAssetAndDir(dataDir string) (string, string) { } func extract(asset, dir string) error { - logrus.Infof("Asset dir %s", dir) + logrus.Debugf("Asset dir %s", dir) if _, err := os.Stat(dir); err == nil { return nil } - logrus.Infof("Staging to dir %s", dir) + logrus.Infof("Preparing data dir %s", dir) content, err := data.Asset(asset) if err != nil { diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000000..2ba524a1af --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,28 @@ +version: '3' +services: + server: + image: rancher/k3s:fa08d60-dirty-amd64 + command: server --disable-agent + environment: + - K3S_CLUSTER_SECRET=somethingtotallyrandom + - K3S_KUBECONFIG_OUTPUT=/output/kubeconfig.yaml + - K3S_KUBECONFIG_MODE=666 + volumes: + - k3s-server:/var/lib/rancher/k3s + # This is just so that we get the kubeconfig file out + - .:/output + ports: + - 6443:6443 + + node: + image: rancher/k3s:fa08d60-dirty-amd64 + tmpfs: + - /run + - /var/run + privileged: true + environment: + - K3S_URL=https://server:6443 + - K3S_CLUSTER_SECRET=somethingtotallyrandom + +volumes: + k3s-server: {} diff --git a/manifests/coredns.yaml b/manifests/coredns.yaml new file mode 100644 index 0000000000..8d8637b544 --- /dev/null +++ b/manifests/coredns.yaml @@ -0,0 +1,175 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: coredns + namespace: kube-system +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRole +metadata: + labels: + kubernetes.io/bootstrapping: rbac-defaults + name: system:coredns +rules: +- apiGroups: + - "" + resources: + - endpoints + - services + - pods + - namespaces + verbs: + - list + - watch +- apiGroups: + - "" + resources: + - nodes + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + annotations: + rbac.authorization.kubernetes.io/autoupdate: "true" + labels: + kubernetes.io/bootstrapping: rbac-defaults + name: system:coredns +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:coredns +subjects: +- kind: ServiceAccount + name: coredns + namespace: kube-system +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: coredns + namespace: kube-system +data: + Corefile: | + .:53 { + errors + health + kubernetes cluster.local in-addr.arpa ip6.arpa { + pods insecure + upstream + fallthrough in-addr.arpa ip6.arpa + } + prometheus :9153 + proxy . 1.1.1.1 + cache 30 + loop + reload + loadbalance + } +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: coredns + namespace: kube-system + labels: + k8s-app: kube-dns + kubernetes.io/name: "CoreDNS" +spec: + #replicas: 1 + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 1 + selector: + matchLabels: + k8s-app: kube-dns + template: + metadata: + labels: + k8s-app: kube-dns + spec: + serviceAccountName: coredns + tolerations: + - key: "CriticalAddonsOnly" + operator: "Exists" + nodeSelector: + beta.kubernetes.io/os: linux + containers: + - name: coredns + image: coredns/coredns:1.3.0 + imagePullPolicy: IfNotPresent + resources: + limits: + memory: 170Mi + requests: + cpu: 100m + memory: 70Mi + args: [ "-conf", "/etc/coredns/Corefile" ] + volumeMounts: + - name: config-volume + mountPath: /etc/coredns + readOnly: true + ports: + - containerPort: 53 + name: dns + protocol: UDP + - containerPort: 53 + name: dns-tcp + protocol: TCP + - containerPort: 9153 + name: metrics + protocol: TCP + securityContext: + allowPrivilegeEscalation: false + capabilities: + add: + - NET_BIND_SERVICE + drop: + - all + readOnlyRootFilesystem: true + livenessProbe: + httpGet: + path: /health + port: 8080 + scheme: HTTP + initialDelaySeconds: 60 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + dnsPolicy: Default + volumes: + - name: config-volume + configMap: + name: coredns + items: + - key: Corefile + path: Corefile +--- +apiVersion: v1 +kind: Service +metadata: + name: kube-dns + namespace: kube-system + annotations: + prometheus.io/port: "9153" + prometheus.io/scrape: "true" + labels: + k8s-app: kube-dns + kubernetes.io/cluster-service: "true" + kubernetes.io/name: "CoreDNS" +spec: + selector: + k8s-app: kube-dns + clusterIP: 10.43.0.10 + ports: + - name: dns + port: 53 + protocol: UDP + - name: dns-tcp + port: 53 + protocol: TCP + - name: metrics + port: 9153 + protocol: TCP diff --git a/pkg/agent/config/config.go b/pkg/agent/config/config.go index 4c2b4899bc..43533415cb 100644 --- a/pkg/agent/config/config.go +++ b/pkg/agent/config/config.go @@ -12,8 +12,6 @@ import ( "path/filepath" "time" - "k8s.io/kubernetes/pkg/kubelet/apis/deviceplugin/v1beta1" - "github.com/pkg/errors" "github.com/rancher/k3s/pkg/cli/cmds" "github.com/rancher/k3s/pkg/daemons/config" @@ -22,6 +20,7 @@ import ( "k8s.io/apimachinery/pkg/util/json" "k8s.io/apimachinery/pkg/util/net" "k8s.io/client-go/util/cert" + "k8s.io/kubernetes/pkg/kubelet/apis/deviceplugin/v1beta1" ) func Get(ctx context.Context, agent cmds.Agent) *config.Node { diff --git a/pkg/agent/run.go b/pkg/agent/run.go index 8b38cc8608..8ddaad2161 100644 --- a/pkg/agent/run.go +++ b/pkg/agent/run.go @@ -6,8 +6,6 @@ import ( "path/filepath" "time" - "github.com/sirupsen/logrus" - "github.com/rancher/k3s/pkg/agent/config" "github.com/rancher/k3s/pkg/agent/containerd" "github.com/rancher/k3s/pkg/agent/flannel" @@ -17,6 +15,7 @@ import ( "github.com/rancher/k3s/pkg/cli/cmds" "github.com/rancher/k3s/pkg/daemons/agent" "github.com/rancher/norman/pkg/clientaccess" + "github.com/sirupsen/logrus" ) func run(ctx context.Context, cfg cmds.Agent) error { @@ -65,6 +64,10 @@ func run(ctx context.Context, cfg cmds.Agent) error { func Run(ctx context.Context, cfg cmds.Agent) error { cfg.DataDir = filepath.Join(cfg.DataDir, "agent") + if cfg.ClusterSecret != "" { + cfg.Token = "K10node:" + cfg.ClusterSecret + } + for { tmpFile, err := clientaccess.AgentAccessInfoToTempKubeConfig("", cfg.ServerURL, cfg.Token) if err != nil { diff --git a/pkg/cli/agent/agent.go b/pkg/cli/agent/agent.go index c148e374cd..1d515e8f5d 100644 --- a/pkg/cli/agent/agent.go +++ b/pkg/cli/agent/agent.go @@ -17,7 +17,7 @@ func Run(ctx *cli.Context) error { return fmt.Errorf("agent must be ran as root") } - if cmds.AgentConfig.Token == "" { + if cmds.AgentConfig.Token == "" && cmds.AgentConfig.ClusterSecret == "" { return fmt.Errorf("--token is required") } diff --git a/pkg/cli/cmds/agent.go b/pkg/cli/cmds/agent.go index 53e74cffed..dccdb52c34 100644 --- a/pkg/cli/cmds/agent.go +++ b/pkg/cli/cmds/agent.go @@ -8,14 +8,15 @@ import ( ) type Agent struct { - Token string - ServerURL string - DataDir string - NodeIP string - NodeName string - Docker bool - NoFlannel bool - Debug bool + Token string + ServerURL string + DataDir string + NodeIP string + NodeName string + ClusterSecret string + Docker bool + NoFlannel bool + Debug bool AgentShared } @@ -74,6 +75,12 @@ func NewAgentCommand(action func(ctx *cli.Context) error) cli.Command { Usage: "Disable embedded flannel", Destination: &AgentConfig.NoFlannel, }, + cli.StringFlag{ + Name: "cluster-secret", + Usage: "Shared secret used to bootstrap a cluster", + Destination: &AgentConfig.ClusterSecret, + EnvVar: "K3S_CLUSTER_SECRET", + }, NodeNameFlag, NodeIPFlag, }, diff --git a/pkg/cli/cmds/server.go b/pkg/cli/cmds/server.go index 3836954fb7..b90b809b64 100644 --- a/pkg/cli/cmds/server.go +++ b/pkg/cli/cmds/server.go @@ -5,13 +5,16 @@ import ( ) type Server struct { - Log string - ClusterCIDR string - ServiceCIDR string - HTTPSPort int - HTTPPort int - DataDir string - DisableAgent bool + Log string + ClusterCIDR string + ClusterSecret string + ServiceCIDR string + HTTPSPort int + HTTPPort int + DataDir string + DisableAgent bool + KubeConfigOutput string + KubeConfigMode string } var ServerConfig Server @@ -55,6 +58,28 @@ func NewServerCommand(action func(*cli.Context) error) cli.Command { Usage: "Network CIDR to use for pod IPs", Destination: &ServerConfig.ClusterCIDR, }, + cli.StringFlag{ + Name: "cluster-secret", + Usage: "Shared secret used to bootstrap a cluster", + Destination: &ServerConfig.ClusterSecret, + EnvVar: "K3S_CLUSTER_SECRET", + }, + cli.StringSliceFlag{ + Name: "no-deploy", + Usage: "Do not deploy packaged manifests (example: coredns)", + }, + cli.StringFlag{ + Name: "write-kubeconfig,o", + Usage: "Write kubeconfig for admin client to this file", + Destination: &ServerConfig.KubeConfigOutput, + EnvVar: "K3S_KUBECONFIG_OUTPUT", + }, + cli.StringFlag{ + Name: "write-kubeconfig-mode", + Usage: "Write kubeconfig with this mode", + Destination: &ServerConfig.KubeConfigMode, + EnvVar: "K3S_KUBECONFIG_MODE", + }, NodeIPFlag, NodeNameFlag, }, diff --git a/pkg/cli/server/server.go b/pkg/cli/server/server.go index 2b317a8d36..c6bb14dbde 100644 --- a/pkg/cli/server/server.go +++ b/pkg/cli/server/server.go @@ -6,6 +6,7 @@ import ( "fmt" "os" "path/filepath" + "strings" "github.com/docker/docker/pkg/reexec" "github.com/natefinch/lumberjack" @@ -15,6 +16,8 @@ import ( "github.com/rancher/norman/signal" "github.com/sirupsen/logrus" "github.com/urfave/cli" + + _ "github.com/mattn/go-sqlite3" // ensure we have sqlite ) func setupLogging(app *cli.Context) { @@ -60,10 +63,23 @@ func run(app *cli.Context, cfg *cmds.Server) error { } serverConfig := server.Config{} + serverConfig.ControlConfig.ClusterSecret = cfg.ClusterSecret serverConfig.ControlConfig.DataDir = cfg.DataDir + serverConfig.ControlConfig.KubeConfigOutput = cfg.KubeConfigOutput + serverConfig.ControlConfig.KubeConfigMode = cfg.KubeConfigMode serverConfig.TLSConfig.HTTPSPort = cfg.HTTPSPort serverConfig.TLSConfig.HTTPPort = cfg.HTTPPort + // TODO: support etcd + serverConfig.ControlConfig.NoLeaderElect = true + + for _, noDeploy := range app.StringSlice("no-deploy") { + if !strings.HasSuffix(noDeploy, ".yaml") { + noDeploy = noDeploy + ".yaml" + } + serverConfig.ControlConfig.Skips = append(serverConfig.ControlConfig.Skips, noDeploy) + } + logrus.Info("Starting k3s ", app.App.Version) ctx := signal.SigTermCancelContext(context.Background()) certs, err := server.StartServer(ctx, &serverConfig) @@ -82,7 +98,7 @@ func run(app *cli.Context, cfg *cmds.Server) error { token := server.FormatToken(serverConfig.ControlConfig.Runtime.NodeToken, certs) agentConfig := cmds.AgentConfig - agentConfig.DataDir = serverConfig.ControlConfig.DataDir + agentConfig.DataDir = filepath.Dir(serverConfig.ControlConfig.DataDir) agentConfig.ServerURL = url agentConfig.Token = token diff --git a/pkg/daemons/agent/agent.go b/pkg/daemons/agent/agent.go index 1171eb6e3a..c04d3d76f9 100644 --- a/pkg/daemons/agent/agent.go +++ b/pkg/daemons/agent/agent.go @@ -1,15 +1,17 @@ package agent import ( + "bufio" "context" "math/rand" + "os" "path/filepath" + "strings" "time" - "k8s.io/apimachinery/pkg/util/net" - "github.com/rancher/k3s/pkg/daemons/config" "github.com/sirupsen/logrus" + "k8s.io/apimachinery/pkg/util/net" "k8s.io/apiserver/pkg/util/logs" app2 "k8s.io/kubernetes/cmd/kube-proxy/app" "k8s.io/kubernetes/cmd/kubelet/app" @@ -56,7 +58,6 @@ func kubelet(cfg *config.Agent) { "--kubeconfig", cfg.KubeConfig, "--eviction-hard", "imagefs.available<5%,nodefs.available<5%", "--eviction-minimum-reclaim", "imagefs.available=10%,nodefs.available=10%", - "--node-ip", cfg.NodeIP, "--fail-swap-on=false", //"--cgroup-root", "/k3s", "--cgroup-driver", "cgroupfs", @@ -92,6 +93,10 @@ func kubelet(cfg *config.Agent) { if err != nil || defaultIP.String() != cfg.NodeIP { args = append(args, "--node-ip", cfg.NodeIP) } + if !hasCFS() { + logrus.Warn("Disabling CPU quotas due to missing cpu.cfs_period_us") + args = append(args, "--cpu-cfs-quota=false") + } args = append(args, cfg.ExtraKubeletArgs...) command.SetArgs(args) @@ -101,3 +106,30 @@ func kubelet(cfg *config.Agent) { logrus.Fatalf("kubelet exited: %v", command.Execute()) }() } + +func hasCFS() bool { + f, err := os.Open("/proc/self/cgroup") + if err != nil { + return false + } + defer f.Close() + + scan := bufio.NewScanner(f) + for scan.Scan() { + parts := strings.Split(scan.Text(), ":") + if len(parts) < 3 { + continue + } + systems := strings.Split(parts[1], ",") + for _, system := range systems { + if system == "cpu" { + p := filepath.Join("/sys/fs/cgroup", parts[1], parts[2], "cpu.cfs_period_us") + if _, err := os.Stat(p); err == nil { + return true + } + } + } + } + + return false +} diff --git a/pkg/daemons/config/types.go b/pkg/daemons/config/types.go index 6f5b1922c0..2297d1d80c 100644 --- a/pkg/daemons/config/types.go +++ b/pkg/daemons/config/types.go @@ -48,11 +48,15 @@ type Agent struct { type Control struct { AdvertisePort int ListenPort int + ClusterSecret string ClusterIPRange *net.IPNet ServiceIPRange *net.IPNet ClusterDNS net.IP NoCoreDNS bool + KubeConfigOutput string + KubeConfigMode string DataDir string + Skips []string ETCDEndpoints []string ETCDKeyFile string ETCDCertFile string @@ -61,7 +65,7 @@ type Control struct { ExtraAPIArgs []string ExtraControllerArgs []string ExtraSchedulerAPIArgs []string - //NodeConfig Node + NoLeaderElect bool Runtime *ControlRuntime `json:"-"` } diff --git a/pkg/daemons/control/server.go b/pkg/daemons/control/server.go index d224be8ca6..b501394109 100644 --- a/pkg/daemons/control/server.go +++ b/pkg/daemons/control/server.go @@ -1,6 +1,7 @@ package control import ( + "bufio" "context" cryptorand "crypto/rand" "crypto/rsa" @@ -23,8 +24,6 @@ import ( "time" "github.com/rancher/k3s/pkg/daemons/config" - - _ "github.com/mattn/go-sqlite3" // sqlite "github.com/sirupsen/logrus" "k8s.io/apiserver/pkg/authentication/authenticator" certutil "k8s.io/client-go/util/cert" @@ -96,7 +95,6 @@ func Server(ctx context.Context, cfg *config.Control) error { func controllerManager(cfg *config.Control, runtime *config.ControlRuntime) { args := []string{ "--kubeconfig", runtime.KubeConfigSystem, - "--leader-elect=true", "--service-account-private-key-file", runtime.ServiceKey, "--allocate-node-cidrs", "--cluster-cidr", cfg.ClusterIPRange.String(), @@ -104,6 +102,9 @@ func controllerManager(cfg *config.Control, runtime *config.ControlRuntime) { "--port", "0", "--secure-port", "0", } + if cfg.NoLeaderElect { + args = append(args, "--leader-elect=false") + } args = append(args, cfg.ExtraControllerArgs...) command := cmapp.NewControllerManagerCommand() command.SetArgs(args) @@ -120,6 +121,9 @@ func scheduler(cfg *config.Control, runtime *config.ControlRuntime) { "--port", "0", "--secure-port", "0", } + if cfg.NoLeaderElect { + args = append(args, "--leader-elect=false") + } args = append(args, cfg.ExtraSchedulerAPIArgs...) command := sapp.NewSchedulerCommand() command.SetArgs(args) @@ -299,9 +303,47 @@ func readTokens(runtime *config.ControlRuntime) error { return nil } +func ensureNodeToken(config *config.Control, runtime *config.ControlRuntime) error { + if config.ClusterSecret == "" { + return nil + } + + f, err := os.Open(runtime.PasswdFile) + if err != nil { + return err + } + defer f.Close() + + buf := &strings.Builder{} + scan := bufio.NewScanner(f) + for scan.Scan() { + line := scan.Text() + parts := strings.Split(line, ",") + if len(parts) < 4 { + continue + } + if parts[1] == "node" { + if parts[0] == config.ClusterSecret { + return nil + } + parts[0] = config.ClusterSecret + line = strings.Join(parts, ",") + } + buf.WriteString(line) + buf.WriteString("\n") + } + + if scan.Err() != nil { + return scan.Err() + } + + f.Close() + return ioutil.WriteFile(runtime.PasswdFile, []byte(buf.String()), 0600) +} + func genUsers(config *config.Control, runtime *config.ControlRuntime) error { if s, err := os.Stat(runtime.PasswdFile); err == nil && s.Size() > 0 { - return nil + return ensureNodeToken(config, runtime) } adminToken, err := getToken() @@ -317,6 +359,10 @@ func genUsers(config *config.Control, runtime *config.ControlRuntime) error { return err } + if config.ClusterSecret != "" { + nodeToken = config.ClusterSecret + } + passwd := fmt.Sprintf(`%s,admin,admin,system:masters %s,system,system,system:masters %s,node,node,system:masters diff --git a/pkg/deploy/controller.go b/pkg/deploy/controller.go new file mode 100644 index 0000000000..36af091bff --- /dev/null +++ b/pkg/deploy/controller.go @@ -0,0 +1,281 @@ +package deploy + +import ( + "bufio" + "bytes" + "context" + "crypto/sha256" + "encoding/hex" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + "time" + + errors2 "github.com/pkg/errors" + + v1 "github.com/rancher/k3s/types/apis/k3s.cattle.io/v1" + "github.com/rancher/norman" + "github.com/rancher/norman/objectclient" + "github.com/rancher/norman/pkg/objectset" + "github.com/rancher/norman/types" + "github.com/sirupsen/logrus" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + yamlDecoder "k8s.io/apimachinery/pkg/util/yaml" + "k8s.io/client-go/discovery" + "k8s.io/client-go/rest" +) + +const ( + ns = "kube-system" +) + +func WatchFiles(ctx context.Context, skips []string, bases ...string) error { + server := norman.GetServer(ctx) + addons := v1.ClientsFrom(ctx).Addon + + w := &watcher{ + addonCache: addons.Cache(), + addons: addons, + skips: skips, + bases: bases, + restConfig: *server.Runtime.LocalConfig, + discovery: server.K8sClient.Discovery(), + clients: map[schema.GroupVersionKind]*objectclient.ObjectClient{}, + } + + addons.Enqueue("", "_start_") + addons.Interface().AddHandler(ctx, "addon-start", func(key string, _ *v1.Addon) (runtime.Object, error) { + w.started = true + return nil, w.listFiles(true) + }) + + w.start(ctx) + return nil +} + +type watcher struct { + started bool + addonCache v1.AddonClientCache + addons v1.AddonClient + bases []string + skips []string + restConfig rest.Config + discovery discovery.DiscoveryInterface + clients map[schema.GroupVersionKind]*objectclient.ObjectClient +} + +func (w *watcher) start(ctx context.Context) { + for { + select { + case <-ctx.Done(): + return + case <-time.After(15 * time.Second): + if err := w.listFiles(false); err != nil { + logrus.Errorf("failed to process config: %v", err) + } + } + } +} + +func (w *watcher) listFiles(force bool) error { + var errs []error + for _, base := range w.bases { + if err := w.listFilesIn(base, force); err != nil { + errs = append(errs, err) + } + + } + return types.NewErrors(errs...) +} + +func (w *watcher) listFilesIn(base string, force bool) error { + if !w.started { + return nil + } + + files, err := ioutil.ReadDir(base) + if os.IsNotExist(err) { + return nil + } else if err != nil { + return err + } + + skips := map[string]bool{} + for _, skip := range w.skips { + skips[skip] = true + } + for _, file := range files { + if strings.HasSuffix(file.Name(), ".skip") { + skips[strings.TrimSuffix(file.Name(), ".skip")] = true + } + + } + + var errs []error + for _, file := range files { + if strings.HasSuffix(file.Name(), ".skip") || skips[file.Name()] { + continue + } + p := filepath.Join(base, file.Name()) + if err := w.deploy(p, !force); err != nil { + errs = append(errs, errors2.Wrapf(err, "failed to process %s", p)) + } + } + + return types.NewErrors(errs...) +} + +func (w *watcher) deploy(path string, compareChecksum bool) error { + content, err := ioutil.ReadFile(path) + if err != nil { + return err + } + + name := name(path) + addon, err := w.addon(name) + if err != nil { + return err + } + + checksum := checksum(content) + if compareChecksum && checksum == addon.Spec.Checksum { + return nil + } + + objectSet, err := objectSet(content) + if err != nil { + return err + } + + clients, err := w.apply(addon, objectSet) + if w.clients == nil { + w.clients = map[schema.GroupVersionKind]*objectclient.ObjectClient{} + } + + addon.Spec.Source = path + addon.Spec.Checksum = checksum + addon.Status.GVKs = nil + + for gvk, client := range clients { + addon.Status.GVKs = append(addon.Status.GVKs, gvk) + w.clients[gvk] = client + } + + if addon.UID == "" { + _, err := w.addons.Create(&addon) + return err + } + + _, err = w.addons.Update(&addon) + return err +} + +func (w *watcher) addon(name string) (v1.Addon, error) { + addon, err := w.addonCache.Get(ns, name) + if errors.IsNotFound(err) { + addon = v1.NewAddon(ns, name, v1.Addon{}) + } else if err != nil { + return v1.Addon{}, err + } + return *addon, nil +} + +func (w *watcher) apply(addon v1.Addon, set *objectset.ObjectSet) (map[schema.GroupVersionKind]*objectclient.ObjectClient, error) { + var ( + err error + ) + + op := objectset.NewProcessor(addon.Name) + op.AllowDiscovery(w.discovery, w.restConfig) + + ds := op.NewDesiredSet(nil, set) + + for _, gvk := range addon.Status.GVKs { + client, ok := w.clients[gvk] + if !ok { + client, err = objectset.NewDiscoveredClient(gvk, w.restConfig, w.discovery) + if err != nil { + return nil, err + } + } + + ds.AddDiscoveredClient(gvk, client) + } + + if err := ds.Apply(); err != nil { + return nil, err + } + + return ds.DiscoveredClients(), nil +} + +func objectSet(content []byte) (*objectset.ObjectSet, error) { + objs, err := yamlToObjects(bytes.NewBuffer(content)) + if err != nil { + return nil, err + } + + os := objectset.NewObjectSet() + os.Add(objs...) + return os, nil +} + +func name(path string) string { + name := filepath.Base(path) + return strings.SplitN(name, ".", 2)[0] +} + +func checksum(bytes []byte) string { + d := sha256.Sum256(bytes) + return hex.EncodeToString(d[:]) +} + +func yamlToObjects(in io.Reader) ([]runtime.Object, error) { + var result []runtime.Object + reader := yamlDecoder.NewYAMLReader(bufio.NewReaderSize(in, 4096)) + for { + raw, err := reader.Read() + if err == io.EOF { + break + } + if err != nil { + return nil, err + } + + obj, err := toObjects(raw) + if err != nil { + return nil, err + } + + result = append(result, obj...) + } + + return result, nil +} + +func toObjects(bytes []byte) ([]runtime.Object, error) { + bytes, err := yamlDecoder.ToJSON(bytes) + if err != nil { + return nil, err + } + obj, _, err := unstructured.UnstructuredJSONScheme.Decode(bytes, nil, nil) + if err != nil { + return nil, err + } + + if l, ok := obj.(*unstructured.UnstructuredList); ok { + var result []runtime.Object + for _, obj := range l.Items { + copy := obj + result = append(result, ©) + } + return result, nil + } + + return []runtime.Object{obj}, nil +} diff --git a/pkg/deploy/stage.go b/pkg/deploy/stage.go new file mode 100644 index 0000000000..7cfd57395d --- /dev/null +++ b/pkg/deploy/stage.go @@ -0,0 +1,29 @@ +package deploy + +import ( + "io/ioutil" + "os" + "path/filepath" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +func Stage(dataDir string) error { + os.MkdirAll(dataDir, 0700) + + for _, name := range AssetNames() { + content, err := Asset(name) + if err != nil { + return err + } + + p := filepath.Join(dataDir, name) + logrus.Info("Writing manifest: ", p) + if err := ioutil.WriteFile(p, content, 0600); err != nil { + return errors.Wrapf(err, "failed to write to %s", name) + } + } + + return nil +} diff --git a/pkg/deploy/zz_generated_bindata.go b/pkg/deploy/zz_generated_bindata.go new file mode 100644 index 0000000000..e334b3a378 --- /dev/null +++ b/pkg/deploy/zz_generated_bindata.go @@ -0,0 +1,235 @@ +// Code generated by go-bindata. +// sources: +// manifests/coredns.yaml +// DO NOT EDIT! + +package deploy + +import ( + "bytes" + "compress/gzip" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + "time" +) + +func bindataRead(data []byte, name string) ([]byte, error) { + gz, err := gzip.NewReader(bytes.NewBuffer(data)) + if err != nil { + return nil, fmt.Errorf("Read %q: %v", name, err) + } + + var buf bytes.Buffer + _, err = io.Copy(&buf, gz) + clErr := gz.Close() + + if err != nil { + return nil, fmt.Errorf("Read %q: %v", name, err) + } + if clErr != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +type asset struct { + bytes []byte + info os.FileInfo +} + +type bindataFileInfo struct { + name string + size int64 + mode os.FileMode + modTime time.Time +} + +func (fi bindataFileInfo) Name() string { + return fi.name +} +func (fi bindataFileInfo) Size() int64 { + return fi.size +} +func (fi bindataFileInfo) Mode() os.FileMode { + return fi.mode +} +func (fi bindataFileInfo) ModTime() time.Time { + return fi.modTime +} +func (fi bindataFileInfo) IsDir() bool { + return false +} +func (fi bindataFileInfo) Sys() interface{} { + return nil +} + +var _corednsYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x56\x4d\x73\xdb\x36\x13\xbe\xeb\x57\x60\xf8\x5e\x5f\xca\xd2\xb8\x49\x5d\xdc\x12\xdb\x4d\x3d\xd3\xb8\x1a\xdb\xc9\xa5\xd3\xc9\xac\xc0\x95\x88\x1a\xc4\xa2\xc0\x52\xb1\xda\xe6\xbf\x77\xc0\x2f\x83\x34\x9d\x49\x32\xa1\x0e\x02\xb0\xd8\x67\x81\xfd\x78\x16\xe0\xf4\x7b\xf4\x41\x93\x95\xe2\xb0\x5e\xdc\x6b\x5b\x48\x71\x8b\xfe\xa0\x15\xbe\x52\x8a\x6a\xcb\x8b\x0a\x19\x0a\x60\x90\x0b\x21\x2c\x54\x28\x85\x22\x8f\x85\x0d\xdd\x3c\x38\x50\x28\xc5\x7d\xbd\xc5\x3c\x1c\x03\x63\xb5\xc8\xf3\x7c\x91\x42\xfb\x2d\xa8\x25\xd4\x5c\x92\xd7\x7f\x03\x6b\xb2\xcb\xfb\xb3\xb0\xd4\x74\x72\x58\x6f\x91\xa1\xb7\x7c\x6e\xea\xc0\xe8\x6f\xc8\xe0\xc8\xac\x81\x2d\x9a\x10\x47\xa2\xb1\xe3\x2d\x32\x36\xfa\x5b\x22\x0e\xec\xc1\x39\x6d\xf7\xad\xa1\xbc\xc0\x1d\xd4\x86\xc3\x70\xde\xf6\x54\xb2\x3f\xb6\xaf\x0d\x06\xb9\xc8\x05\x38\xfd\xc6\x53\xed\x1a\xe4\x5c\x64\xd9\x42\x08\x8f\x81\x6a\xaf\xb0\x5b\x43\x5b\x38\xd2\xb6\x01\xcb\x45\x68\x3d\xd3\x4e\x1c\x15\xed\x60\x70\x42\x9c\x1e\xd0\x6f\x3b\x5d\xa3\x03\x37\x83\x8f\xc0\xaa\xfc\x32\x7b\x96\x8a\x29\xcc\x1e\xf9\x7b\x38\xf4\xb5\xb6\x85\xb6\xfb\x91\x5f\xc1\x5a\xe2\x46\xbd\x73\xee\x1c\xee\xc8\xdf\x50\x33\xd5\xae\x00\x46\x29\x32\xf6\x35\x66\xdf\x3f\x3c\x64\xf0\x06\x77\xcd\xf9\x3a\x87\x7d\xe6\xc2\x0b\x21\x9e\xe6\xce\x33\xc8\xa1\xde\xfe\x89\x8a\x9b\xd8\xcf\xa6\xfa\x37\x27\xf8\x50\x3b\xe7\x64\x77\x7a\xff\x16\xdc\xb7\x94\x4d\xbf\xfd\x9c\x3c\xee\xb4\x41\x29\xfe\x6d\x7c\xba\x94\x2f\x4e\xc5\x3f\xcd\x30\x7e\xe8\x3d\xf9\x30\x4c\x4b\x04\xc3\xe5\x30\x7d\x0c\x80\x50\xad\x4b\x96\x86\x14\x18\xa1\x6d\x0e\x45\xe1\x97\xe0\x1d\x08\xed\x5e\xb6\x83\x47\x58\xd1\x64\xb4\xd0\x36\xa0\xaa\x3d\x26\xeb\xb5\x0b\xec\x11\xaa\x64\x69\x07\xc6\x70\xe9\xa9\xde\x97\xf3\xc0\xc3\xde\x4f\xc3\xc8\x79\xaa\x90\x4b\xac\x83\x90\x3f\xad\x5f\x9c\xa6\x82\x87\xa3\x58\x8a\xf5\xb2\xf9\x0d\xeb\x0a\x54\x89\xe2\x74\x35\x2c\x18\x22\x37\x4c\x3c\x1a\x82\x22\x91\x41\xb1\x05\x03\x56\xb5\x47\xff\xf4\x24\x48\xf8\xc0\x68\xe3\x30\x4c\xaa\xe4\x02\x9d\xa1\x63\x85\xdf\x46\x76\x93\xfc\x3f\x0b\x39\x38\xd7\x6d\x69\x15\xa7\x55\xd1\x02\x67\x31\xcc\x17\xd7\xb7\xd9\x22\x38\x54\x51\xfb\x7f\x1e\x9d\xd1\x0a\x82\x14\xd1\x09\xb1\x70\x18\xf7\xc7\x16\x98\x8f\x0e\xa5\xb8\x21\x63\xb4\xdd\xbf\x6b\x4a\xb0\x2d\xd9\x74\x45\x76\xee\xa8\xe0\xe1\x9d\x85\x03\x68\x03\xdb\x98\x47\x0d\x1c\x1a\x54\x4c\xbe\xdd\x53\x45\x4e\xfa\x35\x39\xf8\xfc\xd1\x19\x2b\x67\x06\xe0\xd4\x3b\x8d\xcf\x47\xfa\xcf\x5d\xbe\xbf\x5e\x33\x1e\x15\xdc\xf5\xc4\xc3\xcd\x3d\xc9\xa0\x4f\x39\x29\x7e\xb9\xb8\xc7\x63\x74\x99\xd7\xac\x15\x98\x57\x45\x41\x36\xfc\x66\xcd\x31\x4b\x92\x92\x5c\xd4\x24\x2f\x45\x76\xf9\xa0\x03\x87\x5e\x18\x59\xf5\x76\x74\xfd\xf8\xc5\x14\x98\xd0\x1b\x05\x29\x8c\xb6\xf5\x43\xb7\x49\x91\x65\xd0\x16\xfd\x70\x96\xfc\x49\x5a\xb4\x9f\xae\x60\xff\xb8\x7c\xd2\xfd\xcb\xf5\xf2\x74\xb9\x1a\x6f\xda\xd4\xc6\x6c\xc8\x68\x75\x94\xe2\x6a\x77\x4d\xbc\xf1\x18\xb0\x61\x9f\x3e\xb1\x93\x96\x30\xa4\xb7\xae\x34\x8f\x56\x62\x38\x2a\xf2\x47\x29\xd6\x3f\xae\xde\xea\x44\xe2\xf1\xaf\x1a\xc3\x74\xb7\x72\xb5\x14\xeb\xd5\xaa\x9a\xc5\x18\x41\x80\xdf\x07\x29\x7e\x17\x59\xae\xc8\xee\xb2\xff\x8b\xec\x04\x59\xf5\x97\x3a\xe9\xf9\x29\x13\x7f\x0c\x2a\x07\x32\x75\x85\x6f\x63\x54\x47\x71\xeb\xbd\x15\x69\x31\x6f\x37\x25\xf6\xab\xb8\x7f\x03\x5c\x4a\x91\x5a\x18\xdd\x05\x8a\x18\x67\x29\x62\xb7\x79\xa4\x0c\xf2\x63\x3b\x43\xa4\x36\xe4\x59\x8a\x84\x5d\xfa\x42\x1e\xe3\x3a\x4f\x4c\x8a\x8c\x14\xef\x2e\x36\x5f\x8b\x93\xb3\x72\xb3\x58\x77\xe7\x9f\xc1\x1a\x71\x5e\x8f\x56\x21\x7b\xad\xe6\x4f\x96\xa2\x35\xa4\xac\xf9\x78\x4e\x96\xf1\x81\xd3\xd0\x82\x31\xf4\x71\xe3\xf5\x41\x1b\xdc\xe3\x65\x50\x60\x9a\xfa\x91\x91\xa5\x43\xea\x6e\x05\x0e\xb6\xda\x68\xd6\x38\x49\x0e\x28\x8a\xf1\x42\x2e\xae\x2f\xef\x3e\xbc\xbe\xba\xbe\xf8\x70\x7b\x79\xf3\xfe\xea\xfc\x72\x24\x2e\x3c\xb9\xa9\x02\x18\x33\x13\xb8\x1b\x22\xfe\x59\x1b\xec\x7a\xf1\x38\x8c\x46\x1f\xd0\x62\x08\x1b\x4f\x5b\x4c\xf1\x4a\x66\xf7\x06\x79\x6c\xc2\xb5\x89\x32\x69\x78\xa2\x4b\x07\x29\xce\x56\x67\xab\xd1\x72\x50\x25\x46\x27\xff\x72\x77\xb7\x49\x04\xda\x6a\xd6\x60\x2e\xd0\xc0\xf1\x16\x15\xd9\x22\x48\xf1\x32\x55\x65\x5d\x21\xd5\x3c\x08\x5f\x24\xb2\x50\x2b\x85\x21\xdc\x95\x1e\x43\x49\xa6\x68\xd9\xb5\xff\x76\xa0\x4d\xed\x31\x91\xf6\xba\x85\x0d\x7d\xd9\x5f\xb4\x4f\xa0\x4e\xd0\x56\xc5\x57\x54\x8d\xea\x1f\x19\x63\xf7\xcc\x13\x53\x73\x61\xc6\x2a\x4c\xc3\xd5\x30\x6a\x5f\xca\x23\x59\xef\xe9\x41\xf8\xec\x73\xa7\x7b\x3f\xcd\xb4\xcd\xa4\x03\x3c\xdb\x37\x9f\x3c\x3f\x1f\x5f\x08\x91\x8c\xdb\xa0\x66\xb1\x6c\xb2\x19\x71\x50\x1e\xdc\xb3\xcf\xd0\x2f\x68\xc3\xdd\xf3\x28\xef\x7a\x52\x82\xf4\xa5\x0d\x7b\xdc\x52\xe7\x6c\x76\x36\xae\x36\x91\x7a\x97\x3f\x9c\x2e\x57\xcb\x75\x4c\xb4\x81\xbf\xf2\x09\x3b\xb9\x94\x76\xa6\x24\x95\xcf\x50\xd0\x33\x0a\x2d\x77\xe4\x33\x2c\xe3\xc6\x64\x34\x56\xf9\x2f\x00\x00\xff\xff\x73\x85\x8d\x7b\x11\x0e\x00\x00") + +func corednsYamlBytes() ([]byte, error) { + return bindataRead( + _corednsYaml, + "coredns.yaml", + ) +} + +func corednsYaml() (*asset, error) { + bytes, err := corednsYamlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "coredns.yaml", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +// Asset loads and returns the asset for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func Asset(name string) ([]byte, error) { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) + } + return a.bytes, nil + } + return nil, fmt.Errorf("Asset %s not found", name) +} + +// MustAsset is like Asset but panics when Asset would return an error. +// It simplifies safe initialization of global variables. +func MustAsset(name string) []byte { + a, err := Asset(name) + if err != nil { + panic("asset: Asset(" + name + "): " + err.Error()) + } + + return a +} + +// AssetInfo loads and returns the asset info for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func AssetInfo(name string) (os.FileInfo, error) { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) + } + return a.info, nil + } + return nil, fmt.Errorf("AssetInfo %s not found", name) +} + +// AssetNames returns the names of the assets. +func AssetNames() []string { + names := make([]string, 0, len(_bindata)) + for name := range _bindata { + names = append(names, name) + } + return names +} + +// _bindata is a table, holding each asset generator, mapped to its name. +var _bindata = map[string]func() (*asset, error){ + "coredns.yaml": corednsYaml, +} + +// AssetDir returns the file names below a certain +// directory embedded in the file by go-bindata. +// For example if you run go-bindata on data/... and data contains the +// following hierarchy: +// data/ +// foo.txt +// img/ +// a.png +// b.png +// then AssetDir("data") would return []string{"foo.txt", "img"} +// AssetDir("data/img") would return []string{"a.png", "b.png"} +// AssetDir("foo.txt") and AssetDir("notexist") would return an error +// AssetDir("") will return []string{"data"}. +func AssetDir(name string) ([]string, error) { + node := _bintree + if len(name) != 0 { + cannonicalName := strings.Replace(name, "\\", "/", -1) + pathList := strings.Split(cannonicalName, "/") + for _, p := range pathList { + node = node.Children[p] + if node == nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + } + } + if node.Func != nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + rv := make([]string, 0, len(node.Children)) + for childName := range node.Children { + rv = append(rv, childName) + } + return rv, nil +} + +type bintree struct { + Func func() (*asset, error) + Children map[string]*bintree +} +var _bintree = &bintree{nil, map[string]*bintree{ + "coredns.yaml": &bintree{corednsYaml, map[string]*bintree{}}, +}} + +// RestoreAsset restores an asset under the given directory +func RestoreAsset(dir, name string) error { + data, err := Asset(name) + if err != nil { + return err + } + info, err := AssetInfo(name) + if err != nil { + return err + } + err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) + if err != nil { + return err + } + err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + if err != nil { + return err + } + err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) + if err != nil { + return err + } + return nil +} + +// RestoreAssets restores an asset under the given directory recursively +func RestoreAssets(dir, name string) error { + children, err := AssetDir(name) + // File + if err != nil { + return RestoreAsset(dir, name) + } + // Dir + for _, child := range children { + err = RestoreAssets(dir, filepath.Join(name, child)) + if err != nil { + return err + } + } + return nil +} + +func _filePath(dir, name string) string { + cannonicalName := strings.Replace(name, "\\", "/", -1) + return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) +} + diff --git a/pkg/server/server.go b/pkg/server/server.go index 1623a646c8..102fdc456f 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -9,12 +9,14 @@ import ( net2 "net" "os" "path/filepath" + "strconv" "strings" "time" "github.com/pkg/errors" "github.com/rancher/k3s/pkg/daemons/config" "github.com/rancher/k3s/pkg/daemons/control" + "github.com/rancher/k3s/pkg/deploy" "github.com/rancher/k3s/pkg/tls" v1 "github.com/rancher/k3s/types/apis/k3s.cattle.io/v1" "github.com/rancher/norman" @@ -89,6 +91,7 @@ func startNorman(ctx context.Context, tlsConfig *dynamiclistener.UserConfig, con CRDs: map[*types.APIVersion][]string{ &v1.APIVersion: { v1.ListenerConfigGroupVersionKind.Kind, + v1.AddonGroupVersionKind.Kind, }, }, IgnoredKubeConfigEnv: true, @@ -96,6 +99,18 @@ func startNorman(ctx context.Context, tlsConfig *dynamiclistener.UserConfig, con tlsServer, err = tls.NewServer(ctx, v1.ClientsFrom(ctx).ListenerConfig, *tlsConfig) return ctx, err }, + MasterControllers: []norman.ControllerRegister{ + func(ctx context.Context) error { + dataDir := filepath.Join(config.DataDir, "manifests") + if err := deploy.Stage(dataDir); err != nil { + return err + } + if err := deploy.WatchFiles(ctx, config.Skips, dataDir); err != nil { + return err + } + return nil + }, + }, } ctx, _, err = normanConfig.Build(ctx, nil) @@ -136,7 +151,7 @@ func printTokens(certs, advertiseIP string, tlsConfig *dynamiclistener.UserConfi } if len(nodeFile) > 0 { - printToken(tlsConfig.HTTPSPort, advertiseIP, "To join node to cluster:", nodeFile, "agent") + printToken(tlsConfig.HTTPSPort, advertiseIP, "To join node to cluster:", "agent") } } @@ -151,10 +166,23 @@ func writeKubeConfig(certs string, tlsConfig *dynamiclistener.UserConfig, config def = false } + if config.KubeConfigOutput != "" { + kubeConfig = config.KubeConfigOutput + } + if err = clientaccess.AgentAccessInfoToKubeConfig(kubeConfig, url, clientToken); err != nil { logrus.Errorf("Failed to generate kubeconfig: %v", err) } + if config.KubeConfigMode != "" { + mode, err := strconv.ParseInt(config.KubeConfigMode, 8, 0) + if err == nil { + os.Chmod(kubeConfig, os.FileMode(mode)) + } else { + logrus.Errorf("failed to set %s to mode %s: %v", kubeConfig, mode, err) + } + } + logrus.Infof("Wrote kubeconfig %s", kubeConfig) if def { logrus.Infof("Run: %s kubectl", filepath.Base(os.Args[0])) @@ -193,12 +221,7 @@ func readTokenFile(file string) (string, error) { return strings.TrimSpace(string(content)), nil } -func printToken(httpsPort int, advertiseIP, prefix, file, cmd string) { - token, err := readTokenFile(file) - if err != nil { - logrus.Error(err) - } - +func printToken(httpsPort int, advertiseIP, prefix, cmd string) { ip := advertiseIP if ip == "" { hostIP, err := net.ChooseHostInterface() @@ -208,7 +231,7 @@ func printToken(httpsPort int, advertiseIP, prefix, file, cmd string) { ip = hostIP.String() } - logrus.Infof("%s k3s %s -s https://%s:%d -t %s", prefix, cmd, ip, httpsPort, token) + logrus.Infof("%s k3s %s -s https://%s:%d -t ${NODE_TOKEN}", prefix, cmd, ip, httpsPort) } func FormatToken(token string, certs string) string { diff --git a/scripts/dev-agent.sh b/scripts/dev-agent.sh index 0e9be1be96..d14cb6daec 100755 --- a/scripts/dev-agent.sh +++ b/scripts/dev-agent.sh @@ -15,4 +15,4 @@ else fi echo Starting agent -sudo env "PATH=$(pwd)/bin:$PATH" ./bin/k3s-agent agent -s https://localhost:6443 -t $(<${HOME}/.rancher/k3s/server/node-token) "$@" +sudo env "PATH=$(pwd)/bin:$PATH" ./bin/k3s-agent --debug agent -s https://localhost:6443 -t $(<${HOME}/.rancher/k3s/server/node-token) "$@" diff --git a/scripts/dev-server.sh b/scripts/dev-server.sh index 19c41304f8..59a427ad75 100755 --- a/scripts/dev-server.sh +++ b/scripts/dev-server.sh @@ -1,6 +1,7 @@ #!/bin/bash set -e +mkdir -p $(dirname $0)/../bin cd $(dirname $0)/../bin echo Running diff --git a/scripts/package-cli b/scripts/package-cli index 79f727e540..372efb58ff 100755 --- a/scripts/package-cli +++ b/scripts/package-cli @@ -9,7 +9,7 @@ cd $(dirname $0)/.. curl --compressed -sfL https://github.com/ibuildthecloud/k3s-root/releases/download/${ROOT_VERSION}/k3s-root-${ARCH}.tar | tar xf - -rm -rf bin/kubectl bin/k3s-agent bin/k3s build/data +rm -rf bin/kubectl bin/k3s-agent bin/k3s-server bin/kubectl bin/k3s build/data ln -s containerd bin/k3s-agent ln -s containerd bin/k3s-server ln -s containerd bin/kubectl diff --git a/scripts/validate b/scripts/validate index de4865ab9e..40e5d952ea 100755 --- a/scripts/validate +++ b/scripts/validate @@ -24,4 +24,5 @@ test -z "$failed" echo Running: go fmt test -z "$(go fmt ${PACKAGES} | \ grep -v 'pkg/data/zz_generated_bindata.go' | \ + grep -v 'pkg/deploy/zz_generated_bindata.go' | \ tee /dev/stderr)" diff --git a/types/apis/k3s.cattle.io/v1/schema.go b/types/apis/k3s.cattle.io/v1/schema.go index 47e1564b8a..38f872bd9b 100644 --- a/types/apis/k3s.cattle.io/v1/schema.go +++ b/types/apis/k3s.cattle.io/v1/schema.go @@ -13,5 +13,6 @@ var ( } Schemas = factory.Schemas(&APIVersion). - MustImport(&APIVersion, ListenerConfig{}) + MustImport(&APIVersion, ListenerConfig{}). + MustImport(&APIVersion, Addon{}) ) diff --git a/types/apis/k3s.cattle.io/v1/types.go b/types/apis/k3s.cattle.io/v1/types.go index bb7dc4f921..54c3d78459 100644 --- a/types/apis/k3s.cattle.io/v1/types.go +++ b/types/apis/k3s.cattle.io/v1/types.go @@ -4,6 +4,7 @@ import ( "github.com/rancher/norman/pkg/dynamiclistener" "github.com/rancher/norman/types" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" ) type ListenerConfig struct { @@ -14,3 +15,22 @@ type ListenerConfig struct { Status dynamiclistener.ListenerStatus `json:"status,omitempty"` } + +type Addon struct { + types.Namespaced + + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec AddonSpec `json:"spec,omitempty"` + Status AddonStatus `json:"status,omitempty"` +} + +type AddonSpec struct { + Source string `json:"source,omitempty"` + Checksum string `json:"checksum,omitempty"` +} + +type AddonStatus struct { + GVKs []schema.GroupVersionKind `json:"gvks,omitempty"` +} diff --git a/types/apis/k3s.cattle.io/v1/zz_generated_addon_controller.go b/types/apis/k3s.cattle.io/v1/zz_generated_addon_controller.go new file mode 100644 index 0000000000..7525837675 --- /dev/null +++ b/types/apis/k3s.cattle.io/v1/zz_generated_addon_controller.go @@ -0,0 +1,440 @@ +package v1 + +import ( + "context" + + "github.com/rancher/norman/controller" + "github.com/rancher/norman/objectclient" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/tools/cache" +) + +var ( + AddonGroupVersionKind = schema.GroupVersionKind{ + Version: Version, + Group: GroupName, + Kind: "Addon", + } + AddonResource = metav1.APIResource{ + Name: "addons", + SingularName: "addon", + Namespaced: true, + + Kind: AddonGroupVersionKind.Kind, + } +) + +func NewAddon(namespace, name string, obj Addon) *Addon { + obj.APIVersion, obj.Kind = AddonGroupVersionKind.ToAPIVersionAndKind() + obj.Name = name + obj.Namespace = namespace + return &obj +} + +type AddonList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Addon +} + +type AddonHandlerFunc func(key string, obj *Addon) (runtime.Object, error) + +type AddonChangeHandlerFunc func(obj *Addon) (runtime.Object, error) + +type AddonLister interface { + List(namespace string, selector labels.Selector) (ret []*Addon, err error) + Get(namespace, name string) (*Addon, error) +} + +type AddonController interface { + Generic() controller.GenericController + Informer() cache.SharedIndexInformer + Lister() AddonLister + AddHandler(ctx context.Context, name string, handler AddonHandlerFunc) + AddClusterScopedHandler(ctx context.Context, name, clusterName string, handler AddonHandlerFunc) + Enqueue(namespace, name string) + Sync(ctx context.Context) error + Start(ctx context.Context, threadiness int) error +} + +type AddonInterface interface { + ObjectClient() *objectclient.ObjectClient + Create(*Addon) (*Addon, error) + GetNamespaced(namespace, name string, opts metav1.GetOptions) (*Addon, error) + Get(name string, opts metav1.GetOptions) (*Addon, error) + Update(*Addon) (*Addon, error) + Delete(name string, options *metav1.DeleteOptions) error + DeleteNamespaced(namespace, name string, options *metav1.DeleteOptions) error + List(opts metav1.ListOptions) (*AddonList, error) + Watch(opts metav1.ListOptions) (watch.Interface, error) + DeleteCollection(deleteOpts *metav1.DeleteOptions, listOpts metav1.ListOptions) error + Controller() AddonController + AddHandler(ctx context.Context, name string, sync AddonHandlerFunc) + AddLifecycle(ctx context.Context, name string, lifecycle AddonLifecycle) + AddClusterScopedHandler(ctx context.Context, name, clusterName string, sync AddonHandlerFunc) + AddClusterScopedLifecycle(ctx context.Context, name, clusterName string, lifecycle AddonLifecycle) +} + +type addonLister struct { + controller *addonController +} + +func (l *addonLister) List(namespace string, selector labels.Selector) (ret []*Addon, err error) { + err = cache.ListAllByNamespace(l.controller.Informer().GetIndexer(), namespace, selector, func(obj interface{}) { + ret = append(ret, obj.(*Addon)) + }) + return +} + +func (l *addonLister) Get(namespace, name string) (*Addon, error) { + var key string + if namespace != "" { + key = namespace + "/" + name + } else { + key = name + } + obj, exists, err := l.controller.Informer().GetIndexer().GetByKey(key) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(schema.GroupResource{ + Group: AddonGroupVersionKind.Group, + Resource: "addon", + }, key) + } + return obj.(*Addon), nil +} + +type addonController struct { + controller.GenericController +} + +func (c *addonController) Generic() controller.GenericController { + return c.GenericController +} + +func (c *addonController) Lister() AddonLister { + return &addonLister{ + controller: c, + } +} + +func (c *addonController) AddHandler(ctx context.Context, name string, handler AddonHandlerFunc) { + c.GenericController.AddHandler(ctx, name, func(key string, obj interface{}) (interface{}, error) { + if obj == nil { + return handler(key, nil) + } else if v, ok := obj.(*Addon); ok { + return handler(key, v) + } else { + return nil, nil + } + }) +} + +func (c *addonController) AddClusterScopedHandler(ctx context.Context, name, cluster string, handler AddonHandlerFunc) { + c.GenericController.AddHandler(ctx, name, func(key string, obj interface{}) (interface{}, error) { + if obj == nil { + return handler(key, nil) + } else if v, ok := obj.(*Addon); ok && controller.ObjectInCluster(cluster, obj) { + return handler(key, v) + } else { + return nil, nil + } + }) +} + +type addonFactory struct { +} + +func (c addonFactory) Object() runtime.Object { + return &Addon{} +} + +func (c addonFactory) List() runtime.Object { + return &AddonList{} +} + +func (s *addonClient) Controller() AddonController { + s.client.Lock() + defer s.client.Unlock() + + c, ok := s.client.addonControllers[s.ns] + if ok { + return c + } + + genericController := controller.NewGenericController(AddonGroupVersionKind.Kind+"Controller", + s.objectClient) + + c = &addonController{ + GenericController: genericController, + } + + s.client.addonControllers[s.ns] = c + s.client.starters = append(s.client.starters, c) + + return c +} + +type addonClient struct { + client *Client + ns string + objectClient *objectclient.ObjectClient + controller AddonController +} + +func (s *addonClient) ObjectClient() *objectclient.ObjectClient { + return s.objectClient +} + +func (s *addonClient) Create(o *Addon) (*Addon, error) { + obj, err := s.objectClient.Create(o) + return obj.(*Addon), err +} + +func (s *addonClient) Get(name string, opts metav1.GetOptions) (*Addon, error) { + obj, err := s.objectClient.Get(name, opts) + return obj.(*Addon), err +} + +func (s *addonClient) GetNamespaced(namespace, name string, opts metav1.GetOptions) (*Addon, error) { + obj, err := s.objectClient.GetNamespaced(namespace, name, opts) + return obj.(*Addon), err +} + +func (s *addonClient) Update(o *Addon) (*Addon, error) { + obj, err := s.objectClient.Update(o.Name, o) + return obj.(*Addon), err +} + +func (s *addonClient) Delete(name string, options *metav1.DeleteOptions) error { + return s.objectClient.Delete(name, options) +} + +func (s *addonClient) DeleteNamespaced(namespace, name string, options *metav1.DeleteOptions) error { + return s.objectClient.DeleteNamespaced(namespace, name, options) +} + +func (s *addonClient) List(opts metav1.ListOptions) (*AddonList, error) { + obj, err := s.objectClient.List(opts) + return obj.(*AddonList), err +} + +func (s *addonClient) Watch(opts metav1.ListOptions) (watch.Interface, error) { + return s.objectClient.Watch(opts) +} + +// Patch applies the patch and returns the patched deployment. +func (s *addonClient) Patch(o *Addon, patchType types.PatchType, data []byte, subresources ...string) (*Addon, error) { + obj, err := s.objectClient.Patch(o.Name, o, patchType, data, subresources...) + return obj.(*Addon), err +} + +func (s *addonClient) DeleteCollection(deleteOpts *metav1.DeleteOptions, listOpts metav1.ListOptions) error { + return s.objectClient.DeleteCollection(deleteOpts, listOpts) +} + +func (s *addonClient) AddHandler(ctx context.Context, name string, sync AddonHandlerFunc) { + s.Controller().AddHandler(ctx, name, sync) +} + +func (s *addonClient) AddLifecycle(ctx context.Context, name string, lifecycle AddonLifecycle) { + sync := NewAddonLifecycleAdapter(name, false, s, lifecycle) + s.Controller().AddHandler(ctx, name, sync) +} + +func (s *addonClient) AddClusterScopedHandler(ctx context.Context, name, clusterName string, sync AddonHandlerFunc) { + s.Controller().AddClusterScopedHandler(ctx, name, clusterName, sync) +} + +func (s *addonClient) AddClusterScopedLifecycle(ctx context.Context, name, clusterName string, lifecycle AddonLifecycle) { + sync := NewAddonLifecycleAdapter(name+"_"+clusterName, true, s, lifecycle) + s.Controller().AddClusterScopedHandler(ctx, name, clusterName, sync) +} + +type AddonIndexer func(obj *Addon) ([]string, error) + +type AddonClientCache interface { + Get(namespace, name string) (*Addon, error) + List(namespace string, selector labels.Selector) ([]*Addon, error) + + Index(name string, indexer AddonIndexer) + GetIndexed(name, key string) ([]*Addon, error) +} + +type AddonClient interface { + Create(*Addon) (*Addon, error) + Get(namespace, name string, opts metav1.GetOptions) (*Addon, error) + Update(*Addon) (*Addon, error) + Delete(namespace, name string, options *metav1.DeleteOptions) error + List(namespace string, opts metav1.ListOptions) (*AddonList, error) + Watch(opts metav1.ListOptions) (watch.Interface, error) + + Cache() AddonClientCache + + OnCreate(ctx context.Context, name string, sync AddonChangeHandlerFunc) + OnChange(ctx context.Context, name string, sync AddonChangeHandlerFunc) + OnRemove(ctx context.Context, name string, sync AddonChangeHandlerFunc) + Enqueue(namespace, name string) + + Generic() controller.GenericController + ObjectClient() *objectclient.ObjectClient + Interface() AddonInterface +} + +type addonClientCache struct { + client *addonClient2 +} + +type addonClient2 struct { + iface AddonInterface + controller AddonController +} + +func (n *addonClient2) Interface() AddonInterface { + return n.iface +} + +func (n *addonClient2) Generic() controller.GenericController { + return n.iface.Controller().Generic() +} + +func (n *addonClient2) ObjectClient() *objectclient.ObjectClient { + return n.Interface().ObjectClient() +} + +func (n *addonClient2) Enqueue(namespace, name string) { + n.iface.Controller().Enqueue(namespace, name) +} + +func (n *addonClient2) Create(obj *Addon) (*Addon, error) { + return n.iface.Create(obj) +} + +func (n *addonClient2) Get(namespace, name string, opts metav1.GetOptions) (*Addon, error) { + return n.iface.GetNamespaced(namespace, name, opts) +} + +func (n *addonClient2) Update(obj *Addon) (*Addon, error) { + return n.iface.Update(obj) +} + +func (n *addonClient2) Delete(namespace, name string, options *metav1.DeleteOptions) error { + return n.iface.DeleteNamespaced(namespace, name, options) +} + +func (n *addonClient2) List(namespace string, opts metav1.ListOptions) (*AddonList, error) { + return n.iface.List(opts) +} + +func (n *addonClient2) Watch(opts metav1.ListOptions) (watch.Interface, error) { + return n.iface.Watch(opts) +} + +func (n *addonClientCache) Get(namespace, name string) (*Addon, error) { + return n.client.controller.Lister().Get(namespace, name) +} + +func (n *addonClientCache) List(namespace string, selector labels.Selector) ([]*Addon, error) { + return n.client.controller.Lister().List(namespace, selector) +} + +func (n *addonClient2) Cache() AddonClientCache { + n.loadController() + return &addonClientCache{ + client: n, + } +} + +func (n *addonClient2) OnCreate(ctx context.Context, name string, sync AddonChangeHandlerFunc) { + n.loadController() + n.iface.AddLifecycle(ctx, name+"-create", &addonLifecycleDelegate{create: sync}) +} + +func (n *addonClient2) OnChange(ctx context.Context, name string, sync AddonChangeHandlerFunc) { + n.loadController() + n.iface.AddLifecycle(ctx, name+"-change", &addonLifecycleDelegate{update: sync}) +} + +func (n *addonClient2) OnRemove(ctx context.Context, name string, sync AddonChangeHandlerFunc) { + n.loadController() + n.iface.AddLifecycle(ctx, name, &addonLifecycleDelegate{remove: sync}) +} + +func (n *addonClientCache) Index(name string, indexer AddonIndexer) { + err := n.client.controller.Informer().GetIndexer().AddIndexers(map[string]cache.IndexFunc{ + name: func(obj interface{}) ([]string, error) { + if v, ok := obj.(*Addon); ok { + return indexer(v) + } + return nil, nil + }, + }) + + if err != nil { + panic(err) + } +} + +func (n *addonClientCache) GetIndexed(name, key string) ([]*Addon, error) { + var result []*Addon + objs, err := n.client.controller.Informer().GetIndexer().ByIndex(name, key) + if err != nil { + return nil, err + } + for _, obj := range objs { + if v, ok := obj.(*Addon); ok { + result = append(result, v) + } + } + + return result, nil +} + +func (n *addonClient2) loadController() { + if n.controller == nil { + n.controller = n.iface.Controller() + } +} + +type addonLifecycleDelegate struct { + create AddonChangeHandlerFunc + update AddonChangeHandlerFunc + remove AddonChangeHandlerFunc +} + +func (n *addonLifecycleDelegate) HasCreate() bool { + return n.create != nil +} + +func (n *addonLifecycleDelegate) Create(obj *Addon) (runtime.Object, error) { + if n.create == nil { + return obj, nil + } + return n.create(obj) +} + +func (n *addonLifecycleDelegate) HasFinalize() bool { + return n.remove != nil +} + +func (n *addonLifecycleDelegate) Remove(obj *Addon) (runtime.Object, error) { + if n.remove == nil { + return obj, nil + } + return n.remove(obj) +} + +func (n *addonLifecycleDelegate) Updated(obj *Addon) (runtime.Object, error) { + if n.update == nil { + return obj, nil + } + return n.update(obj) +} diff --git a/types/apis/k3s.cattle.io/v1/zz_generated_addon_lifecycle_adapter.go b/types/apis/k3s.cattle.io/v1/zz_generated_addon_lifecycle_adapter.go new file mode 100644 index 0000000000..9dc1513d2f --- /dev/null +++ b/types/apis/k3s.cattle.io/v1/zz_generated_addon_lifecycle_adapter.go @@ -0,0 +1,62 @@ +package v1 + +import ( + "github.com/rancher/norman/lifecycle" + "k8s.io/apimachinery/pkg/runtime" +) + +type AddonLifecycle interface { + Create(obj *Addon) (runtime.Object, error) + Remove(obj *Addon) (runtime.Object, error) + Updated(obj *Addon) (runtime.Object, error) +} + +type addonLifecycleAdapter struct { + lifecycle AddonLifecycle +} + +func (w *addonLifecycleAdapter) HasCreate() bool { + o, ok := w.lifecycle.(lifecycle.ObjectLifecycleCondition) + return !ok || o.HasCreate() +} + +func (w *addonLifecycleAdapter) HasFinalize() bool { + o, ok := w.lifecycle.(lifecycle.ObjectLifecycleCondition) + return !ok || o.HasFinalize() +} + +func (w *addonLifecycleAdapter) Create(obj runtime.Object) (runtime.Object, error) { + o, err := w.lifecycle.Create(obj.(*Addon)) + if o == nil { + return nil, err + } + return o, err +} + +func (w *addonLifecycleAdapter) Finalize(obj runtime.Object) (runtime.Object, error) { + o, err := w.lifecycle.Remove(obj.(*Addon)) + if o == nil { + return nil, err + } + return o, err +} + +func (w *addonLifecycleAdapter) Updated(obj runtime.Object) (runtime.Object, error) { + o, err := w.lifecycle.Updated(obj.(*Addon)) + if o == nil { + return nil, err + } + return o, err +} + +func NewAddonLifecycleAdapter(name string, clusterScoped bool, client AddonInterface, l AddonLifecycle) AddonHandlerFunc { + adapter := &addonLifecycleAdapter{lifecycle: l} + syncFn := lifecycle.NewObjectLifecycleAdapter(name, clusterScoped, adapter, client.ObjectClient()) + return func(key string, obj *Addon) (runtime.Object, error) { + newObj, err := syncFn(key, obj) + if o, ok := newObj.(runtime.Object); ok { + return o, err + } + return nil, err + } +} diff --git a/types/apis/k3s.cattle.io/v1/zz_generated_deepcopy.go b/types/apis/k3s.cattle.io/v1/zz_generated_deepcopy.go index ac63e31185..dd09e6b374 100644 --- a/types/apis/k3s.cattle.io/v1/zz_generated_deepcopy.go +++ b/types/apis/k3s.cattle.io/v1/zz_generated_deepcopy.go @@ -2,8 +2,108 @@ package v1 import ( runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Addon) DeepCopyInto(out *Addon) { + *out = *in + out.Namespaced = in.Namespaced + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Addon. +func (in *Addon) DeepCopy() *Addon { + if in == nil { + return nil + } + out := new(Addon) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Addon) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AddonList) DeepCopyInto(out *AddonList) { + *out = *in + out.TypeMeta = in.TypeMeta + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Addon, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AddonList. +func (in *AddonList) DeepCopy() *AddonList { + if in == nil { + return nil + } + out := new(AddonList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *AddonList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AddonSpec) DeepCopyInto(out *AddonSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AddonSpec. +func (in *AddonSpec) DeepCopy() *AddonSpec { + if in == nil { + return nil + } + out := new(AddonSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AddonStatus) DeepCopyInto(out *AddonStatus) { + *out = *in + if in.GVKs != nil { + in, out := &in.GVKs, &out.GVKs + *out = make([]schema.GroupVersionKind, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AddonStatus. +func (in *AddonStatus) DeepCopy() *AddonStatus { + if in == nil { + return nil + } + out := new(AddonStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ListenerConfig) DeepCopyInto(out *ListenerConfig) { *out = *in diff --git a/types/apis/k3s.cattle.io/v1/zz_generated_k8s_client.go b/types/apis/k3s.cattle.io/v1/zz_generated_k8s_client.go index f46b3f301b..48dc4342f0 100644 --- a/types/apis/k3s.cattle.io/v1/zz_generated_k8s_client.go +++ b/types/apis/k3s.cattle.io/v1/zz_generated_k8s_client.go @@ -21,12 +21,14 @@ type Interface interface { controller.Starter ListenerConfigsGetter + AddonsGetter } type Clients struct { Interface Interface ListenerConfig ListenerConfigClient + Addon AddonClient } type Client struct { @@ -35,6 +37,7 @@ type Client struct { starters []controller.Starter listenerConfigControllers map[string]ListenerConfigController + addonControllers map[string]AddonController } func Factory(ctx context.Context, config rest.Config) (context.Context, controller.Starter, error) { @@ -73,6 +76,9 @@ func NewClientsFromInterface(iface Interface) *Clients { ListenerConfig: &listenerConfigClient2{ iface: iface.ListenerConfigs(""), }, + Addon: &addonClient2{ + iface: iface.Addons(""), + }, } } @@ -90,6 +96,7 @@ func NewForConfig(config rest.Config) (Interface, error) { restClient: restClient, listenerConfigControllers: map[string]ListenerConfigController{}, + addonControllers: map[string]AddonController{}, }, nil } @@ -117,3 +124,16 @@ func (c *Client) ListenerConfigs(namespace string) ListenerConfigInterface { objectClient: objectClient, } } + +type AddonsGetter interface { + Addons(namespace string) AddonInterface +} + +func (c *Client) Addons(namespace string) AddonInterface { + objectClient := objectclient.NewObjectClient(namespace, c.restClient, &AddonResource, AddonGroupVersionKind, addonFactory{}) + return &addonClient{ + ns: namespace, + client: c, + objectClient: objectClient, + } +} diff --git a/types/apis/k3s.cattle.io/v1/zz_generated_scheme.go b/types/apis/k3s.cattle.io/v1/zz_generated_scheme.go index 9744026d89..40870d6e58 100644 --- a/types/apis/k3s.cattle.io/v1/zz_generated_scheme.go +++ b/types/apis/k3s.cattle.io/v1/zz_generated_scheme.go @@ -35,6 +35,8 @@ func addKnownTypes(scheme *runtime.Scheme) error { &ListenerConfig{}, &ListenerConfigList{}, + &Addon{}, + &AddonList{}, ) return nil } diff --git a/types/codegen/main.go b/types/codegen/main.go index 13489cc4c1..15c06dd087 100644 --- a/types/codegen/main.go +++ b/types/codegen/main.go @@ -28,6 +28,22 @@ func main() { if err := bindata.Translate(bc); err != nil { logrus.Fatal(err) } + + bc = &bindata.Config{ + Input: []bindata.InputConfig{ + { + Path: "manifests", + }, + }, + Package: "deploy", + NoMetadata: true, + Prefix: "manifests/", + Output: "pkg/deploy/zz_generated_bindata.go", + } + if err := bindata.Translate(bc); err != nil { + logrus.Fatal(err) + } + if err := generator.DefaultGenerate(v1.Schemas, basePackage, false, nil); err != nil { logrus.Fatal(err) }