Skip to content

Commit

Permalink
More refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
marcwickenden committed Dec 12, 2018
1 parent 80b949b commit 06d077d
Show file tree
Hide file tree
Showing 5 changed files with 404 additions and 0 deletions.
45 changes: 45 additions & 0 deletions pkg/bootstrap/bootstrap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright © 2018 Marc Wickenden <marc@4armed.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

package bootstrap

import (
"github.com/4armed/kubeletmein/pkg/config"
"github.com/spf13/cobra"
)

// Command represents the bootstrap command
func Command() *cobra.Command {

c := &config.Config{}

cmd := &cobra.Command{
Use: "bootstrap",
Short: "Retrieve kubelet creds",
Run: func(cmd *cobra.Command, args []string) {
cmd.Help()
},
}

cmd.AddCommand(bootstrapDoCmd(c))
cmd.AddCommand(bootstrapGkeCmd(c))

cmd.PersistentFlags().StringVarP(&c.BootstrapConfig, "bootstrap-kubeconfig", "b", "bootstrap-kubeconfig", "The filename to write the bootstrap kubeconfig to")
cmd.PersistentFlags().StringVarP(&c.CaCertPath, "ca-cert", "a", "ca-certificates.crt", "The filename to write the apiserver CA cert to")
cmd.PersistentFlags().StringVarP(&c.MetadataFile, "metadata-file", "f", "", "Don't try to parse metadata, load from the specified filename instead.")

return cmd

}
126 changes: 126 additions & 0 deletions pkg/bootstrap/do.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// Copyright © 2018 Marc Wickenden <marc@4armed.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

package bootstrap

import (
"fmt"
"io/ioutil"
"net/http"
"os"

"github.com/4armed/kubeletmein/pkg/config"
"github.com/kubicorn/kubicorn/pkg/logger"
"github.com/spf13/cobra"
yaml "gopkg.in/yaml.v2"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
)

const (
metadataIP = "169.254.169.254"
)

// Metadata stores the Kubernetes-related YAML
type Metadata struct {
CaCert string `yaml:"k8saas_ca_cert"`
KubeletToken string `yaml:"k8saas_bootstrap_token"`
KubeMaster string `yaml:"k8saas_master_domain_name"`
}

// bootstrapCmd represents the bootstrap command
func bootstrapDoCmd(c *config.Config) *cobra.Command {
m := Metadata{}
userData := []byte{}
var kubeMaster string
var err error

cmd := &cobra.Command{
Use: "do",
TraverseChildren: true,
Short: "Write out a bootstrap kubeconfig for the kubelet LoadClientCert function on Digital Ocean",
RunE: func(cmd *cobra.Command, args []string) error {

if c.MetadataFile == "" {
logger.Info("fetching kubelet creds from metadata service")
resp, err := http.Get("http://" + metadataIP + "/metadata/v1/user-data")
if err != nil {
panic(err)
}
defer resp.Body.Close()
userData, err = ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
} else {
logger.Info("fetching kubelet creds from file: %v", c.MetadataFile)
userData, err = ioutil.ReadFile(c.MetadataFile)
if err != nil {
return err
}
}

err = yaml.Unmarshal([]byte(userData), &m)
if err != nil {
return fmt.Errorf("unable to parse YAML from user-data: %v", err)
}

logger.Info("writing ca cert to: %v", c.CaCertPath)
err = ioutil.WriteFile(c.CaCertPath, []byte(m.CaCert), 0644)
if err != nil {
return fmt.Errorf("unable to write ca cert to file: %v", err)
}

if os.Getenv("KUBERNETES_SERVICE_HOST") != "" && os.Getenv("KUBERNETES_SERVICE_PORT_HTTPS") != "" {
kubeMaster = os.Getenv("KUBERNETES_SERVICE_HOST") + ":" + os.Getenv("KUBERNETES_SERVICE_PORT_HTTPS")
} else {
kubeMaster = m.KubeMaster
}

logger.Info("generating bootstrap-kubeconfig file at: %v", c.BootstrapConfig)
kubeconfigData := clientcmdapi.Config{
// Define a cluster stanza
Clusters: map[string]*clientcmdapi.Cluster{"local": {
Server: "https://" + kubeMaster,
InsecureSkipTLSVerify: false,
CertificateAuthority: c.CaCertPath,
}},
// Define auth based on the kubelet client cert retrieved
AuthInfos: map[string]*clientcmdapi.AuthInfo{"kubelet": {
Token: m.KubeletToken,
}},
// Define a context and set as current
Contexts: map[string]*clientcmdapi.Context{"service-account-context": {
Cluster: "local",
AuthInfo: "kubelet",
}},
CurrentContext: "service-account-context",
}

// Marshal to disk
err = clientcmd.WriteToFile(kubeconfigData, c.BootstrapConfig)
if err != nil {
return fmt.Errorf("unable to write bootstrap-kubeconfig file: %v", err)
}

logger.Info("wrote bootstrap-kubeconfig")
logger.Info("now generate a new node certificate with: kubeletmein do generate")

return err
},
}

return cmd
}
147 changes: 147 additions & 0 deletions pkg/bootstrap/gke.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// Copyright © 2018 Marc Wickenden <marc@4armed.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

package bootstrap

import (
"encoding/base64"
"fmt"
"io/ioutil"
"net/http"

"cloud.google.com/go/compute/metadata"
"github.com/4armed/kubeletmein/pkg/config"
"github.com/kubicorn/kubicorn/pkg/logger"
"github.com/spf13/cobra"
yaml "gopkg.in/yaml.v2"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
)

// Kubeenv stores the kube-env YAML
type Kubeenv struct {
CaCert string `yaml:"CA_CERT"`
KubeletCert string `yaml:"KUBELET_CERT"`
KubeletKey string `yaml:"KUBELET_KEY"`
KubeMasterName string `yaml:"KUBERNETES_MASTER_NAME"`
}

// bootstrapCmd represents the bootstrap command
func bootstrapGkeCmd(c *config.Config) *cobra.Command {
m := metadata.NewClient(&http.Client{})
k := Kubeenv{}
var kubeenv []byte
var err error

cmd := &cobra.Command{
Use: "gke",
TraverseChildren: true,
Short: "Write out a bootstrap kubeconfig for the kubelet LoadClientCert function on GKE",
RunE: func(cmd *cobra.Command, args []string) error {

if c.MetadataFile == "" {
logger.Info("fetching kubelet creds from metadata service")
ke, err := m.InstanceAttributeValue("kube-env")
if err != nil {
return err
}
kubeenv = []byte(ke)
} else {
logger.Info("fetching kubelet creds from file: %v", c.MetadataFile)
kubeenv, err = ioutil.ReadFile(c.MetadataFile)
if err != nil {
return err
}
}

err = yaml.Unmarshal(kubeenv, &k)
if err != nil {
return fmt.Errorf("unable to parse YAML from kube-env: %v", err)
}

logger.Debug("decoding ca cert")
caCert, err := base64.StdEncoding.DecodeString(k.CaCert)
if err != nil {
return fmt.Errorf("unable to decode ca cert: %v", err)
}
logger.Info("writing ca cert to: %v", c.CaCertPath)
err = ioutil.WriteFile(c.CaCertPath, caCert, 0644)
if err != nil {
return fmt.Errorf("unable to write ca cert to file: %v", err)
}

logger.Debug("decoding kubelet cert")
kubeletCert, err := base64.StdEncoding.DecodeString(k.KubeletCert)
if err != nil {
return fmt.Errorf("unable to decode kubelet cert: %v", err)
}

logger.Info("writing kubelet cert to: %v", c.KubeletCertPath)
err = ioutil.WriteFile(c.KubeletCertPath, kubeletCert, 0644)
if err != nil {
return fmt.Errorf("unable to write kubelet cert to file: %v", err)
}

logger.Debug("decoding kubelet key")
kubeletKey, err := base64.StdEncoding.DecodeString(k.KubeletKey)
if err != nil {
return fmt.Errorf("unable to decode kubelet key: %v", err)
}

logger.Info("writing kubelet key to: %v", c.KubeletKeyPath)
err = ioutil.WriteFile(c.KubeletKeyPath, kubeletKey, 0644)
if err != nil {
return fmt.Errorf("unable to write kubelet key to file: %v", err)
}

logger.Info("generating bootstrap-kubeconfig file at: %v", c.BootstrapConfig)
kubeconfigData := clientcmdapi.Config{
// Define a cluster stanza
Clusters: map[string]*clientcmdapi.Cluster{"local": {
Server: "https://" + k.KubeMasterName,
InsecureSkipTLSVerify: false,
CertificateAuthority: c.CaCertPath,
}},
// Define auth based on the kubelet client cert retrieved
AuthInfos: map[string]*clientcmdapi.AuthInfo{"kubelet": {
ClientCertificate: c.KubeletCertPath,
ClientKey: c.KubeletKeyPath,
}},
// Define a context and set as current
Contexts: map[string]*clientcmdapi.Context{"service-account-context": {
Cluster: "local",
AuthInfo: "kubelet",
}},
CurrentContext: "service-account-context",
}

// Marshal to disk
err = clientcmd.WriteToFile(kubeconfigData, c.BootstrapConfig)
if err != nil {
return fmt.Errorf("unable to write bootstrap-kubeconfig file: %v", err)
}

logger.Info("wrote bootstrap-kubeconfig")
logger.Info("now generate a new node certificate with: kubeletmein gke generate")

return err
},
}

cmd.Flags().StringVarP(&c.KubeletCertPath, "kubelet-cert", "c", "kubelet.crt", "The filename to write the kubelet cert to")
cmd.Flags().StringVarP(&c.KubeletKeyPath, "kubelet-key", "k", "kubelet.key", "The filename to write the kubelet key to")

return cmd
}
30 changes: 30 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright © 2018 Marc Wickenden <marc@4armed.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

package config

// Config holds configuration values for GKE operations
type Config struct {
BootstrapConfig string
CaCertPath string
KubeletKeyPath string
KubeletCertPath string
KubeletToken string
KubeConfig string
CertDir string
NodeName string
KubeAPIServer string
MetadataFile string
}
Loading

0 comments on commit 06d077d

Please sign in to comment.