We are using the Go helm.sh/helm/v3/pkg/action package to install Helm charts.
We are able to pass installation values to Helm chart Run functions via map[string]interface{}, as in Samples on kubernetes helm golang client:
rel, err := client.Run(myChart, vals)
if err != nil {
log.Println(err)
panic(err)
}
But we have a values.yaml file also which was passed as -f values.yaml when installing charts from the CLI.
Is there a way to pass these values.yaml files via the action Go package during installation (client.Run())?
Or do we need to unmarshal the YAML file and pass that also as map:
data2 := make(map[string]interface{})
yfile2, err := ioutil.ReadFile("./utils/values.yaml")
fmt.Println(err)
err = yaml.Unmarshal(yfile2, &data2)
One straightforward thing to do could be to reuse the helm.sh/helm/v3/pkg/cli/values package. This has the logic to handle the -f option (and also --set and its variants) and return a unified map[string]interface{}. This could look like:
import (
"helm.sh/helm/v3/pkg/cli"
"helm.sh/helm/v3/pkg/cli/values"
"helm.sh/helm/v3/pkg/getter"
)
envSettings := cli.New()
providers := getter.All(envSettings)
options := values.Options{
ValueFiles: []string{"./utils/values.yaml"},
}
theValues := options.MergeValues(providers)
Now theValues is the map[string]interface{} that results from reading those files. You can customize the values further if required (as Go data) and then pass that to install.Run().
Related
Now i have Pods as Kubernetes structs wiht the help of the command
pods , err := clientset.CoreV1().Pods("namespace_String").List(context.TODO(), metav1.ListOptions{})
now i do i get it as individual yaml files
which command should i use
for i , pod := range pods.Items{
if i==0{
t := reflect.TypeOF(&pod)
for j := 0; j<t.NumMethod(); j++{
m := t.Method(j)
fmt.Println(m.Name)
}
}
}
this function will print the list of functions in the pod item which should i use
Thanks for the answer
The yaml is just a representation of the Pod object in the kubernetes internal storage in etcd. With your client-go what you have got is the Pod instance, of the type v1.Pod. So you should be able to work with this object itself and get whatever you want, for example p.Labels() etc. But if for some reason, you are insisting on getting a yaml, you can do that via:
import (
"sigs.k8s.io/yaml"
)
b, err := yaml.Marshal(pod)
if err != nil {
// handle err
}
log.Printf("Yaml of the pod is: %q", string(b))
Note that yaml library coming here is not coming from client-go library. The documentation for the yaml library can be found in: https://pkg.go.dev/sigs.k8s.io/yaml#Marshal
Instead of yaml if you want to use json, you can simply use the Marshal function https://pkg.go.dev/k8s.io/apiserver/pkg/apis/example/v1#Pod.Marshal provided by the v1.Pod struct itself, like any other Go object.
To get individual pod using client-go:
pod, err := clientset.CoreV1().Pods("pod_namespace").Get(context.TODO(),"pod_name", metav1.GetOptions{})
if err!=nil {
log.Fatalln(err)
}
// do something with pod
I'm trying to write a "Download Page Website", and I trying to show the file icon to my webpage.
Like Windows system, ".exe" file has icon image inside. Or linux executable file. Can I read it?
I know python can do it with a package name "win32api", is it any package for Golang to achieve this function?
You can use the linux package in your advantage.
For example, you can use icoextract, which can be installed via apt:
apt install icoextract
And then run it like this:
icoextract /path/to/file.exe /path/to/file.ico
Go make possible to call commands and execute them using the package os/exec. So you can do something like
func ExtractIcon(executablePath string) []byte {
file, err := ioutil.TempFile("dir", "prefix")
if err != nil {
log.Fatal(err)
}
defer os.Remove(file.Name())
cmd := exec.Command("icoextract", executablePath, file.Name())
if err = cmd.Run(); err != nil {
log.Fatal(err)
}
content, _ := ioutil.ReadFile(file.Name())
return content
}
I'm creating a simple program to register my IP to OpenDNS when it changes. I know about the ddclient but I want to write my own just for learning purposes.
To be able to perform any operation on OpenDNS, I have to call the URL specifying my user and pass, so a curl example would be something like: curl -u user:password https://updates.opendns.com/nic/update?hostname=xxxx&myip=123.123.123.123
In Go I created the following function:
func registerNewIpToOpenDns(ip string) (int, error) {
openDnsURL := "https://updates.opendns.com/nic/update?hostname=xxxx&myip=" + ip
req, err := http.NewRequest("GET", openDnsURL, nil)
if err != nil {
return 0, err
}
req.SetBasicAuth("USER", "PASS")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return 0, err
}
defer resp.Body.Close()
return resp.StatusCode, nil
}
So how should I perform the input of the user/pass to this program? I will let this project 'Public' in Github.
I was thinking in creating a file something like "input" and add it to .gitignore.
So if someone else wants to use the program, the person would just need to create it own "input" file and the program would read from it.
What do you think?
Put the configuration data that wouldn't be true for everybody in environment variables.
Use os.Getenv() to retrieve the variables at runtime. Make sure they are set to something valid looking (at least not empty string) as part of your program's configuration.
You can then set the environment variables in a systemd configuration file if you're running this from systemd, or in the .bash_config for a user dedicated to this process, or wherever is the most convenient for where your program is executed.
Or, create a configuration file you can read from your program. I usually use Json encoding for configuration like this, but you could use anything. Reading secrets from configuration files might arguably be somewhat safer than environment variables which can often be introspected by system processes.
When I create a configuration file, I usually model my configuration with a struct,
type Config struct {
Username string
Password string
}
Then as part of my program's initialization, I'd do something like
const ConfigFileEnv "ConfigFile" // avoid typing errors
var config Config
...
if f, err := os.Open(os.Getenv(ConfigFileEnv); err != nil {
panic(fmt.Errorf("Couldn't open config file %s: %w",
os.Getenv(ConfigFileEnv),
err,
))
} else if err := json.NewDecoder(f).Decode(&config); err != nil {
panic(fmt.Errorf("Couldn't decode json from config file %s: %w",
os.Getenv(ConfigFileEnv),
err
)
}
// Now config file has been loaded into config
...
req.SetBasicAuth(config.Username, config.Password)
Working minimal example (without your logic): https://github.com/farrellit/stackoverflow/tree/main/69335827
So the question seems to have been asked a couple of times before, but none of the previous answers worked for me, I go from errors to errors to no results.
So as I am most certainly missing something that I don't see I would like for some help:
res, err := os.Create(strings.Replace(f, ".tmpl", "", -1))
if err != nil {
log.Fatal(err)
}
t, err := template.ParseFiles(f)
if err != nil {
log.Fatal(err)
}
removes = append(removes, res.Name())
config := make(map[string]string)
for _, v := range vars {
config[v] = os.Getenv(v)
}
err = t.Execute(res, config)
if err != nil {
log.Fatal(err)
}
res.Close()
So to explain what I am doing, I'm passing a string to a file (path/file) that has a yaml.tmpl extension. The result file should be yaml so I remove the last part to generate the result file name.
I then parse the file with go template and then I execute with a configmap that I generate.
This is working fine like this but I would like to add: .Option("missingkey=error") to have it generate an error in case I don't give a value from the configmap to a variable in the template.
So I tried to add the options in the template parse file like this:
t, err := template.New("test").Option("missingkey=error").ParseFiles(f)
But I can't use template Exectute and have to use template ExecuteTemplate but with those I get:
template: no template "test" associated with template "test" or template: test: "test" is an incomplete or empty template
In the rare cases I don't get an error, it just ignore the option like if I do this:
err = t.Option("missingkey=error").Execute(res, config)
Does anyone has an idea of what I'm doing wrong?
EDIT
I updated the code with the answer of Cerise Limon and here is the playground: playground
Currently that playground just ignore errors and do the template even if the config passed is empty and there is no or condition in the template.
The ParseFiles method documentation says:
Since the templates created by ParseFiles are named by the base names of the argument files, t should usually have the name of one of the (base) names of the files.
Use filepath.Base to get the base name of the file. Use that name as the name of the template:
t, err := template.New(filepath.Base(f)).Option("missingkey=error").ParseFiles(f)
Run an example on the Playground.
Im using the following code to install chart that is bounded in my source code (eg. in my app/chart/chart1 in my go bin app), Now I need to move the chart to git repository or to artifactory,
My question is how can I install the chart from outside my program?
This is the code I use which works for bundled chart
I use the helm3 loader package which works when I have the chart bundled in my app
chart, err := loader.Load(“chart/chart1”)
https://pkg.go.dev/helm.sh/helm/v3#v3.5.4/pkg/chart/loader
Should I load it somehow with an http call or helm have some built in functionality ? we need some efficient way to handle it
It seems that helm during its upgrade/install commands checks first a couple of different locations which you can see getting called here. The content of that function is here.
And then continues here with loader.Load
You can use something like this for installing nginx chart
myChart, err := loader.Load("https://charts.bitnami.com/bitnami/nginx-8.8.4.tgz")
...
install := action.NewInstall(m.actionConfig)
install.ReleaseName = "my-release"
...
myRelease, err := install.Run(myChart, myValues)
It would be similar to:
helm install my-release https://charts.bitnami.com/bitnami/nginx-8.8.4.tgz
loader.load checks only for files and directories. If you want to use URL helm sdk provides LocateChart method in Install interface. Here is an example:
settings := cli.New()
actionConfig := new(action.Configuration)
if err := actionConfig.Init(settings.RESTClientGetter(), settings.Namespace(), os.Getenv("HELM_DRIVER"), log.Printf); err != nil {
log.Printf("%+v", err)
os.Exit(1)
}
client := action.NewInstall(actionConfig)
chrt_path, err := client.LocateChart("https://github.com/kubernetes/ingress-nginx/releases/download/helm-chart-4.0.6/ingress-nginx-4.0.6.tgz", settings); if err != nil {
panic(err)
}
myChart, err := loader.Load(chrt_path); if err != nil {
panic(err)
}
Then you can simple setup install options and call client.Run method.