Is there documentation on building k8s jobs in go-client somewhere? In particular I'm trying to convert a job yaml to go code and for the life of me cannot find reference docs that say how the fields convert
-
Does this answer your question? [Using client-go to \`kubectl apply\` against the Kubernetes API directly with multiple types in a single YAML file](https://stackoverflow.com/questions/58783939/using-client-go-to-kubectl-apply-against-the-kubernetes-api-directly-with-mult) – avocadoLambda Jun 10 '20 at 19:45
-
Not completely but definitely helps, thank you! – Saloni Goel Jun 10 '20 at 20:16
2 Answers
k8s.io/api
is a package of Kubernetes which kubectl
and other components use it to implement Kubernetes API
s. In this package, there is a struct that implements the Job
API and you can use it to convert Job manifests to go structs.
I think this code can help:
package main
import (
"fmt"
"io/ioutil"
"os"
"gopkg.in/yaml.v2"
v1 "k8s.io/api/batch/v1"
)
func main() {
file, err := os.Open("/path/to/job.yaml")
if err != nil {
panic(err)
}
b, err := ioutil.ReadAll(file)
if err != nil {
panic(err)
}
job := &v1.Job{}
err = yaml.Unmarshal(b, job)
if err != nil {
panic(err)
}
fmt.Println(job)
}

- 137
- 6
-
1While this code may solve the question, [including an explanation](//meta.stackexchange.com/q/114762) of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please [edit] your answer to add explanations and give an indication of what limitations and assumptions apply. – Dharman Jul 22 '20 at 23:41
Converting YAML to Golang can be difficult, and often times the documentation with examples is missing.
I wrote a tool called naml that is able to convert any Kubernetes YAML to raw Go. It is handy because it works using the version of Kubernetes you are running it against, and is compiled with the latest version of the Kubernetes code base.
If you wanted to create a Job, and see valid Go for the job it would look like this. Creating a Job beeps
with container image boops
[nova@emma ~]$ kubectl create job beeps --image boops
job.batch/beeps created
[nova@emma ~]$
Naml will out a working program by design, but you will also get the output you are looking for.
[nova@emma naml]$ kubectl get job beeps -o yaml | naml codify
// Copyright © 2021 Kris Nóva <kris@nivenly.com>
//
// 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 main
import (
"context"
"fmt"
"os"
"github.com/hexops/valast"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"github.com/kris-nova/naml"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes"
)
// Version is the current release of your application.
var Version string = "0.0.1"
func main() {
// Load the application into the NAML registery
// Note: naml.Register() can be used multiple times.
naml.Register(NewApp("App", "Application autogenerated from NAML v0.3.1"))
// Run the generic naml command line program with
// the application loaded.
err := naml.RunCommandLine()
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
}
// App is a very important grown up business application.
type App struct {
metav1.ObjectMeta
description string
objects []runtime.Object
// ----------------------------------
// Add your configuration fields here
// ----------------------------------
}
// NewApp will create a new instance of App.
//
// See https://github.com/naml-examples for more examples.
//
// This is where you pass in fields to your application (similar to Values.yaml)
// Example: func NewApp(name string, example string, something int) *App
func NewApp(name, description string) *App {
return &App{
description: description,
ObjectMeta: metav1.ObjectMeta{
Name: name,
ResourceVersion: Version,
},
// ----------------------------------
// Add your configuration fields here
// ----------------------------------
}
}
func (a *App) Install(client *kubernetes.Clientset) error {
var err error
beepsJob := &batchv1.Job{
TypeMeta: metav1.TypeMeta{
Kind: "Job",
APIVersion: "batch/batchv1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "beeps",
Namespace: "default",
UID: types.UID("650e4f36-3316-4506-bbe0-1e34c13742cf"),
ResourceVersion: "3231200",
Generation: 1,
Labels: map[string]string{
"controller-uid": "650e4f36-3316-4506-bbe0-1e34c13742cf",
"job-name": "beeps",
},
},
Spec: batchv1.JobSpec{
Parallelism: valast.Addr(int32(1)).(*int32),
Completions: valast.Addr(int32(1)).(*int32),
BackoffLimit: valast.Addr(int32(6)).(*int32),
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{
"controller-uid": "650e4f36-3316-4506-bbe0-1e34c13742cf",
}},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{
"controller-uid": "650e4f36-3316-4506-bbe0-1e34c13742cf",
"job-name": "beeps",
}},
Spec: corev1.PodSpec{
Containers: []corev1.Container{corev1.Container{
Name: "beeps",
Image: "boops",
TerminationMessagePath: "/dev/termination-log",
TerminationMessagePolicy: corev1.TerminationMessagePolicy("File"),
ImagePullPolicy: corev1.PullPolicy("Always"),
}},
RestartPolicy: corev1.RestartPolicy("Never"),
TerminationGracePeriodSeconds: valast.Addr(int64(30)).(*int64),
DNSPolicy: corev1.DNSPolicy("ClusterFirst"),
SecurityContext: &corev1.PodSecurityContext{},
SchedulerName: "default-scheduler",
},
},
CompletionMode: valast.Addr(batchv1.CompletionMode("NonIndexed")).(*batchv1.CompletionMode),
Suspend: valast.Addr(false).(*bool),
},
}
a.objects = append(a.objects, beepsJob)
if client != nil {
_, err = client.BatchV1().Jobs("default").Create(context.TODO(), beepsJob, metav1.CreateOptions{})
if err != nil {
return err
}
}
return err
}
func (a *App) Uninstall(client *kubernetes.Clientset) error {
var err error
if client != nil {
err = client.BatchV1().Jobs("default").Delete(context.TODO(), "beeps", metav1.DeleteOptions{})
if err != nil {
return err
}
}
return err
}
func (a *App) Description() string {
return a.description
}
func (a *App) Meta() *metav1.ObjectMeta {
return &a.ObjectMeta
}
func (a *App) Objects() []runtime.Object {
return a.objects
}

- 51
- 2