diff --git a/cmd/kdk/kubesync.go b/cmd/kdk/kubesync.go new file mode 100644 index 0000000..9ace6b6 --- /dev/null +++ b/cmd/kdk/kubesync.go @@ -0,0 +1,33 @@ +// Copyright © 2018 Cisco Systems, Inc. +// +// 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 cmd + +import ( + "github.com/cisco-sso/kdk/pkg/kdk" + "github.com/spf13/cobra" +) + +var kubesyncCmd = &cobra.Command{ + Use: `kubesync`, + Short: "Sync default KUBECONFIG to KDK", + Long: "Sync default KUBECONFIG to KDK and tune Docker Kubernetes API hostname", + Run: func(cmd *cobra.Command, args []string) { + kdk.Kubesync(CurrentKdkEnvConfig) + }, +} + +func init() { + rootCmd.AddCommand(kubesyncCmd) +} diff --git a/pkg/kdk/config.go b/pkg/kdk/config.go index e681126..81dadd0 100644 --- a/pkg/kdk/config.go +++ b/pkg/kdk/config.go @@ -16,6 +16,7 @@ package kdk import ( "context" + "fmt" "io/ioutil" "os" "os/user" @@ -23,17 +24,18 @@ import ( "strconv" "strings" - log "github.com/sirupsen/logrus" "github.com/cisco-sso/kdk/pkg/keybase" "github.com/cisco-sso/kdk/pkg/prompt" "github.com/cisco-sso/kdk/pkg/ssh" "github.com/cisco-sso/kdk/pkg/utils" + "github.com/codeskyblue/go-sh" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/mount" "github.com/docker/docker/client" "github.com/docker/go-connections/nat" "github.com/ghodss/yaml" "github.com/mitchellh/go-homedir" + log "github.com/sirupsen/logrus" ) var ( @@ -335,3 +337,42 @@ func (c *KdkEnvConfig) CreateKdkSshKeyPair() (err error) { } return nil } + +// Returns SSH connection string +func (c *KdkEnvConfig) SSHConnectionString() string { + return c.User() + "@localhost" +} + +// Returns SSH command string +func (c *KdkEnvConfig) SSHCommandString() string { + return fmt.Sprintf("ssh %s -A -p %s -i %s -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null", + c.SSHConnectionString(), c.ConfigFile.AppConfig.Port, c.PrivateKeyPath()) +} + +// Returns SCP command string +func (c *KdkEnvConfig) SCPCommandString() string { + return fmt.Sprintf("scp -P %s -i %s -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null", + c.ConfigFile.AppConfig.Port, c.PrivateKeyPath()) +} + +// SCP's a file into the KDK container +func (c *KdkEnvConfig) SCPTo(hostPath, kdkPath string) error { + commandString := fmt.Sprintf("%s %s %s:%s", c.SCPCommandString(), hostPath, c.SSHConnectionString(), kdkPath) + log.Infof("executing scp command: %s", commandString) + commandMap := strings.Split(commandString, " ") + if err := sh.Command(commandMap[0], commandMap[1:]).SetStdin(os.Stdin).Run(); err != nil { + return err + } + return nil +} + +// Executes a command on the KDK container +func (c *KdkEnvConfig) Exec(command string) error { + commandString := fmt.Sprintf("%s %s", c.SSHCommandString(), command) + log.Infof("executing ssh command: %s", commandString) + commandMap := strings.Split(commandString, " ") + if err := sh.Command(commandMap[0], commandMap[1:]).SetStdin(os.Stdin).Run(); err != nil { + return err + } + return nil +} diff --git a/pkg/kdk/kubesync.go b/pkg/kdk/kubesync.go new file mode 100644 index 0000000..f4e7576 --- /dev/null +++ b/pkg/kdk/kubesync.go @@ -0,0 +1,69 @@ +// Copyright © 2018 Cisco Systems, Inc. +// +// 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 kdk + +import ( + "github.com/docker/docker/api/types" + log "github.com/sirupsen/logrus" +) + +func Kubesync(cfg KdkEnvConfig) { + + // Check if KDK container is running + kdkRunning := false + + containers, err := cfg.DockerClient.ContainerList(cfg.Ctx, types.ContainerListOptions{All: true}) + + if err != nil { + log.WithField("error", err).Fatal("Failed to list docker containers") + } + + for _, container := range containers { + for _, name := range container.Names { + if name == "/"+cfg.ConfigFile.AppConfig.Name { + if container.State == "running" { + kdkRunning = true + break + } + } + } + } + + // if KDK container is not running, start it and provision KDK user + if !kdkRunning { + log.Error("KDK is not currently running.") + } + + kubeconfigHostPath := cfg.Home() + "/.kube/config" + kubeconfigKDKPath := ".kube/docker-for-desktop.example.org" + + // Create ~/.kube directory inside KDK if it doesn't already exist. + remoteCommand := "mkdir -p ~/.kubetest" + if err = cfg.Exec(remoteCommand); err != nil { + log.WithField("error", err).Fatal("Failed to mkdir in KDK container.") + } + + // Sync default KUBECONFIG to KDK + if err = cfg.SCPTo(kubeconfigHostPath, kubeconfigKDKPath); err != nil { + log.WithField("error", err).Fatal("Failed to scp to KDK container.") + } + + // Tune Docker for Desktop's Kubernetes API hostname in KUBECONFIG + remoteCommand = "sed -i -e 's@localhost@host.docker.internal@g' -e 's@docker-for-desktop.*@docker-for-desktop.example.org@g' " + kubeconfigKDKPath + if err = cfg.Exec(remoteCommand); err != nil { + log.WithField("error", err).Fatal("Failed to transform KUBECONFIG in KDK container.") + } + log.Info("Docker for Desktop KUBECONFIG synchronized to KDK.") +}