github.com/codefresh-io/kcfi@v0.0.0-20230301195427-c1578715cc46/cmd/kcfi/main.go (about) 1 /* 2 Copyright The Codefresh Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package main 18 19 import ( 20 "flag" 21 "fmt" 22 "io/ioutil" 23 "log" 24 "os" 25 "strings" 26 27 "github.com/spf13/cobra" 28 "github.com/spf13/pflag" 29 "github.com/spf13/viper" 30 31 "k8s.io/klog" 32 "sigs.k8s.io/yaml" 33 34 // Import to initialize client auth plugins. 35 _ "k8s.io/client-go/plugin/pkg/client/auth" 36 37 "helm.sh/helm/v3/pkg/action" 38 "helm.sh/helm/v3/pkg/cli" 39 "helm.sh/helm/v3/pkg/gates" 40 "helm.sh/helm/v3/pkg/kube" 41 kubefake "helm.sh/helm/v3/pkg/kube/fake" 42 "helm.sh/helm/v3/pkg/release" 43 "helm.sh/helm/v3/pkg/storage/driver" 44 45 c "github.com/codefresh-io/kcfi/pkg/config" 46 ) 47 48 // FeatureGateOCI is the feature gate for checking if `helm chart` and `helm registry` commands should work 49 const FeatureGateOCI = gates.Gate("HELM_EXPERIMENTAL_OCI") 50 51 var ( 52 settings *cli.EnvSettings 53 defaultNamespace = "codefresh" 54 55 flagConfig = "config" 56 ) 57 58 var configuredNamespace string 59 60 func init() { 61 log.SetFlags(log.Lshortfile) 62 //Set Codefresh default namespace 63 // if _, ok := os.LookupEnv("HELM_NAMESPACE"); !ok { 64 // os.Setenv("HELM_NAMESPACE", defaultNamespace) 65 // } 66 settings = cli.New() 67 } 68 69 func debug(format string, v ...interface{}) { 70 if settings.Debug { 71 format = fmt.Sprintf("[debug] %s\n", format) 72 log.Output(2, fmt.Sprintf(format, v...)) 73 } 74 } 75 76 func initKubeLogs() { 77 pflag.CommandLine.SetNormalizeFunc(wordSepNormalizeFunc) 78 gofs := flag.NewFlagSet("klog", flag.ExitOnError) 79 klog.InitFlags(gofs) 80 pflag.CommandLine.AddGoFlagSet(gofs) 81 pflag.CommandLine.Set("logtostderr", "true") 82 } 83 84 func main() { 85 initKubeLogs() 86 87 actionConfig := new(action.Configuration) 88 cmd := newRootCmd(actionConfig, os.Stdout, os.Args[1:]) 89 90 configuredNamespace = settings.Namespace() 91 // setting kubernetes client namespace, kube-context, kubeconfig 92 // priorities: 93 // 1. use flags --namespace, --kube-context, --kubeconfig. they are already set in o.Helm.* 94 // 2. use values specified in config.yaml: {kubernetes: {namespace: "", kube-context: "", kubectonfig}} 95 // 3. default context and namespace defined in ~/.kube/config 96 // finding the command being executing and parse its config file to set kube client 97 // we should do it here in main() because helm sets kube client here 98 childCmd, childArgs, _ := cmd.Find(os.Args[1:]) 99 childFlags := childCmd.Flags() 100 configFlag := childFlags.Lookup(flagConfig) 101 if configFlag != nil { 102 //merging config file kubernetes parameters into settings 103 childFlags.Parse(childArgs) 104 configFileName := configFlag.Value.String() 105 if configFileName != "" { 106 viper.SetConfigFile(configFileName) 107 if err := viper.ReadInConfig(); err == nil { 108 debug("Using config file: %s", viper.ConfigFileUsed()) 109 110 viper.BindPFlag(c.KeyKubeNamespace, childFlags.Lookup("namespace")) 111 viper.BindPFlag(c.KeyKubeContext, childFlags.Lookup("kube-context")) 112 viper.BindPFlag(c.KeyKubeKubeconfig, childFlags.Lookup("kubeconfig")) 113 114 if ns := viper.GetString(c.KeyKubeNamespace); ns != "" { 115 configuredNamespace = ns 116 } 117 if kubeContext := viper.GetString(c.KeyKubeContext); kubeContext != "" { 118 settings.KubeContext = kubeContext 119 } 120 if kubeconfig := viper.GetString(c.KeyKubeKubeconfig); kubeconfig != "" { 121 settings.KubeConfig = kubeconfig 122 } 123 } 124 } 125 } 126 127 kubeClientConfig := kube.GetConfig(settings.KubeConfig, settings.KubeContext, configuredNamespace) 128 if settings.KubeToken != "" { 129 kubeClientConfig.BearerToken = &settings.KubeToken 130 } 131 if settings.KubeAPIServer != "" { 132 kubeClientConfig.APIServer = &settings.KubeAPIServer 133 } 134 135 helmDriver := os.Getenv("HELM_DRIVER") 136 debug("Initializing Kubernetes client: namespace=%s kube-context=%s kubeconfig =%s", configuredNamespace, settings.KubeContext, settings.KubeConfig) 137 if err := actionConfig.Init(kubeClientConfig, configuredNamespace, helmDriver, debug); err != nil { 138 log.Fatal(err) 139 } 140 if helmDriver == "memory" { 141 loadReleasesInMemory(actionConfig) 142 } 143 144 if err := cmd.Execute(); err != nil { 145 debug("%+v", err) 146 switch e := err.(type) { 147 case pluginError: 148 os.Exit(e.code) 149 default: 150 os.Exit(1) 151 } 152 } 153 } 154 155 // wordSepNormalizeFunc changes all flags that contain "_" separators 156 func wordSepNormalizeFunc(f *pflag.FlagSet, name string) pflag.NormalizedName { 157 return pflag.NormalizedName(strings.ReplaceAll(name, "_", "-")) 158 } 159 160 func checkOCIFeatureGate() func(_ *cobra.Command, _ []string) error { 161 return func(_ *cobra.Command, _ []string) error { 162 if !FeatureGateOCI.IsEnabled() { 163 return FeatureGateOCI.Error() 164 } 165 return nil 166 } 167 } 168 169 // This function loads releases into the memory storage if the 170 // environment variable is properly set. 171 func loadReleasesInMemory(actionConfig *action.Configuration) { 172 filePaths := strings.Split(os.Getenv("HELM_MEMORY_DRIVER_DATA"), ":") 173 if len(filePaths) == 0 { 174 return 175 } 176 177 store := actionConfig.Releases 178 mem, ok := store.Driver.(*driver.Memory) 179 if !ok { 180 // For an unexpected reason we are not dealing with the memory storage driver. 181 return 182 } 183 184 actionConfig.KubeClient = &kubefake.PrintingKubeClient{Out: ioutil.Discard} 185 186 for _, path := range filePaths { 187 b, err := ioutil.ReadFile(path) 188 if err != nil { 189 log.Fatal("Unable to read memory driver data", err) 190 } 191 192 releases := []*release.Release{} 193 if err := yaml.Unmarshal(b, &releases); err != nil { 194 log.Fatal("Unable to unmarshal memory driver data: ", err) 195 } 196 197 for _, rel := range releases { 198 if err := store.Create(rel); err != nil { 199 log.Fatal(err) 200 } 201 } 202 } 203 // Must reset namespace to the proper one 204 mem.SetNamespace(settings.Namespace()) 205 }