diff --git a/cluster.yml b/cluster.yml index 5060abee..6b95ae38 100644 --- a/cluster.yml +++ b/cluster.yml @@ -1,4 +1,5 @@ --- +auth_type: x509 network_plugin: flannel hosts: - hostname: server1 diff --git a/cluster/certificates.go b/cluster/certificates.go index 30df41f1..43232eca 100644 --- a/cluster/certificates.go +++ b/cluster/certificates.go @@ -12,8 +12,8 @@ import ( "k8s.io/client-go/util/cert" ) -func SetUpAuthentication(kubeCluster, currentCluster *Cluster, authType string) error { - if authType == X509AuthenticationProvider { +func SetUpAuthentication(kubeCluster, currentCluster *Cluster) error { + if kubeCluster.AuthType == X509AuthenticationProvider { var err error if currentCluster != nil { kubeCluster.Certificates, err = getClusterCerts(kubeCluster.KubeClient) diff --git a/cluster/cluster.go b/cluster/cluster.go index d57f4ccc..d4858bcf 100644 --- a/cluster/cluster.go +++ b/cluster/cluster.go @@ -5,6 +5,7 @@ import ( "net" "github.com/Sirupsen/logrus" + "github.com/alena1108/cluster-controller/client/v1" "github.com/rancher/rke/hosts" "github.com/rancher/rke/pki" "github.com/rancher/rke/services" @@ -13,8 +14,7 @@ import ( ) type Cluster struct { - Services services.Services `yaml:"services"` - Hosts []hosts.Host `yaml:"hosts"` + v1.RKEConfig `yaml:",inline"` EtcdHosts []hosts.Host WorkerHosts []hosts.Host ControlPlaneHosts []hosts.Host @@ -22,7 +22,6 @@ type Cluster struct { KubernetesServiceIP net.IP Certificates map[string]pki.CertificatePKI ClusterDomain string - NetworkPlugin string `yaml:"network_plugin"` ClusterCIDR string ClusterDNSServer string } diff --git a/cluster/hosts.go b/cluster/hosts.go index f675c396..1ec786c8 100644 --- a/cluster/hosts.go +++ b/cluster/hosts.go @@ -38,13 +38,16 @@ func (c *Cluster) InvertIndexHosts() error { for _, host := range c.Hosts { for _, role := range host.Role { logrus.Debugf("Host: " + host.Hostname + " has role: " + role) + newHost := hosts.Host{ + RKEConfigHost: host, + } switch role { case services.ETCDRole: - c.EtcdHosts = append(c.EtcdHosts, host) + c.EtcdHosts = append(c.EtcdHosts, newHost) case services.ControlRole: - c.ControlPlaneHosts = append(c.ControlPlaneHosts, host) + c.ControlPlaneHosts = append(c.ControlPlaneHosts, newHost) case services.WorkerRole: - c.WorkerHosts = append(c.WorkerHosts, host) + c.WorkerHosts = append(c.WorkerHosts, newHost) default: return fmt.Errorf("Failed to recognize host [%s] role %s", host.Hostname, role) } @@ -53,8 +56,8 @@ func (c *Cluster) InvertIndexHosts() error { return nil } -func (c *Cluster) SetUpHosts(authType string) error { - if authType == X509AuthenticationProvider { +func (c *Cluster) SetUpHosts() error { + if c.AuthType == X509AuthenticationProvider { logrus.Infof("[certificates] Deploying kubernetes certificates to Cluster nodes") err := pki.DeployCertificatesOnMasters(c.ControlPlaneHosts, c.Certificates) if err != nil { diff --git a/cmd/cluster.go b/cmd/cluster.go index 6f751375..aaf75ffd 100644 --- a/cmd/cluster.go +++ b/cmd/cluster.go @@ -21,12 +21,6 @@ func ClusterCommand() cli.Command { Value: "cluster.yml", EnvVar: "CLUSTER_FILE", }, - cli.StringFlag{ - Name: "auth-type", - Usage: "Specify authentication type", - Value: "x509", - EnvVar: "AUTH_TYPE", - }, } return cli.Command{ Name: "cluster", @@ -44,7 +38,7 @@ func ClusterCommand() cli.Command { } } -func ClusterUp(clusterFile, authType string) (string, string, string, string, error) { +func ClusterUp(clusterFile string) (string, string, string, string, error) { logrus.Infof("Building Kubernetes cluster") var APIURL, caCrt, clientCert, clientKey string kubeCluster, err := cluster.ParseConfig(clusterFile) @@ -62,12 +56,12 @@ func ClusterUp(clusterFile, authType string) (string, string, string, string, er return APIURL, caCrt, clientCert, clientKey, err } - err = cluster.SetUpAuthentication(kubeCluster, currentCluster, authType) + err = cluster.SetUpAuthentication(kubeCluster, currentCluster) if err != nil { return APIURL, caCrt, clientCert, clientKey, err } - err = kubeCluster.SetUpHosts(authType) + err = kubeCluster.SetUpHosts() if err != nil { return APIURL, caCrt, clientCert, clientKey, err } @@ -99,12 +93,11 @@ func ClusterUp(clusterFile, authType string) (string, string, string, string, er } func clusterUpFromCli(ctx *cli.Context) error { - authType := ctx.String("auth-type") clusterFile, err := resolveClusterFile(ctx) if err != nil { return fmt.Errorf("Failed to resolve cluster file: %v", err) } - _, _, _, _, err = ClusterUp(clusterFile, authType) + _, _, _, _, err = ClusterUp(clusterFile) return err } diff --git a/hosts/hosts.go b/hosts/hosts.go index 3492b17f..511b818b 100644 --- a/hosts/hosts.go +++ b/hosts/hosts.go @@ -2,19 +2,15 @@ package hosts import ( "github.com/Sirupsen/logrus" + "github.com/alena1108/cluster-controller/client/v1" "github.com/docker/docker/client" "github.com/rancher/rke/k8s" "k8s.io/client-go/kubernetes" ) type Host struct { - IP string `yaml:"ip"` - AdvertiseAddress string `yaml:"advertise_address"` - Role []string `yaml:"role"` - Hostname string `yaml:"hostname"` - User string `yaml:"user"` - DockerSocket string `yaml:"docker_socket"` - DClient *client.Client + v1.RKEConfigHost + DClient *client.Client } func ReconcileWorkers(currentWorkers []Host, newWorkers []Host, kubeClient *kubernetes.Clientset) error { diff --git a/pki/pki_test.go b/pki/pki_test.go index c290b39b..3db28cf9 100644 --- a/pki/pki_test.go +++ b/pki/pki_test.go @@ -6,6 +6,7 @@ import ( "net" "testing" + "github.com/alena1108/cluster-controller/client/v1" "github.com/rancher/rke/hosts" ) @@ -17,10 +18,13 @@ const ( func TestPKI(t *testing.T) { cpHosts := []hosts.Host{ hosts.Host{ - IP: "1.1.1.1", - AdvertiseAddress: "192.168.1.5", - Role: []string{"controlplane"}, - Hostname: "server1", + RKEConfigHost: v1.RKEConfigHost{ + IP: "1.1.1.1", + AdvertiseAddress: "192.168.1.5", + Role: []string{"controlplane"}, + Hostname: "server1", + }, + DClient: nil, }, } certificateMap, err := StartCertificatesGeneration(cpHosts, cpHosts, FakeClusterDomain, net.ParseIP(FakeKubernetesServiceIP)) diff --git a/services/controlplane.go b/services/controlplane.go index 50f4e417..03716a2b 100644 --- a/services/controlplane.go +++ b/services/controlplane.go @@ -2,10 +2,11 @@ package services import ( "github.com/Sirupsen/logrus" + "github.com/alena1108/cluster-controller/client/v1" "github.com/rancher/rke/hosts" ) -func RunControlPlane(controlHosts []hosts.Host, etcdHosts []hosts.Host, controlServices Services) error { +func RunControlPlane(controlHosts []hosts.Host, etcdHosts []hosts.Host, controlServices v1.RKEConfigServices) error { logrus.Infof("[%s] Building up Controller Plane..", ControlRole) for _, host := range controlHosts { // run kubeapi diff --git a/services/etcd.go b/services/etcd.go index d33abe69..c02d54c2 100644 --- a/services/etcd.go +++ b/services/etcd.go @@ -2,13 +2,14 @@ package services import ( "github.com/Sirupsen/logrus" + "github.com/alena1108/cluster-controller/client/v1" "github.com/docker/docker/api/types/container" "github.com/docker/go-connections/nat" "github.com/rancher/rke/docker" "github.com/rancher/rke/hosts" ) -func RunEtcdPlane(etcdHosts []hosts.Host, etcdService Etcd) error { +func RunEtcdPlane(etcdHosts []hosts.Host, etcdService v1.ETCDService) error { logrus.Infof("[%s] Building up Etcd Plane..", ETCDRole) for _, host := range etcdHosts { imageCfg, hostCfg := buildEtcdConfig(host, etcdService) @@ -21,7 +22,7 @@ func RunEtcdPlane(etcdHosts []hosts.Host, etcdService Etcd) error { return nil } -func buildEtcdConfig(host hosts.Host, etcdService Etcd) (*container.Config, *container.HostConfig) { +func buildEtcdConfig(host hosts.Host, etcdService v1.ETCDService) (*container.Config, *container.HostConfig) { imageCfg := &container.Config{ Image: etcdService.Image, Cmd: []string{"/usr/local/bin/etcd", diff --git a/services/kubeapi.go b/services/kubeapi.go index a4e94c28..7ee30856 100644 --- a/services/kubeapi.go +++ b/services/kubeapi.go @@ -1,6 +1,7 @@ package services import ( + "github.com/alena1108/cluster-controller/client/v1" "github.com/docker/docker/api/types/container" "github.com/docker/go-connections/nat" "github.com/rancher/rke/docker" @@ -8,13 +9,13 @@ import ( "github.com/rancher/rke/pki" ) -func runKubeAPI(host hosts.Host, etcdHosts []hosts.Host, kubeAPIService KubeAPI) error { +func runKubeAPI(host hosts.Host, etcdHosts []hosts.Host, kubeAPIService v1.KubeAPIService) error { etcdConnString := getEtcdConnString(etcdHosts) imageCfg, hostCfg := buildKubeAPIConfig(host, kubeAPIService, etcdConnString) return docker.DoRunContainer(host.DClient, imageCfg, hostCfg, KubeAPIContainerName, host.Hostname, ControlRole) } -func buildKubeAPIConfig(host hosts.Host, kubeAPIService KubeAPI, etcdConnString string) (*container.Config, *container.HostConfig) { +func buildKubeAPIConfig(host hosts.Host, kubeAPIService v1.KubeAPIService, etcdConnString string) (*container.Config, *container.HostConfig) { imageCfg := &container.Config{ Image: kubeAPIService.Image, Cmd: []string{"/hyperkube", diff --git a/services/kubecontroller.go b/services/kubecontroller.go index f33b198c..0ed04462 100644 --- a/services/kubecontroller.go +++ b/services/kubecontroller.go @@ -1,18 +1,19 @@ package services import ( + "github.com/alena1108/cluster-controller/client/v1" "github.com/docker/docker/api/types/container" "github.com/rancher/rke/docker" "github.com/rancher/rke/hosts" "github.com/rancher/rke/pki" ) -func runKubeController(host hosts.Host, kubeControllerService KubeController) error { +func runKubeController(host hosts.Host, kubeControllerService v1.KubeControllerService) error { imageCfg, hostCfg := buildKubeControllerConfig(kubeControllerService) return docker.DoRunContainer(host.DClient, imageCfg, hostCfg, KubeControllerContainerName, host.Hostname, ControlRole) } -func buildKubeControllerConfig(kubeControllerService KubeController) (*container.Config, *container.HostConfig) { +func buildKubeControllerConfig(kubeControllerService v1.KubeControllerService) (*container.Config, *container.HostConfig) { imageCfg := &container.Config{ Image: kubeControllerService.Image, Cmd: []string{"/hyperkube", diff --git a/services/kubelet.go b/services/kubelet.go index a050d79a..082a75ad 100644 --- a/services/kubelet.go +++ b/services/kubelet.go @@ -1,6 +1,7 @@ package services import ( + "github.com/alena1108/cluster-controller/client/v1" "github.com/docker/docker/api/types/container" "github.com/docker/go-connections/nat" "github.com/rancher/rke/docker" @@ -8,12 +9,12 @@ import ( "github.com/rancher/rke/pki" ) -func runKubelet(host hosts.Host, kubeletService Kubelet, isMaster bool) error { +func runKubelet(host hosts.Host, kubeletService v1.KubeletService, isMaster bool) error { imageCfg, hostCfg := buildKubeletConfig(host, kubeletService, isMaster) return docker.DoRunContainer(host.DClient, imageCfg, hostCfg, KubeletContainerName, host.Hostname, WorkerRole) } -func buildKubeletConfig(host hosts.Host, kubeletService Kubelet, isMaster bool) (*container.Config, *container.HostConfig) { +func buildKubeletConfig(host hosts.Host, kubeletService v1.KubeletService, isMaster bool) (*container.Config, *container.HostConfig) { imageCfg := &container.Config{ Image: kubeletService.Image, Cmd: []string{"/hyperkube", diff --git a/services/kubeproxy.go b/services/kubeproxy.go index fa304a46..69add76a 100644 --- a/services/kubeproxy.go +++ b/services/kubeproxy.go @@ -1,18 +1,19 @@ package services import ( + "github.com/alena1108/cluster-controller/client/v1" "github.com/docker/docker/api/types/container" "github.com/rancher/rke/docker" "github.com/rancher/rke/hosts" "github.com/rancher/rke/pki" ) -func runKubeproxy(host hosts.Host, kubeproxyService Kubeproxy) error { +func runKubeproxy(host hosts.Host, kubeproxyService v1.KubeproxyService) error { imageCfg, hostCfg := buildKubeproxyConfig(host, kubeproxyService) return docker.DoRunContainer(host.DClient, imageCfg, hostCfg, KubeproxyContainerName, host.Hostname, WorkerRole) } -func buildKubeproxyConfig(host hosts.Host, kubeproxyService Kubeproxy) (*container.Config, *container.HostConfig) { +func buildKubeproxyConfig(host hosts.Host, kubeproxyService v1.KubeproxyService) (*container.Config, *container.HostConfig) { imageCfg := &container.Config{ Image: kubeproxyService.Image, Cmd: []string{"/hyperkube", diff --git a/services/scheduler.go b/services/scheduler.go index 7fe7e6a0..4dcbf87f 100644 --- a/services/scheduler.go +++ b/services/scheduler.go @@ -1,18 +1,19 @@ package services import ( + "github.com/alena1108/cluster-controller/client/v1" "github.com/docker/docker/api/types/container" "github.com/rancher/rke/docker" "github.com/rancher/rke/hosts" "github.com/rancher/rke/pki" ) -func runScheduler(host hosts.Host, schedulerService Scheduler) error { +func runScheduler(host hosts.Host, schedulerService v1.SchedulerService) error { imageCfg, hostCfg := buildSchedulerConfig(host, schedulerService) return docker.DoRunContainer(host.DClient, imageCfg, hostCfg, SchedulerContainerName, host.Hostname, ControlRole) } -func buildSchedulerConfig(host hosts.Host, schedulerService Scheduler) (*container.Config, *container.HostConfig) { +func buildSchedulerConfig(host hosts.Host, schedulerService v1.SchedulerService) (*container.Config, *container.HostConfig) { imageCfg := &container.Config{ Image: schedulerService.Image, Cmd: []string{"/hyperkube", diff --git a/services/types.go b/services/types.go deleted file mode 100644 index 093e5db4..00000000 --- a/services/types.go +++ /dev/null @@ -1,40 +0,0 @@ -package services - -type Services struct { - Etcd Etcd `yaml:"etcd"` - KubeAPI KubeAPI `yaml:"kube-api"` - KubeController KubeController `yaml:"kube-controller"` - Scheduler Scheduler `yaml:"scheduler"` - Kubelet Kubelet `yaml:"kubelet"` - Kubeproxy Kubeproxy `yaml:"kubeproxy"` -} - -type Etcd struct { - Image string `yaml:"image"` -} - -type KubeAPI struct { - Image string `yaml:"image"` - ServiceClusterIPRange string `yaml:"service_cluster_ip_range"` -} - -type KubeController struct { - Image string `yaml:"image"` - ClusterCIDR string `yaml:"cluster_cidr"` - ServiceClusterIPRange string `yaml:"service_cluster_ip_range"` -} - -type Kubelet struct { - Image string `yaml:"image"` - ClusterDomain string `yaml:"cluster_domain"` - InfraContainerImage string `yaml:"infra_container_image"` - ClusterDNSServer string `yaml:"cluster_dns_server"` -} - -type Kubeproxy struct { - Image string `yaml:"image"` -} - -type Scheduler struct { - Image string `yaml:"image"` -} diff --git a/services/workerplane.go b/services/workerplane.go index 2c014c3c..d6e7c0a4 100644 --- a/services/workerplane.go +++ b/services/workerplane.go @@ -2,10 +2,11 @@ package services import ( "github.com/Sirupsen/logrus" + "github.com/alena1108/cluster-controller/client/v1" "github.com/rancher/rke/hosts" ) -func RunWorkerPlane(controlHosts []hosts.Host, workerHosts []hosts.Host, workerServices Services) error { +func RunWorkerPlane(controlHosts []hosts.Host, workerHosts []hosts.Host, workerServices v1.RKEConfigServices) error { logrus.Infof("[%s] Building up Worker Plane..", WorkerRole) for _, host := range controlHosts { // only one master for now diff --git a/vendor.conf b/vendor.conf index ba185c72..8293eee4 100644 --- a/vendor.conf +++ b/vendor.conf @@ -1,18 +1,19 @@ # package github.com/rancher/rke -github.com/Sirupsen/logrus v0.10.0 -github.com/urfave/cli v1.18.0 -golang.org/x/crypto 2509b142fb2b797aa7587dad548f113b2c0f20ce -gopkg.in/yaml.v2 eb3733d160e74a9c7e442f435eb3bea458e1d19f -github.com/docker/docker ecf4125b85e0faa57d2739348e0d453c1d24d10c -github.com/docker/distribution 3800056b8832cf6075e78b282ac010131d8687bc -github.com/docker/go-connections 3ede32e2033de7505e6500d6c868c2b9ed9f169d -github.com/docker/go-units 0dadbb0345b35ec7ef35e228dabb8de89a65bf52 -golang.org/x/net 186fd3fc8194a5e9980a82230d69c1ff7134229f -github.com/opencontainers/go-digest 279bed98673dd5bef374d3b6e4b09e2af76183bf -github.com/gogo/protobuf 117892bf1866fbaa2318c03e50e40564c8845457 -github.com/opencontainers/image-spec 7c889fafd04a893f5c5f50b7ab9963d5d64e5242 -github.com/pkg/errors f15c970de5b76fac0b59abb32d62c17cc7bed265 -k8s.io/client-go v4.0.0 transitive=true -gopkg.in/check.v1 11d3bc7aa68e238947792f30573146a3231fc0f1 +github.com/Sirupsen/logrus v0.10.0 +github.com/urfave/cli v1.18.0 +golang.org/x/crypto 2509b142fb2b797aa7587dad548f113b2c0f20ce +gopkg.in/yaml.v2 eb3733d160e74a9c7e442f435eb3bea458e1d19f +github.com/docker/docker ecf4125b85e0faa57d2739348e0d453c1d24d10c +github.com/docker/distribution 3800056b8832cf6075e78b282ac010131d8687bc +github.com/docker/go-connections 3ede32e2033de7505e6500d6c868c2b9ed9f169d +github.com/docker/go-units 0dadbb0345b35ec7ef35e228dabb8de89a65bf52 +golang.org/x/net 186fd3fc8194a5e9980a82230d69c1ff7134229f +github.com/opencontainers/go-digest 279bed98673dd5bef374d3b6e4b09e2af76183bf +github.com/gogo/protobuf 117892bf1866fbaa2318c03e50e40564c8845457 +github.com/opencontainers/image-spec 7c889fafd04a893f5c5f50b7ab9963d5d64e5242 +github.com/pkg/errors f15c970de5b76fac0b59abb32d62c17cc7bed265 +k8s.io/client-go v4.0.0 transitive=true +gopkg.in/check.v1 11d3bc7aa68e238947792f30573146a3231fc0f1 +github.com/alena1108/cluster-controller 85168a7fe249bf97f703afe53f03d3654cc70350 diff --git a/vendor/github.com/alena1108/cluster-controller/.dockerignore b/vendor/github.com/alena1108/cluster-controller/.dockerignore new file mode 100644 index 00000000..6e43c2a9 --- /dev/null +++ b/vendor/github.com/alena1108/cluster-controller/.dockerignore @@ -0,0 +1,4 @@ +./bin +./.dapper +./dist +./.trash-cache diff --git a/vendor/github.com/alena1108/cluster-controller/.drone.yml b/vendor/github.com/alena1108/cluster-controller/.drone.yml new file mode 100644 index 00000000..8f5c37cd --- /dev/null +++ b/vendor/github.com/alena1108/cluster-controller/.drone.yml @@ -0,0 +1,9 @@ +--- +pipeline: + build: + privileged: true + image: rancher/dapper:1.11.2 + volumes: + - /var/run/docker.sock:/var/run/docker.sock + commands: + - dapper ci diff --git a/vendor/github.com/alena1108/cluster-controller/.gitignore b/vendor/github.com/alena1108/cluster-controller/.gitignore new file mode 100644 index 00000000..9e8d97d2 --- /dev/null +++ b/vendor/github.com/alena1108/cluster-controller/.gitignore @@ -0,0 +1,6 @@ +/.dapper +/bin +/dist +*.swp +/.trash-cache +cluster-controller diff --git a/vendor/github.com/alena1108/cluster-controller/Dockerfile.dapper b/vendor/github.com/alena1108/cluster-controller/Dockerfile.dapper new file mode 100644 index 00000000..b7ad8e2b --- /dev/null +++ b/vendor/github.com/alena1108/cluster-controller/Dockerfile.dapper @@ -0,0 +1,31 @@ +FROM ubuntu:16.04 +# FROM arm=armhf/ubuntu:16.04 + +ARG DAPPER_HOST_ARCH +ENV HOST_ARCH=${DAPPER_HOST_ARCH} ARCH=${DAPPER_HOST_ARCH} + +RUN apt-get update && \ + apt-get install -y gcc ca-certificates git wget curl vim less file && \ + rm -f /bin/sh && ln -s /bin/bash /bin/sh + +ENV GOLANG_ARCH_amd64=amd64 GOLANG_ARCH_arm=armv6l GOLANG_ARCH=GOLANG_ARCH_${ARCH} \ + GOPATH=/go PATH=/go/bin:/usr/local/go/bin:${PATH} SHELL=/bin/bash + +RUN wget -O - https://storage.googleapis.com/golang/go1.8.3.linux-${!GOLANG_ARCH}.tar.gz | tar -xzf - -C /usr/local && \ + go get github.com/rancher/trash && go get github.com/golang/lint/golint + +ENV DOCKER_URL_amd64=https://get.docker.com/builds/Linux/x86_64/docker-1.10.3 \ + DOCKER_URL_arm=https://github.com/rancher/docker/releases/download/v1.10.3-ros1/docker-1.10.3_arm \ + DOCKER_URL=DOCKER_URL_${ARCH} + +RUN wget -O - ${!DOCKER_URL} > /usr/bin/docker && chmod +x /usr/bin/docker + +ENV DAPPER_SOURCE /go/src/github.com/rancher/cluster-controller/ +ENV DAPPER_OUTPUT ./bin ./dist +ENV DAPPER_DOCKER_SOCKET true +ENV TRASH_CACHE ${DAPPER_SOURCE}/.trash-cache +ENV HOME ${DAPPER_SOURCE} +WORKDIR ${DAPPER_SOURCE} + +ENTRYPOINT ["./scripts/entry"] +CMD ["ci"] diff --git a/vendor/github.com/alena1108/cluster-controller/LICENSE b/vendor/github.com/alena1108/cluster-controller/LICENSE new file mode 100644 index 00000000..f433b1a5 --- /dev/null +++ b/vendor/github.com/alena1108/cluster-controller/LICENSE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/vendor/github.com/alena1108/cluster-controller/Makefile b/vendor/github.com/alena1108/cluster-controller/Makefile new file mode 100644 index 00000000..d7d72a16 --- /dev/null +++ b/vendor/github.com/alena1108/cluster-controller/Makefile @@ -0,0 +1,23 @@ +TARGETS := $(shell ls scripts) + +.dapper: + @echo Downloading dapper + @curl -sL https://releases.rancher.com/dapper/latest/dapper-`uname -s`-`uname -m` > .dapper.tmp + @@chmod +x .dapper.tmp + @./.dapper.tmp -v + @mv .dapper.tmp .dapper + +$(TARGETS): .dapper + ./.dapper $@ + +trash: .dapper + ./.dapper -m bind trash + +trash-keep: .dapper + ./.dapper -m bind trash -k + +deps: trash + +.DEFAULT_GOAL := ci + +.PHONY: $(TARGETS) diff --git a/vendor/github.com/alena1108/cluster-controller/README.md b/vendor/github.com/alena1108/cluster-controller/README.md new file mode 100644 index 00000000..acf4aa78 --- /dev/null +++ b/vendor/github.com/alena1108/cluster-controller/README.md @@ -0,0 +1,28 @@ +cluster-controller +======== + +A microservice that does micro things. + +## Building + +`make` + + +## Running + +`./bin/cluster-controller` + +## License +Copyright (c) 2014-2017 [Rancher Labs, Inc.](http://rancher.com) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +[http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/vendor/github.com/alena1108/cluster-controller/client/v1/client.go b/vendor/github.com/alena1108/cluster-controller/client/v1/client.go new file mode 100644 index 00000000..726f5bed --- /dev/null +++ b/vendor/github.com/alena1108/cluster-controller/client/v1/client.go @@ -0,0 +1,63 @@ +package v1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/runtime/serializer" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/pkg/api" + "k8s.io/client-go/rest" +) + +const ( + Group = "rancher.com" +) + +var Version = "v1" + +type ClustersManagerV1Interface interface { + RESTClient() rest.Interface + ClustersGetter +} + +type ClustersManagerV1Client struct { + restClient rest.Interface + dynamicClient *dynamic.Client +} + +func (c *ClustersManagerV1Client) Clusters() ClusterInterface { + return newClusters(c.restClient, c.dynamicClient) +} + +func (c *ClustersManagerV1Client) ClusterNodes() ClusterNodeInterface { + return newClusterNodes(c.restClient, c.dynamicClient) +} + +func (c *ClustersManagerV1Client) RESTClient() rest.Interface { + return c.restClient +} + +func NewForConfig(c *rest.Config) (*ClustersManagerV1Client, error) { + config := *c + SetConfigDefaults(&config) + client, err := rest.RESTClientFor(&config) + if err != nil { + return nil, err + } + + dynamicClient, err := dynamic.NewClient(&config) + if err != nil { + return nil, err + } + + return &ClustersManagerV1Client{client, dynamicClient}, nil +} + +func SetConfigDefaults(config *rest.Config) { + config.GroupVersion = &schema.GroupVersion{ + Group: Group, + Version: Version, + } + config.APIPath = "/apis" + config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: api.Codecs} + return +} diff --git a/vendor/github.com/alena1108/cluster-controller/client/v1/cluster.go b/vendor/github.com/alena1108/cluster-controller/client/v1/cluster.go new file mode 100644 index 00000000..61016611 --- /dev/null +++ b/vendor/github.com/alena1108/cluster-controller/client/v1/cluster.go @@ -0,0 +1,184 @@ +package v1 + +import ( + "encoding/json" + + "github.com/pkg/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/rest" +) + +const ( + ClustersKind = "Cluster" + ClustersName = "clusters" + ClustersSingularName = "cluster" +) + +type ClustersGetter interface { + Clusters(namespace string) ClusterInterface +} + +var _ ClusterInterface = &clusters{} + +type ClusterInterface interface { + Create(*Cluster) (*Cluster, error) + Get(name string, opts metav1.GetOptions) (*Cluster, error) + Update(*Cluster) (*Cluster, error) + Delete(name string, options *metav1.DeleteOptions) error + List(opts metav1.ListOptions) (runtime.Object, error) + Watch(opts metav1.ListOptions) (watch.Interface, error) + DeleteCollection(dopts *metav1.DeleteOptions, lopts metav1.ListOptions) error +} + +type clusters struct { + restClient rest.Interface + client *dynamic.ResourceClient + ns string +} + +func newClusters(r rest.Interface, c *dynamic.Client) *clusters { + return &clusters{ + r, + c.Resource( + &metav1.APIResource{ + Kind: ClustersKind, + Name: ClustersName, + Namespaced: false, + }, + "", + ), + "", + } +} + +func (p *clusters) Create(o *Cluster) (*Cluster, error) { + up, err := UnstructuredFromCluster(o) + if err != nil { + return nil, err + } + + up, err = p.client.Create(up) + if err != nil { + return nil, err + } + + return ClusterFromUnstructured(up) +} + +func (p *clusters) Get(name string, opts metav1.GetOptions) (*Cluster, error) { + obj, err := p.client.Get(name, opts) + if err != nil { + return nil, err + } + return ClusterFromUnstructured(obj) +} + +func (p *clusters) Update(o *Cluster) (*Cluster, error) { + up, err := UnstructuredFromCluster(o) + if err != nil { + return nil, err + } + + curp, err := p.Get(o.Name, metav1.GetOptions{}) + if err != nil { + return nil, errors.Wrap(err, "unable to get current version for update") + } + up.SetResourceVersion(curp.ObjectMeta.ResourceVersion) + + up, err = p.client.Update(up) + if err != nil { + return nil, err + } + + return ClusterFromUnstructured(up) +} + +func (p *clusters) Delete(name string, options *metav1.DeleteOptions) error { + return p.client.Delete(name, options) +} + +func (p *clusters) List(opts metav1.ListOptions) (runtime.Object, error) { + req := p.restClient.Get(). + Namespace(p.ns). + Resource(ClustersName). + FieldsSelectorParam(nil) + + b, err := req.DoRaw() + if err != nil { + return nil, err + } + var prom ClusterList + return &prom, json.Unmarshal(b, &prom) +} + +func (p *clusters) Watch(opts metav1.ListOptions) (watch.Interface, error) { + r, err := p.restClient.Get(). + Prefix("watch"). + Namespace(p.ns). + Resource(ClustersName). + FieldsSelectorParam(nil). + Stream() + if err != nil { + return nil, err + } + return watch.NewStreamWatcher(&clusterDecoder{ + dec: json.NewDecoder(r), + close: r.Close, + }), nil +} + +func (p *clusters) DeleteCollection(dopts *metav1.DeleteOptions, lopts metav1.ListOptions) error { + return p.client.DeleteCollection(dopts, lopts) +} + +func ClusterFromUnstructured(r *unstructured.Unstructured) (*Cluster, error) { + b, err := json.Marshal(r.Object) + if err != nil { + return nil, err + } + var p Cluster + if err := json.Unmarshal(b, &p); err != nil { + return nil, err + } + p.TypeMeta.Kind = ClustersKind + p.TypeMeta.APIVersion = Group + "/" + Version + return &p, nil +} + +func UnstructuredFromCluster(p *Cluster) (*unstructured.Unstructured, error) { + p.TypeMeta.Kind = ClustersKind + p.TypeMeta.APIVersion = Group + "/" + Version + b, err := json.Marshal(p) + if err != nil { + return nil, err + } + var r unstructured.Unstructured + if err := json.Unmarshal(b, &r.Object); err != nil { + return nil, err + } + return &r, nil +} + +type clusterDecoder struct { + dec *json.Decoder + close func() error +} + +func (d *clusterDecoder) Close() { + d.close() +} + +func (d *clusterDecoder) Decode() (action watch.EventType, object runtime.Object, err error) { + var e struct { + Type watch.EventType + Object Cluster + } + if err := d.dec.Decode(&e); err != nil { + return watch.Error, nil, err + } + return e.Type, &e.Object, nil +} diff --git a/vendor/github.com/alena1108/cluster-controller/client/v1/clusternode.go b/vendor/github.com/alena1108/cluster-controller/client/v1/clusternode.go new file mode 100644 index 00000000..5f33cd6e --- /dev/null +++ b/vendor/github.com/alena1108/cluster-controller/client/v1/clusternode.go @@ -0,0 +1,184 @@ +package v1 + +import ( + "encoding/json" + + "github.com/pkg/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/rest" +) + +const ( + ClusterNodesKind = "ClusterNode" + ClusterNodesName = "clusternodes" + ClusterNodesSingularName = "clusternode" +) + +type ClusterNodesGetter interface { + ClusterNodes(namespace string) ClusterNodeInterface +} + +var _ ClusterNodeInterface = &clusternodes{} + +type ClusterNodeInterface interface { + Create(*ClusterNode) (*ClusterNode, error) + Get(name string, opts metav1.GetOptions) (*ClusterNode, error) + Update(*ClusterNode) (*ClusterNode, error) + Delete(name string, options *metav1.DeleteOptions) error + List(opts metav1.ListOptions) (runtime.Object, error) + Watch(opts metav1.ListOptions) (watch.Interface, error) + DeleteCollection(dopts *metav1.DeleteOptions, lopts metav1.ListOptions) error +} + +type clusternodes struct { + restClient rest.Interface + client *dynamic.ResourceClient + ns string +} + +func newClusterNodes(r rest.Interface, c *dynamic.Client) *clusternodes { + return &clusternodes{ + r, + c.Resource( + &metav1.APIResource{ + Kind: ClusterNodesKind, + Name: ClusterNodesName, + Namespaced: false, + }, + "", + ), + "", + } +} + +func (p *clusternodes) Create(o *ClusterNode) (*ClusterNode, error) { + up, err := UnstructuredFromClusterNode(o) + if err != nil { + return nil, err + } + + up, err = p.client.Create(up) + if err != nil { + return nil, err + } + + return ClusterNodeFromUnstructured(up) +} + +func (p *clusternodes) Get(name string, opts metav1.GetOptions) (*ClusterNode, error) { + obj, err := p.client.Get(name, opts) + if err != nil { + return nil, err + } + return ClusterNodeFromUnstructured(obj) +} + +func (p *clusternodes) Update(o *ClusterNode) (*ClusterNode, error) { + up, err := UnstructuredFromClusterNode(o) + if err != nil { + return nil, err + } + + curp, err := p.Get(o.Name, metav1.GetOptions{}) + if err != nil { + return nil, errors.Wrap(err, "unable to get current version for update") + } + up.SetResourceVersion(curp.ObjectMeta.ResourceVersion) + + up, err = p.client.Update(up) + if err != nil { + return nil, err + } + + return ClusterNodeFromUnstructured(up) +} + +func (p *clusternodes) Delete(name string, options *metav1.DeleteOptions) error { + return p.client.Delete(name, options) +} + +func (p *clusternodes) List(opts metav1.ListOptions) (runtime.Object, error) { + req := p.restClient.Get(). + Namespace(p.ns). + Resource(ClusterNodesName). + FieldsSelectorParam(nil) + + b, err := req.DoRaw() + if err != nil { + return nil, err + } + var prom ClusterNodeList + return &prom, json.Unmarshal(b, &prom) +} + +func (p *clusternodes) Watch(opts metav1.ListOptions) (watch.Interface, error) { + r, err := p.restClient.Get(). + Prefix("watch"). + Namespace(p.ns). + Resource(ClusterNodesName). + FieldsSelectorParam(nil). + Stream() + if err != nil { + return nil, err + } + return watch.NewStreamWatcher(&clusterDecoder{ + dec: json.NewDecoder(r), + close: r.Close, + }), nil +} + +func (p *clusternodes) DeleteCollection(dopts *metav1.DeleteOptions, lopts metav1.ListOptions) error { + return p.client.DeleteCollection(dopts, lopts) +} + +func ClusterNodeFromUnstructured(r *unstructured.Unstructured) (*ClusterNode, error) { + b, err := json.Marshal(r.Object) + if err != nil { + return nil, err + } + var p ClusterNode + if err := json.Unmarshal(b, &p); err != nil { + return nil, err + } + p.TypeMeta.Kind = ClusterNodesKind + p.TypeMeta.APIVersion = Group + "/" + Version + return &p, nil +} + +func UnstructuredFromClusterNode(p *ClusterNode) (*unstructured.Unstructured, error) { + p.TypeMeta.Kind = ClusterNodesKind + p.TypeMeta.APIVersion = Group + "/" + Version + b, err := json.Marshal(p) + if err != nil { + return nil, err + } + var r unstructured.Unstructured + if err := json.Unmarshal(b, &r.Object); err != nil { + return nil, err + } + return &r, nil +} + +type clusterNodeDecoder struct { + dec *json.Decoder + close func() error +} + +func (d *clusterNodeDecoder) Close() { + d.close() +} + +func (d *clusterNodeDecoder) Decode() (action watch.EventType, object runtime.Object, err error) { + var e struct { + Type watch.EventType + Object ClusterNode + } + if err := d.dec.Decode(&e); err != nil { + return watch.Error, nil, err + } + return e.Type, &e.Object, nil +} diff --git a/vendor/github.com/alena1108/cluster-controller/client/v1/types.go b/vendor/github.com/alena1108/cluster-controller/client/v1/types.go new file mode 100644 index 00000000..df5e7dd4 --- /dev/null +++ b/vendor/github.com/alena1108/cluster-controller/client/v1/types.go @@ -0,0 +1,216 @@ +package v1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/pkg/api/v1" +) + +type ClusterConditionType string + +const ( + // ClusterConditionReady Cluster ready to serve API (healthy when true, unehalthy when false) + ClusterConditionReady = "Ready" + // ClusterConditionProvisioned Cluster is provisioned + ClusterConditionProvisioned = "Provisioned" + // ClusterConditionUpdating Cluster is being updating (upgrading, scaling up) + ClusterConditionUpdating = "Updating" + // More conditions can be added if unredlying controllers request it +) + +type Cluster struct { + metav1.TypeMeta `json:",inline"` + // Standard object’s metadata. More info: + // https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#metadata + metav1.ObjectMeta `json:"metadata,omitempty"` + // Specification of the desired behavior of the the cluster. More info: + // https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#spec-and-status + Spec ClusterSpec `json:"spec"` + // Most recent observed status of the cluster. More info: + // https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#spec-and-status + Status *ClusterStatus `json:"status"` +} + +type ClusterList struct { + metav1.TypeMeta `json:",inline"` + // Standard list metadata + // More info: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#metadata + metav1.ListMeta `json:"metadata,omitempty"` + // List of Clusters + Items []*Cluster `json:"items"` +} + +type ClusterSpec struct { + GKEConfig *GKEConfig + AKSConfig *AKSConfig + RKEConfig *RKEConfig +} + +type ClusterStatus struct { + //Conditions represent the latest available observations of an object's current state: + //More info: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#typical-status-properties + Conditions []ClusterCondition `json:"conditions,omitempty"` + //Component statuses will represent cluster's components (etcd/controller/scheduler) health + // https://kubernetes.io/docs/api-reference/v1.8/#componentstatus-v1-core + ComponentStatuses v1.ComponentStatusList + APIEndpoint string + ServiceAccountToken string + CACert string +} + +type ClusterCondition struct { + // Type of cluster condition. + Type ClusterConditionType `json:"type"` + // Status of the condition, one of True, False, Unknown. + Status v1.ConditionStatus `json:"status"` + // The last time this condition was updated. + LastUpdateTime string `json:"lastUpdateTime,omitempty"` + // Last time the condition transitioned from one status to another. + LastTransitionTime string `json:"lastTransitionTime,omitempty"` + // The reason for the condition's last transition. + Reason string `json:"reason,omitempty"` +} + +type GKEConfig struct { + // ProjectID is the ID of your project to use when creating a cluster + ProjectID string + // The zone to launch the cluster + Zone string + // The IP address range of the container pods + ClusterIpv4Cidr string + // An optional description of this cluster + Description string + // The number of nodes to create in this cluster + InitialNodeCount int64 + // Size of the disk attached to each node + DiskSizeGb int64 + // The name of a Google Compute Engine + MachineType string + // the initial kubernetes version + InitialClusterVersion string + // The map of Kubernetes labels (key/value pairs) to be applied + // to each node. + Labels map[string]string + // The path to the credential file(key.json) + CredentialPath string + // Enable alpha feature + EnableAlphaFeature bool + // NodePool id + NodePoolID string + + // Update Config + UpdateConfig gkeUpdateConfig +} + +type gkeUpdateConfig struct { + // the number of node + NodeCount int64 + // Master kubernetes version + MasterVersion string + // Node kubernetes version + NodeVersion string +} + +type AKSConfig struct { + //TBD +} + +type RKEConfig struct { + // Kubernetes nodes + Hosts []RKEConfigHost `yaml:"hosts"` + // Kubernetes components + Services RKEConfigServices `yaml:"services"` + // Network plugin used in the kubernetes cluster (flannel, calico) + NetworkPlugin string `yaml:"network_plugin"` + // Authentication type used in the cluster (default: x509) + AuthType string `yaml:"auth_type"` +} + +type RKEConfigHost struct { + // SSH IP address of the host + IP string `yaml:"ip"` + // Advertised address that will be used for components communication + AdvertiseAddress string `yaml:"advertise_address"` + // Host role in kubernetes cluster (controlplane, worker, or etcd) + Role []string `yaml:"role"` + // Hostname of the host + Hostname string `yaml:"hostname"` + // SSH usesr that will be used by RKE + User string `yaml:"user"` + // Docker socket on the host that will be used in tunneling + DockerSocket string `yaml:"docker_socket"` +} + +type RKEConfigServices struct { + // Etcd Service + Etcd ETCDService `yaml:"etcd"` + // KubeAPI Service + KubeAPI KubeAPIService `yaml:"kube-api"` + // KubeController Service + KubeController KubeControllerService `yaml:"kube-controller"` + // Scheduler Service + Scheduler SchedulerService `yaml:"scheduler"` + // Kubelet Service + Kubelet KubeletService `yaml:"kubelet"` + // KubeProxy Service + Kubeproxy KubeproxyService `yaml:"kubeproxy"` +} + +type ETCDService struct { + // Base service properties + baseService `yaml:",inline"` +} + +type KubeAPIService struct { + // Base service properties + baseService `yaml:",inline"` + // Virtual IP range that will be used by Kubernetes services + ServiceClusterIPRange string `yaml:"service_cluster_ip_range"` +} + +type KubeControllerService struct { + // Base service properties + baseService `yaml:",inline"` + // CIDR Range for Pods in cluster + ClusterCIDR string `yaml:"cluster_cidr"` + // Virtual IP range that will be used by Kubernetes services + ServiceClusterIPRange string `yaml:"service_cluster_ip_range"` +} + +type KubeletService struct { + // Base service properties + baseService `yaml:",inline"` + // Domain of the cluster (default: "cluster.local") + ClusterDomain string `yaml:"cluster_domain"` + // The image whose network/ipc namespaces containers in each pod will use + InfraContainerImage string `yaml:"infra_container_image"` + // Cluster DNS service ip + ClusterDNSServer string `yaml:"cluster_dns_server"` +} + +type KubeproxyService struct { + // Base service properties + baseService `yaml:",inline"` +} + +type SchedulerService struct { + // Base service properties + baseService `yaml:",inline"` +} + +type baseService struct { + // Docker image of the service + Image string `yaml:"image"` +} + +type ClusterNode struct { + v1.Node +} + +type ClusterNodeList struct { + metav1.TypeMeta `json:",inline"` + // Standard list metadata + // More info: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#metadata + metav1.ListMeta `json:"metadata,omitempty"` + // List of Clusters + Items []*Cluster `json:"items"` +} diff --git a/vendor/github.com/alena1108/cluster-controller/vendor.conf b/vendor/github.com/alena1108/cluster-controller/vendor.conf new file mode 100644 index 00000000..4ef86ebf --- /dev/null +++ b/vendor/github.com/alena1108/cluster-controller/vendor.conf @@ -0,0 +1,10 @@ +# package +github.com/rancher/cluster-controller + +github.com/Sirupsen/logrus v0.10.0 +github.com/urfave/cli v1.18.0 +k8s.io/client-go v4.0.0 transitive=true +github.com/pkg/errors v0.8.0 +github.com/urfave/cli v1.19.1 +golang.org/x/sync/errgroup 450f422ab23cf9881c94e2db30cac0eb1b7cf80c + diff --git a/vendor/k8s.io/client-go/dynamic/BUILD b/vendor/k8s.io/client-go/dynamic/BUILD new file mode 100644 index 00000000..e0320e26 --- /dev/null +++ b/vendor/k8s.io/client-go/dynamic/BUILD @@ -0,0 +1,55 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +load( + "@io_bazel_rules_go//go:def.bzl", + "go_library", + "go_test", +) + +go_test( + name = "go_default_test", + srcs = [ + "client_test.go", + "dynamic_util_test.go", + ], + library = ":go_default_library", + tags = ["automanaged"], + deps = [ + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime/serializer/streaming:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", + "//vendor/k8s.io/client-go/rest:go_default_library", + "//vendor/k8s.io/client-go/rest/watch:go_default_library", + ], +) + +go_library( + name = "go_default_library", + srcs = [ + "client.go", + "client_pool.go", + "dynamic_util.go", + ], + tags = ["automanaged"], + deps = [ + "//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/conversion/queryparams:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", + "//vendor/k8s.io/client-go/kubernetes/scheme:go_default_library", + "//vendor/k8s.io/client-go/pkg/api/v1:go_default_library", + "//vendor/k8s.io/client-go/rest:go_default_library", + "//vendor/k8s.io/client-go/util/flowcontrol:go_default_library", + ], +) diff --git a/vendor/k8s.io/client-go/dynamic/client.go b/vendor/k8s.io/client-go/dynamic/client.go new file mode 100644 index 00000000..a0c18209 --- /dev/null +++ b/vendor/k8s.io/client-go/dynamic/client.go @@ -0,0 +1,306 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package dynamic provides a client interface to arbitrary Kubernetes +// APIs that exposes common high level operations and exposes common +// metadata. +package dynamic + +import ( + "encoding/json" + "errors" + "io" + "net/url" + "strings" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/conversion/queryparams" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/runtime/serializer" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/pkg/api/v1" + restclient "k8s.io/client-go/rest" + "k8s.io/client-go/util/flowcontrol" +) + +// Client is a Kubernetes client that allows you to access metadata +// and manipulate metadata of a Kubernetes API group. +type Client struct { + cl *restclient.RESTClient + parameterCodec runtime.ParameterCodec +} + +// NewClient returns a new client based on the passed in config. The +// codec is ignored, as the dynamic client uses it's own codec. +func NewClient(conf *restclient.Config) (*Client, error) { + // avoid changing the original config + confCopy := *conf + conf = &confCopy + + contentConfig := ContentConfig() + contentConfig.GroupVersion = conf.GroupVersion + if conf.NegotiatedSerializer != nil { + contentConfig.NegotiatedSerializer = conf.NegotiatedSerializer + } + conf.ContentConfig = contentConfig + + if conf.APIPath == "" { + conf.APIPath = "/api" + } + + if len(conf.UserAgent) == 0 { + conf.UserAgent = restclient.DefaultKubernetesUserAgent() + } + + cl, err := restclient.RESTClientFor(conf) + if err != nil { + return nil, err + } + + return &Client{cl: cl}, nil +} + +// GetRateLimiter returns rate limier. +func (c *Client) GetRateLimiter() flowcontrol.RateLimiter { + return c.cl.GetRateLimiter() +} + +// Resource returns an API interface to the specified resource for this client's +// group and version. If resource is not a namespaced resource, then namespace +// is ignored. The ResourceClient inherits the parameter codec of c. +func (c *Client) Resource(resource *metav1.APIResource, namespace string) *ResourceClient { + return &ResourceClient{ + cl: c.cl, + resource: resource, + ns: namespace, + parameterCodec: c.parameterCodec, + } +} + +// ParameterCodec returns a client with the provided parameter codec. +func (c *Client) ParameterCodec(parameterCodec runtime.ParameterCodec) *Client { + return &Client{ + cl: c.cl, + parameterCodec: parameterCodec, + } +} + +// ResourceClient is an API interface to a specific resource under a +// dynamic client. +type ResourceClient struct { + cl *restclient.RESTClient + resource *metav1.APIResource + ns string + parameterCodec runtime.ParameterCodec +} + +// List returns a list of objects for this resource. +func (rc *ResourceClient) List(opts metav1.ListOptions) (runtime.Object, error) { + parameterEncoder := rc.parameterCodec + if parameterEncoder == nil { + parameterEncoder = defaultParameterEncoder + } + return rc.cl.Get(). + NamespaceIfScoped(rc.ns, rc.resource.Namespaced). + Resource(rc.resource.Name). + VersionedParams(&opts, parameterEncoder). + Do(). + Get() +} + +// Get gets the resource with the specified name. +func (rc *ResourceClient) Get(name string, opts metav1.GetOptions) (*unstructured.Unstructured, error) { + parameterEncoder := rc.parameterCodec + if parameterEncoder == nil { + parameterEncoder = defaultParameterEncoder + } + result := new(unstructured.Unstructured) + err := rc.cl.Get(). + NamespaceIfScoped(rc.ns, rc.resource.Namespaced). + Resource(rc.resource.Name). + VersionedParams(&opts, parameterEncoder). + Name(name). + Do(). + Into(result) + return result, err +} + +// Delete deletes the resource with the specified name. +func (rc *ResourceClient) Delete(name string, opts *metav1.DeleteOptions) error { + return rc.cl.Delete(). + NamespaceIfScoped(rc.ns, rc.resource.Namespaced). + Resource(rc.resource.Name). + Name(name). + Body(opts). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (rc *ResourceClient) DeleteCollection(deleteOptions *metav1.DeleteOptions, listOptions metav1.ListOptions) error { + parameterEncoder := rc.parameterCodec + if parameterEncoder == nil { + parameterEncoder = defaultParameterEncoder + } + return rc.cl.Delete(). + NamespaceIfScoped(rc.ns, rc.resource.Namespaced). + Resource(rc.resource.Name). + VersionedParams(&listOptions, parameterEncoder). + Body(deleteOptions). + Do(). + Error() +} + +// Create creates the provided resource. +func (rc *ResourceClient) Create(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) { + result := new(unstructured.Unstructured) + err := rc.cl.Post(). + NamespaceIfScoped(rc.ns, rc.resource.Namespaced). + Resource(rc.resource.Name). + Body(obj). + Do(). + Into(result) + return result, err +} + +// Update updates the provided resource. +func (rc *ResourceClient) Update(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) { + result := new(unstructured.Unstructured) + if len(obj.GetName()) == 0 { + return result, errors.New("object missing name") + } + err := rc.cl.Put(). + NamespaceIfScoped(rc.ns, rc.resource.Namespaced). + Resource(rc.resource.Name). + Name(obj.GetName()). + Body(obj). + Do(). + Into(result) + return result, err +} + +// Watch returns a watch.Interface that watches the resource. +func (rc *ResourceClient) Watch(opts metav1.ListOptions) (watch.Interface, error) { + parameterEncoder := rc.parameterCodec + if parameterEncoder == nil { + parameterEncoder = defaultParameterEncoder + } + opts.Watch = true + return rc.cl.Get(). + NamespaceIfScoped(rc.ns, rc.resource.Namespaced). + Resource(rc.resource.Name). + VersionedParams(&opts, parameterEncoder). + Watch() +} + +func (rc *ResourceClient) Patch(name string, pt types.PatchType, data []byte) (*unstructured.Unstructured, error) { + result := new(unstructured.Unstructured) + err := rc.cl.Patch(pt). + NamespaceIfScoped(rc.ns, rc.resource.Namespaced). + Resource(rc.resource.Name). + Name(name). + Body(data). + Do(). + Into(result) + return result, err +} + +// dynamicCodec is a codec that wraps the standard unstructured codec +// with special handling for Status objects. +type dynamicCodec struct{} + +func (dynamicCodec) Decode(data []byte, gvk *schema.GroupVersionKind, obj runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) { + obj, gvk, err := unstructured.UnstructuredJSONScheme.Decode(data, gvk, obj) + if err != nil { + return nil, nil, err + } + + if _, ok := obj.(*metav1.Status); !ok && strings.ToLower(gvk.Kind) == "status" { + obj = &metav1.Status{} + err := json.Unmarshal(data, obj) + if err != nil { + return nil, nil, err + } + } + + return obj, gvk, nil +} + +func (dynamicCodec) Encode(obj runtime.Object, w io.Writer) error { + return unstructured.UnstructuredJSONScheme.Encode(obj, w) +} + +// ContentConfig returns a restclient.ContentConfig for dynamic types. +func ContentConfig() restclient.ContentConfig { + var jsonInfo runtime.SerializerInfo + // TODO: scheme.Codecs here should become "pkg/apis/server/scheme" which is the minimal core you need + // to talk to a kubernetes server + for _, info := range scheme.Codecs.SupportedMediaTypes() { + if info.MediaType == runtime.ContentTypeJSON { + jsonInfo = info + break + } + } + + jsonInfo.Serializer = dynamicCodec{} + jsonInfo.PrettySerializer = nil + return restclient.ContentConfig{ + AcceptContentTypes: runtime.ContentTypeJSON, + ContentType: runtime.ContentTypeJSON, + NegotiatedSerializer: serializer.NegotiatedSerializerWrapper(jsonInfo), + } +} + +// paramaterCodec is a codec converts an API object to query +// parameters without trying to convert to the target version. +type parameterCodec struct{} + +func (parameterCodec) EncodeParameters(obj runtime.Object, to schema.GroupVersion) (url.Values, error) { + return queryparams.Convert(obj) +} + +func (parameterCodec) DecodeParameters(parameters url.Values, from schema.GroupVersion, into runtime.Object) error { + return errors.New("DecodeParameters not implemented on dynamic parameterCodec") +} + +var defaultParameterEncoder runtime.ParameterCodec = parameterCodec{} + +type versionedParameterEncoderWithV1Fallback struct{} + +func (versionedParameterEncoderWithV1Fallback) EncodeParameters(obj runtime.Object, to schema.GroupVersion) (url.Values, error) { + ret, err := scheme.ParameterCodec.EncodeParameters(obj, to) + if err != nil && runtime.IsNotRegisteredError(err) { + // fallback to v1 + return scheme.ParameterCodec.EncodeParameters(obj, v1.SchemeGroupVersion) + } + return ret, err +} + +func (versionedParameterEncoderWithV1Fallback) DecodeParameters(parameters url.Values, from schema.GroupVersion, into runtime.Object) error { + return errors.New("DecodeParameters not implemented on versionedParameterEncoderWithV1Fallback") +} + +// VersionedParameterEncoderWithV1Fallback is useful for encoding query +// parameters for thirdparty resources. It tries to convert object to the +// specified version before converting it to query parameters, and falls back to +// converting to v1 if the object is not registered in the specified version. +// For the record, currently API server always treats query parameters sent to a +// thirdparty resource endpoint as v1. +var VersionedParameterEncoderWithV1Fallback runtime.ParameterCodec = versionedParameterEncoderWithV1Fallback{} diff --git a/vendor/k8s.io/client-go/dynamic/client_pool.go b/vendor/k8s.io/client-go/dynamic/client_pool.go new file mode 100644 index 00000000..f4f2596a --- /dev/null +++ b/vendor/k8s.io/client-go/dynamic/client_pool.go @@ -0,0 +1,122 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package dynamic + +import ( + "sync" + + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/runtime/schema" + restclient "k8s.io/client-go/rest" +) + +// ClientPool manages a pool of dynamic clients. +type ClientPool interface { + // ClientForGroupVersionKind returns a client configured for the specified groupVersionResource. + // Resource may be empty. + ClientForGroupVersionResource(resource schema.GroupVersionResource) (*Client, error) + // ClientForGroupVersionKind returns a client configured for the specified groupVersionKind. + // Kind may be empty. + ClientForGroupVersionKind(kind schema.GroupVersionKind) (*Client, error) +} + +// APIPathResolverFunc knows how to convert a groupVersion to its API path. The Kind field is +// optional. +type APIPathResolverFunc func(kind schema.GroupVersionKind) string + +// LegacyAPIPathResolverFunc can resolve paths properly with the legacy API. +func LegacyAPIPathResolverFunc(kind schema.GroupVersionKind) string { + if len(kind.Group) == 0 { + return "/api" + } + return "/apis" +} + +// clientPoolImpl implements ClientPool and caches clients for the resource group versions +// is asked to retrieve. This type is thread safe. +type clientPoolImpl struct { + lock sync.RWMutex + config *restclient.Config + clients map[schema.GroupVersion]*Client + apiPathResolverFunc APIPathResolverFunc + mapper meta.RESTMapper +} + +// NewClientPool returns a ClientPool from the specified config. It reuses clients for the the same +// group version. It is expected this type may be wrapped by specific logic that special cases certain +// resources or groups. +func NewClientPool(config *restclient.Config, mapper meta.RESTMapper, apiPathResolverFunc APIPathResolverFunc) ClientPool { + confCopy := *config + + return &clientPoolImpl{ + config: &confCopy, + clients: map[schema.GroupVersion]*Client{}, + apiPathResolverFunc: apiPathResolverFunc, + mapper: mapper, + } +} + +// Instantiates a new dynamic client pool with the given config. +func NewDynamicClientPool(cfg *restclient.Config) ClientPool { + // restMapper is not needed when using LegacyAPIPathResolverFunc + emptyMapper := meta.MultiRESTMapper{} + return NewClientPool(cfg, emptyMapper, LegacyAPIPathResolverFunc) +} + +// ClientForGroupVersionResource uses the provided RESTMapper to identify the appropriate resource. Resource may +// be empty. If no matching kind is found the underlying client for that group is still returned. +func (c *clientPoolImpl) ClientForGroupVersionResource(resource schema.GroupVersionResource) (*Client, error) { + kinds, err := c.mapper.KindsFor(resource) + if err != nil { + if meta.IsNoMatchError(err) { + return c.ClientForGroupVersionKind(schema.GroupVersionKind{Group: resource.Group, Version: resource.Version}) + } + return nil, err + } + return c.ClientForGroupVersionKind(kinds[0]) +} + +// ClientForGroupVersion returns a client for the specified groupVersion, creates one if none exists. Kind +// in the GroupVersionKind may be empty. +func (c *clientPoolImpl) ClientForGroupVersionKind(kind schema.GroupVersionKind) (*Client, error) { + c.lock.Lock() + defer c.lock.Unlock() + + gv := kind.GroupVersion() + + // do we have a client already configured? + if existingClient, found := c.clients[gv]; found { + return existingClient, nil + } + + // avoid changing the original config + confCopy := *c.config + conf := &confCopy + + // we need to set the api path based on group version, if no group, default to legacy path + conf.APIPath = c.apiPathResolverFunc(kind) + + // we need to make a client + conf.GroupVersion = &gv + + dynamicClient, err := NewClient(conf) + if err != nil { + return nil, err + } + c.clients[gv] = dynamicClient + return dynamicClient, nil +} diff --git a/vendor/k8s.io/client-go/dynamic/dynamic_util.go b/vendor/k8s.io/client-go/dynamic/dynamic_util.go new file mode 100644 index 00000000..c834dbb0 --- /dev/null +++ b/vendor/k8s.io/client-go/dynamic/dynamic_util.go @@ -0,0 +1,96 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package dynamic + +import ( + "fmt" + + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// VersionInterfaces provides an object converter and metadata +// accessor appropriate for use with unstructured objects. +func VersionInterfaces(schema.GroupVersion) (*meta.VersionInterfaces, error) { + return &meta.VersionInterfaces{ + ObjectConvertor: &unstructured.UnstructuredObjectConverter{}, + MetadataAccessor: meta.NewAccessor(), + }, nil +} + +// NewDiscoveryRESTMapper returns a RESTMapper based on discovery information. +func NewDiscoveryRESTMapper(resources []*metav1.APIResourceList, versionFunc meta.VersionInterfacesFunc) (*meta.DefaultRESTMapper, error) { + rm := meta.NewDefaultRESTMapper(nil, versionFunc) + for _, resourceList := range resources { + gv, err := schema.ParseGroupVersion(resourceList.GroupVersion) + if err != nil { + return nil, err + } + + for _, resource := range resourceList.APIResources { + gvk := gv.WithKind(resource.Kind) + scope := meta.RESTScopeRoot + if resource.Namespaced { + scope = meta.RESTScopeNamespace + } + rm.Add(gvk, scope) + } + } + return rm, nil +} + +// ObjectTyper provides an ObjectTyper implementation for +// unstructured.Unstructured object based on discovery information. +type ObjectTyper struct { + registered map[schema.GroupVersionKind]bool +} + +// NewObjectTyper constructs an ObjectTyper from discovery information. +func NewObjectTyper(resources []*metav1.APIResourceList) (runtime.ObjectTyper, error) { + ot := &ObjectTyper{registered: make(map[schema.GroupVersionKind]bool)} + for _, resourceList := range resources { + gv, err := schema.ParseGroupVersion(resourceList.GroupVersion) + if err != nil { + return nil, err + } + + for _, resource := range resourceList.APIResources { + ot.registered[gv.WithKind(resource.Kind)] = true + } + } + return ot, nil +} + +// ObjectKinds returns a slice of one element with the +// group,version,kind of the provided object, or an error if the +// object is not *unstructured.Unstructured or has no group,version,kind +// information. +func (ot *ObjectTyper) ObjectKinds(obj runtime.Object) ([]schema.GroupVersionKind, bool, error) { + if _, ok := obj.(*unstructured.Unstructured); !ok { + return nil, false, fmt.Errorf("type %T is invalid for dynamic object typer", obj) + } + return []schema.GroupVersionKind{obj.GetObjectKind().GroupVersionKind()}, false, nil +} + +// Recognizes returns true if the provided group,version,kind was in +// the discovery information. +func (ot *ObjectTyper) Recognizes(gvk schema.GroupVersionKind) bool { + return ot.registered[gvk] +}