cmd/hub/kube/kubeconfig.go (168 lines of code) (raw):
// Copyright (c) 2022 EPAM Systems, Inc.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package kube
import (
"errors"
"fmt"
"io"
"log"
"os"
"path/filepath"
"gopkg.in/yaml.v2"
"github.com/epam/hubctl/cmd/hub/config"
"github.com/epam/hubctl/cmd/hub/parameters"
"github.com/epam/hubctl/cmd/hub/state"
"github.com/epam/hubctl/cmd/hub/util"
)
func Kubeconfig(filenames []string, providers []string, context string, keepPems bool) {
state := state.MustParseStateFiles(filenames)
providerState, provider := findState(state, providers)
if providerState == nil && config.Verbose {
// TODO (1) find a provider with kubernetes.api.* outputs
// (2) write `provides` into state and search by that
log.Printf("There is no state for %v found in state file(s) %v; trying stack parameters", providers, filenames)
}
outputs := make(parameters.CapturedOutputs)
if providerState != nil {
for _, o := range providerState.CapturedOutputs {
outputs[o.QName()] = o
}
}
params := make(parameters.LockedParameters)
for _, p := range state.StackParameters {
params[p.QName()] = p
}
SetupKubernetes(params, provider, outputs, context, config.Force, keepPems)
}
func findState(state *state.StateManifest, providers []string) (*state.StateStep, string) {
for _, provider := range providers {
providerState, exist := state.Components[provider]
if exist {
return providerState, provider
}
}
return nil, ""
}
type KubeAuthPlugin struct {
ApiVersion string `yaml:"apiVersion,omitempty"`
Command string `yaml:"command,omitempty"`
Args []string `yaml:"args,omitempty"`
}
type KubeUser struct {
ClientCertificate string `yaml:"client-certificate,omitempty"`
ClientCertificateData string `yaml:"client-certificate-data,omitempty"`
ClientKey string `yaml:"client-key,omitempty"`
ClientKeyData string `yaml:"client-key-data,omitempty"`
Exec KubeAuthPlugin `yaml:"exec,omitempty"`
}
type KubeUsers struct {
Name string `yaml:"name,omitempty"`
User KubeUser `yaml:"user,omitempty"`
}
type KubeConfig struct {
Kind string `yaml:"kind,omitempty"`
ApiVersion string `yaml:"apiVersion,omitempty"`
Preferences map[string]string `yaml:"preferences,omitempty"`
CurrentContext string `yaml:"current-context,omitempty"`
Clusters []struct {
Name string `yaml:"name,omitempty"`
Cluster map[string]string `yaml:"cluster,omitempty"`
} `yaml:"clusters,omitempty"`
Contexts []struct {
Name string `yaml:"name,omitempty"`
Context map[string]string `yaml:"context,omitempty"`
} `yaml:"contexts,omitempty"`
Users []KubeUsers `yaml:"users,omitempty"`
}
func kubeconfigFilename() (string, error) {
filename := os.Getenv("KUBECONFIG")
if filename != "" {
return filename, nil
}
home := os.Getenv("HOME")
if home == "" {
home = os.Getenv("USERPROFILE")
}
if home == "" {
return "", errors.New("HOME / USERPROFILE, nor KUBECONFIG is set")
}
dir := filepath.Join(home, ".kube")
const defaultModeDir = 0775
os.MkdirAll(dir, defaultModeDir)
filename = filepath.Join(dir, "config")
_, err := os.Stat(filename)
if err != nil {
if util.NoSuchFile(err) {
if config.Verbose {
log.Printf("Kubeconfig `%s` not found", filename)
}
} else {
return "", fmt.Errorf("Unable to stat `%s` Kubeconfig: %v", filename, err)
}
}
return filename, nil
}
func readKubeconfig(filename string) (*KubeConfig, error) {
file, err := os.OpenFile(filename, os.O_RDONLY, 0644)
if err != nil {
return nil, fmt.Errorf("Unable to open `%s`: %v", filename, err)
}
defer file.Close()
content, err := io.ReadAll(file)
if err != nil {
return nil, fmt.Errorf("Unable to read `%s`: %v", filename, err)
}
var config KubeConfig
err = yaml.Unmarshal(content, &config)
if err != nil {
return nil, fmt.Errorf("Unable to unmarshall `%s`: %v", filename, err)
}
return &config, nil
}
//lint:ignore U1000 WIP
func writeKubeconfig(filename string, config *KubeConfig) error {
file, err := os.OpenFile(filename, os.O_WRONLY, 0644)
if err != nil {
return fmt.Errorf("Unable to open `%s`: %v", filename, err)
}
defer file.Close()
content, err := yaml.Marshal(&config)
if err != nil {
return fmt.Errorf("Unable to marshall `%s`: %v", filename, err)
}
_, err = file.Seek(0, io.SeekStart)
if err != nil {
return fmt.Errorf("Unable to seek `%s`: %v", filename, err)
}
wrote, err := file.Write(content)
if err != nil || wrote != len(content) {
return fmt.Errorf("Unable to write `%s` (wrote %d bytes): %s", filename, wrote, util.Errors2(err))
}
err = file.Truncate(int64(wrote))
if err != nil {
return fmt.Errorf("Unable to truncate `%s`: %v", filename, err)
}
return nil
}
//lint:ignore U1000 WIP
func addHeptioUser(configFilename, user, cluster string) {
config, err := readKubeconfig(configFilename)
if err != nil {
log.Fatalf("%v", err)
}
auth := KubeUser{Exec: KubeAuthPlugin{
ApiVersion: "client.authentication.k8s.io/v1alpha1",
Command: "aws-iam-authenticator",
Args: []string{"token", "-i", cluster},
}}
found := false
for i := range config.Users {
u := &config.Users[i]
if u.Name == user {
u.User = auth
found = true
break
}
}
if !found {
config.Users = append(config.Users, KubeUsers{Name: user, User: auth})
}
err = writeKubeconfig(configFilename, config)
if err != nil {
log.Fatalf("%v", err)
}
}