package main import ( "flag" "fmt" "io/ioutil" "path/filepath" "strings" "gopkg.in/yaml.v2" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/util/homedir" "git.martyn.berlin/martyn/helm-import/internal/exporters" // // Uncomment to load all auth plugins // "k8s.io/client-go/plugin/pkg/client/auth" // // Or uncomment to load specific auth plugins // _ "k8s.io/client-go/plugin/pkg/client/auth/azure" // _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" // _ "k8s.io/client-go/plugin/pkg/client/auth/oidc" // _ "k8s.io/client-go/plugin/pkg/client/auth/openstack" ) type objectFlags []string func (i *objectFlags) String() string { return strings.Join(*i, ", ") } func (i *objectFlags) Set(value string) error { *i = append(*i, value) return nil } type jqCommands []string func (i *jqCommands) String() string { return strings.Join(*i, ", ") } func (i *jqCommands) Set(value string) error { *i = append(*i, value) return nil } var deployments objectFlags var services objectFlags var configmaps objectFlags var secrets objectFlags var ingresses objectFlags var namespace string var noBuiltinMods bool var lessOpinions bool var deploymentJqCommands jqCommands var serviceJqCommands jqCommands var ingressJqCommands jqCommands var globalJqCommands jqCommands var configmapJqCommands jqCommands var secretJqCommands jqCommands type chartDef struct { APIVersion string `json: "apiVersion"` Name string `json: "name"` Description string `json: "description"` Type string `json: "type"` Version string `json: "version"` AppVersion string `json: "appVersion"` } var chart chartDef func main() { chartYamlFile, err := ioutil.ReadFile("Chart.yaml") if err != nil { panic("Run this from the chart folder, where Chart.yaml is. Error: " + err.Error()) } err = yaml.Unmarshal(chartYamlFile, &chart) if err != nil { panic("Could not read the chart yaml as expected. Error: " + err.Error()) } var kubeconfig *string if home := homedir.HomeDir(); home != "" { kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file") } else { kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file") } flag.StringVar(&namespace, "namespace", "default", "namespace to work in") flag.Var(&deployments, "deployment", "deployment to import into the helm chart (can be specified multiple times)") flag.Var(&services, "service", "service to import into the helm chart (can be specified multiple times)") flag.Var(&configmaps, "configmap", "configmap to import into the helm chart (can be specified multiple times)") flag.Var(&secrets, "secret", "secret to import into the helm chart (can be specified multiple times)") flag.Var(&ingresses, "ingress", "ingress to import into the helm chart (can be specified multiple times)") flag.BoolVar(&noBuiltinMods, "nobuiltinmods", false, "don't do the built-in modifications, only ones passed in by user") flag.BoolVar(&lessOpinions, "lessopinions", false, "just import the objects into files, don't get opinionated") flag.Var(&globalJqCommands, "globalJqCommands", "extra jq commands to apply to all k8s objects") flag.Var(&deploymentJqCommands, "deploymentJqCommands", "extra jq commands to apply to deployments") flag.Var(&serviceJqCommands, "serviceJqCommands", "extra jq commands to apply to services") flag.Var(&configmapJqCommands, "configmapJqCommands", "extra jq commands to apply to configmaps") flag.Var(&secretJqCommands, "secretJqCommands", "extra jq commands to apply to secrets") flag.Var(&ingressJqCommands, "ingressJqCommands", "extra jq commands to apply to ingresses") flag.Parse() // use the current context in kubeconfig config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig) if err != nil { panic(err.Error()) } // create the clientset clientset, err := kubernetes.NewForConfig(config) if err != nil { panic(err.Error()) } var y = "" fileString := "" for _, deployment := range deployments { deploymentObject, err := clientset.AppsV1().Deployments(namespace).Get(deployment, metav1.GetOptions{}) if errors.IsNotFound(err) { fmt.Printf("Deployment %s in namespace %s not found\n", deployment, namespace) } else if statusError, isStatus := err.(*errors.StatusError); isStatus { fmt.Printf("Error getting deployment %s in namespace %s: %v\n", deployment, namespace, statusError.ErrStatus.Message) } else if err != nil { panic(err.Error()) } else { fmt.Printf("Found deployment %s in namespace %s\n", deployment, namespace) y = exporters.Deployment(deploymentObject, append(deploymentJqCommands, globalJqCommands...), noBuiltinMods, chart.Name, lessOpinions) if fileString != "" { fileString += "---\n" } fileString += y } } if y != "" { err = ioutil.WriteFile("templates/imported-deployment.yaml", []byte(fileString), 0644) if err != nil { panic(err.Error()) } } for _, service := range services { serviceObject, err := clientset.CoreV1().Services(namespace).Get(service, metav1.GetOptions{}) if errors.IsNotFound(err) { fmt.Printf("service %s in namespace %s not found\n", service, namespace) } else if statusError, isStatus := err.(*errors.StatusError); isStatus { fmt.Printf("Error getting service %s in namespace %s: %v\n", service, namespace, statusError.ErrStatus.Message) } else if err != nil { panic(err.Error()) } else { fmt.Printf("Found service %s in namespace %s\n", service, namespace) y = exporters.Service(serviceObject, append(serviceJqCommands, globalJqCommands...), noBuiltinMods, chart.Name, lessOpinions) if fileString != "" { fileString += "---\n" } fileString += y } } if y != "" { err = ioutil.WriteFile("templates/imported-service.yaml", []byte(fileString), 0644) if err != nil { panic(err.Error()) } } for _, configmap := range configmaps { configmapObject, err := clientset.CoreV1().ConfigMaps(namespace).Get(configmap, metav1.GetOptions{}) if errors.IsNotFound(err) { fmt.Printf("configmap %s in namespace %s not found\n", configmap, namespace) } else if statusError, isStatus := err.(*errors.StatusError); isStatus { fmt.Printf("Error getting configmap %s in namespace %s: %v\n", configmap, namespace, statusError.ErrStatus.Message) } else if err != nil { panic(err.Error()) } else { fmt.Printf("Found configmap %s in namespace %s\n", configmap, namespace) y = exporters.Configmap(configmapObject, append(serviceJqCommands, globalJqCommands...), noBuiltinMods, chart.Name, lessOpinions) } if fileString != "" { fileString += "---\n" } fileString += y } if y != "" { err = ioutil.WriteFile("templates/imported-configmap.yaml", []byte(fileString), 0644) if err != nil { panic(err.Error()) } } for _, secret := range secrets { secretObject, err := clientset.CoreV1().Secrets(namespace).Get(secret, metav1.GetOptions{}) if errors.IsNotFound(err) { fmt.Printf("secret %s in namespace %s not found\n", secret, namespace) } else if statusError, isStatus := err.(*errors.StatusError); isStatus { fmt.Printf("Error getting secret %s in namespace %s: %v\n", secret, namespace, statusError.ErrStatus.Message) } else if err != nil { panic(err.Error()) } else { fmt.Printf("Found secret %s in namespace %s\n", secret, namespace) y = exporters.Secret(secretObject, append(serviceJqCommands, globalJqCommands...), noBuiltinMods, chart.Name, lessOpinions) } if fileString != "" { fileString += "---\n" } fileString += y } if y != "" { err = ioutil.WriteFile("templates/imported-secret.yaml", []byte(fileString), 0644) if err != nil { panic(err.Error()) } } for _, ingress := range ingresses { ingressObject, err := clientset.ExtensionsV1beta1().Ingresses(namespace).Get(ingress, metav1.GetOptions{}) if errors.IsNotFound(err) { fmt.Printf("ingress %s in namespace %s not found\n", ingress, namespace) } else if statusError, isStatus := err.(*errors.StatusError); isStatus { fmt.Printf("Error getting ingress %s in namespace %s: %v\n", ingress, namespace, statusError.ErrStatus.Message) } else if err != nil { panic(err.Error()) } else { fmt.Printf("Found ingress %s in namespace %s\n", ingress, namespace) y = exporters.Ingress(ingressObject, append(serviceJqCommands, globalJqCommands...), noBuiltinMods, chart.Name, lessOpinions) } if fileString != "" { fileString += "---\n" } fileString += y } if y != "" { err = ioutil.WriteFile("templates/imported-ingress.yaml", []byte(fileString), 0644) if err != nil { panic(err.Error()) } } }