List Openshift objects via Go client API - go

Trying to write a microservice to manage imagestreams on my Openshift cluster. I read the oc client code to work out how to read my kubeconfig and create the Client.
I can make requests with the Kubernetes Client to get the Kubernetes objects, e.g. pods, but any requests I make with the Openshift Client returns back an empty list.
I'm new to Go as well, so I'm sure I'm doing something wrong. Here's what I have so far:
package main
import (
"fmt"
"log"
"github.com/spf13/pflag"
kapi "k8s.io/kubernetes/pkg/api"
"github.com/openshift/origin/pkg/cmd/util/clientcmd"
)
func main() {
flags := pflag.FlagSet{}
factory := clientcmd.New(&flags)
osclient, kclient, err := factory.Clients()
if err != nil {
log.Fatalln("Error:", err)
}
config, _ := factory.ClientConfig()
fmt.Println("KClient config", config)
config, _ = factory.OpenShiftClientConfig.ClientConfig()
fmt.Println("OSClient config", config)
// Empty list!
projects, err := osclient.Projects().List(kapi.ListOptions{})
if err != nil {
log.Println("Error:", err)
} else {
fmt.Println("Projects", projects, len(projects.Items))
}
// Also empty list
buildconfigs, err := osclient.BuildConfigs("my-project").List(kapi.ListOptions{})
if err != nil {
log.Println("Error:", err)
} else {
fmt.Println("Buildconfigs", buildconfigs, len(buildconfigs.Items))
}
// Works!
pods, err := kclient.Pods("my-project").List(kapi.ListOptions{})
if err != nil {
log.Println("Error:", err)
} else {
fmt.Println("Pods", len(pods.Items))
for _, pod := range pods.Items {
fmt.Println(pod.ObjectMeta.Name)
}
}
// Permission error, as expected
namespaces, err := kclient.Namespaces().List(kapi.ListOptions{})
if err != nil {
log.Println("Error:", err)
} else {
fmt.Println("Namespaces", namespaces, len(namespaces.Items))
}
}

You were ever so close and the issue was a tiny one: you needed to include the following additional import:
import _ "github.com/openshift/origin/pkg/api/install"
I'm not fully clear what the import actually does, but evidently it causes necessary additional functionality to be linked into the binary, without which the OpenShift client doesn't work (returns empty lists).
All of the OpenShift command line tools include that import, and as of writing many include some/all of the following as well:
import (
_ "github.com/openshift/origin/pkg/api/install"
_ "k8s.io/kubernetes/pkg/api/install"
_ "k8s.io/kubernetes/pkg/apis/autoscaling/install"
_ "k8s.io/kubernetes/pkg/apis/batch/install"
_ "k8s.io/kubernetes/pkg/apis/extensions/install"
)
Finally, here's a full code example which works for me (updated against origin v3.6.0-alpha):
package main
import (
"fmt"
_ "github.com/openshift/origin/pkg/api/install"
"github.com/openshift/origin/pkg/cmd/util/clientcmd"
"github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/apis/meta/v1"
)
func main() {
factory := clientcmd.New(pflag.CommandLine)
pflag.Parse()
oc, kc, err := factory.Clients()
if err != nil {
panic(err)
}
namespace, _, err := factory.DefaultNamespace()
if err != nil {
panic(err)
}
pods, err := kc.Core().Pods(namespace).List(v1.ListOptions{})
if err != nil {
panic(err)
}
for _, pod := range pods.Items {
fmt.Printf("Pod: %s\n", pod.Name)
}
buildconfigs, err := oc.BuildConfigs(namespace).List(v1.ListOptions{})
if err != nil {
panic(err)
}
for _, buildconfig := range buildconfigs.Items {
fmt.Printf("BuildConfig: %s\n", buildconfig.Name)
}
}
To run this example, you will currently need to vendor OpenShift and its dependencies. One very hacky way to do this is as follows:
rm -rf vendor
mkdir -p vendor/github.com/openshift/origin
ln -s $GOPATH/src/github.com/openshift/origin/vendor/* vendor
ln -s $GOPATH/src/github.com/openshift/origin/vendor/github.com/* vendor/github.com
ln -s $GOPATH/src/github.com/openshift/origin/vendor/github.com/openshift/* vendor/github.com/openshift
ln -s $GOPATH/src/github.com/openshift/origin/pkg vendor/github.com/openshift/origin
Finally, it is intended to make a proper standalone Go client for OpenShift - the backlog card for this is at https://trello.com/c/PTDrY0GF/794-13-client-provide-go-client-similar-to-kubernetes.

Related

How to use kubernetes go-client on amazon eks service?

I've been looking for documentation for a long time and still couldn't find any clear connection procedure.
I came up with this code sample :
package aws
import (
"fmt"
"net/http"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/eks"
"github.com/joho/godotenv"
)
func Connect() {
godotenv.Load(".env")
session := session.Must(session.NewSession())
svc := eks.New(session)
clusters, err := svc.ListClusters(&eks.ListClustersInput{})
if err != nil {
fmt.Println(err.Error())
}
fmt.Println(clusters)
}
i mean, this still returns a 403 forbidden error because of env variable mess, but the code is valid i guess. My question is, having this connection established : how to convert this svc variable into the *kubernetes.Clientset one from the go driver ?
Have you had a look at the client-go example on how to authenticate in-cluster?
Code that authenticate to the Kubernetes API typically start like this:
// creates the in-cluster config
config, err := rest.InClusterConfig()
if err != nil {
panic(err.Error())
}
// creates the clientset
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
panic(err.Error())
}
I use the following code to automatically detect where its running from local machine or any kubernetes cluster.
var config *rest.Config
if _, err := os.Stat("/var/run/secrets/kubernetes.io/serviceaccount/token"); err == nil {
config, err = rest.InClusterConfig()
if err != nil {
log.Fatal(err)
}
} else if os.IsNotExist(err) {
config, err = clientcmd.BuildConfigFromFlags("", *kubeConfig)
if err != nil {
log.Fatal("No serviceaccount mounted or -kubeconfig flag passed or .kube/config file \n " ,err)
}
}
// Create an rest client not targeting specific API version
clientSet, err := kubernetes.NewForConfig(config)
if err != nil {
log.Fatal(err)
}

How to list running instances on Google Cloud Platform with Go

I'm trying to learn Go by managing Google Cloud Platform. I didn't understand how to use related functions about Compute. The goal is listing instances with some go code.
This is https://godoc.org/google.golang.org/api/compute/v1#InstancesService.List the related function.
func (r *InstancesService) List(project string, zone string) *InstancesListCall
There are two structs, InstancesService and InstancesListCall
As far as i understand i should define these structs but it's not clear the things should be defined in the structs. I've searched for examples but many of them using rest calls instead of golang api. Have any idea how to list instances with go?
i had to write something like this today and googling for examples turned up surprisingly little. i've written up what i learned below, however, i'm quite new to golang so maybe smarter people can suggest improvements.
my work in progress is at: https://github.com/grenade/rubberneck
if you want to run your go program from a development pc that is not on the google compute platform:
set up the gcloud cli to run on your pc (instructions: https://cloud.google.com/sdk/gcloud)
create a service account for your go application to run under (instructions: https://cloud.google.com/docs/authentication/production#creating_a_service_account)
grant permissions to the service account (use the same instructions link above)
create a local key file containing your new service account credentials (use the same instructions link above)
set the GOOGLE_APPLICATION_CREDENTIALS environment variable to the path of your local key file
write your go application. something like this:
package main
import (
"golang.org/x/net/context"
"google.golang.org/api/compute/v1"
"golang.org/x/oauth2/google"
"fmt"
"strings"
)
func main() {
projects := [...]string{
"my-project-one",
"my-project-two",
}
filters := [...]string{
"status = RUNNING",
"name != my-uninteresting-instance-one",
"name != my-uninteresting-instance-two",
}
ctx := context.Background()
client, err := google.DefaultClient(ctx,compute.ComputeScope)
if err != nil {
fmt.Println(err)
}
computeService, err := compute.New(client)
for _, project := range projects {
zoneListCall := computeService.Zones.List(project)
zoneList, err := zoneListCall.Do()
if err != nil {
fmt.Println("Error", err)
} else {
for _, zone := range zoneList.Items {
instanceListCall := computeService.Instances.List(project, zone.Name)
instanceListCall.Filter(strings.Join(filters[:], " "))
instanceList, err := instanceListCall.Do()
if err != nil {
fmt.Println("Error", err)
} else {
for _, instance := range instanceList.Items {
if workerType, isWorker := instance.Labels["worker-type"]; isWorker {
m := strings.Split(instance.MachineType, "/")
fmt.Printf("cloud: gcp, zone: %v, name: %v, instance id: %v, machine type: %v, worker type: %v, launch time: %v\n",
zone.Name,
instance.Name,
instance.Id,
m[len(m)-1],
workerType,
instance.CreationTimestamp)
}
}
}
}
}
}
}
You can also use Aggregated List which will search every zone for you. This saves you having to do nested loops or figuring out what the zones are.
https://pkg.go.dev/cloud.google.com/go/compute/apiv1#InstancesClient.AggregatedList
The below assumes you have logged into gcloud and set your ADC.
$ gcloud init
$ gcloud auth application-default login
Using a service account key is also possible but not demonstrated below.
package main
import (
"context"
"fmt"
"log"
compute "cloud.google.com/go/compute/apiv1"
"google.golang.org/api/iterator"
protobuf "google.golang.org/genproto/googleapis/cloud/compute/v1"
)
func main() {
ctx := context.Background()
c, err := compute.NewInstancesRESTClient(ctx)
if err != nil {
log.Fatalln(err)
}
defer c.Close()
project := "my-project"
req := &protobuf.AggregatedListInstancesRequest{
Project: project,
}
it := c.client.AggregatedList(ctx, req)
for {
resp, err := it.Next()
if err == iterator.Done {
break
}
if err != nil {
log.Fatalln(err)
}
fmt.Println(resp)
}
}
You can also use AggregatedList and cloud cred in golang and can retrieve all instance information
package main
import (
"context"
"flag"
"fmt"
"log"
"github.com/binxio/gcloudconfig"
"golang.org/x/oauth2/google"
"google.golang.org/api/compute/v1"
"google.golang.org/api/option"
)
func main() {
//gcp session
var credentials *google.Credentials
name := flag.String("configuration", "", "`kunets` of the configuration to use")
project := flag.String("project", "", "`kunets` of the project to query")
flag.Parse()
credentials, _ = gcloudconfig.GetCredentials(*name)
if project == nil || *project == "" {
project = &credentials.ProjectID
}
if *project == "" {
log.Printf("%v", credentials)
log.Fatal("no -project specified")
}
computeService, err := compute.NewService(context.Background(), option.WithCredentials(credentials))
if err != nil {
log.Fatal(err)
}
token := ""
var list *compute.InstanceAggregatedList
if list, err = computeService.Instances.AggregatedList(*project).PageToken(token).Do(); err != nil {
log.Fatal(err)
}
for _, instances := range list.Items {
for _, instance := range instances.Instances {
EXTERNAL_IP := instance.NetworkInterfaces[0].AccessConfigs[0].NatIP
fmt.Printf("%s \n", EXTERNAL_IP)
INTERNAL_IP := instance.NetworkInterfaces[0].NetworkIP
fmt.Printf("%s \n", INTERNAL_IP)
fmt.Printf("%s \n", instance.Name)
}
}
}

Docker client build error

This defeats me. I am not sure what I am doing wrong. There is so less documentation that searching did not produce good result. I will be happy to see what is the reason behind this strange behaviour.
I am on a MAC (10.11.6) and I am running docker for MAC (beta)
Here is the code I am trying to run
package main
import (
"fmt"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
"golang.org/x/net/context"
)
func main() {
defaultHeaders := map[string]string{"User-Agent": "ego-v-0.0.1"}
cli, _ := client.NewClient("unix:///var/run/docker.sock", "v1.24", nil, defaultHeaders)
options := types.ImageBuildOptions{
Dockerfile: "/path/to/my/Dockerfile",
SuppressOutput: false,
Remove: true,
ForceRemove: true,
PullParent: true}
buildResponse, err := cli.ImageBuild(context.Background(), nil, options)
if err != nil {
fmt.Printf("%s", err.Error())
}
fmt.Printf("%s", buildResponse.OSType)
}
This gives me this error -
Error response from daemon: {"message":"Cannot locate specified Dockerfile: /path/to/my/Dockerfile"}
Whereas when I run this command (from the same directory where my Go code is)
docker build /path/to/my
It works absolutely fine.
What am I doing wrong? I feel like banging my head against a wall now. Please help.
------------ EDIT / ADD ------------
I ended up doing this -
package main
import (
"archive/tar"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"golang.org/x/net/context"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
)
func tarit(source, target string) error {
filename := filepath.Base(source)
target = filepath.Join(target, fmt.Sprintf("%s.tar", filename))
fmt.Println(target)
tarfile, err := os.Create(target)
if err != nil {
return err
}
defer tarfile.Close()
tarball := tar.NewWriter(tarfile)
defer tarball.Close()
info, err := os.Stat(source)
if err != nil {
return nil
}
var baseDir string
if info.IsDir() {
baseDir = filepath.Base(source)
}
return filepath.Walk(source,
func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
header, err := tar.FileInfoHeader(info, info.Name())
if err != nil {
return err
}
if baseDir != "" {
header.Name = filepath.Join(baseDir, strings.TrimPrefix(path, source))
}
if err := tarball.WriteHeader(header); err != nil {
return err
}
if info.IsDir() {
return nil
}
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
_, err = io.Copy(tarball, file)
return err
})
}
func main() {
tarit("/dir/with/files/and/dockerfile", "repo")
dockerBuildContext, err := os.Open("./repo.tar")
defer dockerBuildContext.Close()
defaultHeaders := map[string]string{"User-Agent": "ego-v-0.0.1"}
cli, _ := client.NewClient("unix:///var/run/docker.sock", "v1.24", nil, defaultHeaders)
options := types.ImageBuildOptions{
Dockerfile: "repo/Dockerfile",
SuppressOutput: false,
Remove: true,
ForceRemove: true,
PullParent: true}
buildResponse, err := cli.ImageBuild(context.Background(), dockerBuildContext, options)
if err != nil {
fmt.Printf("%s", err.Error())
}
fmt.Printf("********* %s **********", buildResponse.OSType)
}
Now it is not complaining about anything and I can see that the tar is getting made properly and the last println is printing
********* linux **********
Which is a reply from the server. But it does not build anything. I understand that reply is almost immediate as under the hood it is just a POST request. But not sure why it is not building anything although.
The Dockerfile has to be part of the build context that gets TARd and sent to the engine. You shouldn't use an absolute path to the Dockerfile, it must be relative to the context, so you just pass the name.
I don't think the CLI command you demonstrate is right - unless Dockerfile is actually a directory name in your case:
> ls /tmp/path/to/context
Dockerfile
> docker build /tmp/path/to/context/Dockerfile
unable to prepare context: context must be a directory: /tmp/path/to/context/Dockerfile
When you send just the path (which contains the Dockerfile file), that works:
> docker build /tmp/path/to/context
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM alpine
---> ee4603260daa
Successfully built ee4603260daa
The working code to this problem. For anyone who will probably be stuck like me -
package main
import (
"fmt"
"io/ioutil"
"os"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
"github.com/jhoonb/archivex"
"golang.org/x/net/context"
)
func main() {
tar := new(archivex.TarFile)
tar.Create("/path/to/tar/archieve")
tar.AddAll("/path/to/base/folder", false)
tar.Close()
dockerBuildContext, err := os.Open("/path/to/tar/archieve.tar")
defer dockerBuildContext.Close()
defaultHeaders := map[string]string{"Content-Type": "application/tar"}
cli, _ := client.NewClient("unix:///var/run/docker.sock", "v1.24", nil, defaultHeaders)
options := types.ImageBuildOptions{
SuppressOutput: true,
Remove: true,
ForceRemove: true,
PullParent: true,
Tags: []string{"xxx"}}
buildResponse, err := cli.ImageBuild(context.Background(), dockerBuildContext, options)
defer buildResponse.Body.Close()
if err != nil {
fmt.Printf("%s", err.Error())
}
//time.Sleep(5000 * time.Millisecond)
fmt.Printf("********* %s **********", buildResponse.OSType)
response, err := ioutil.ReadAll(buildResponse.Body)
if err != nil {
fmt.Printf("%s", err.Error())
}
fmt.Println(string(response))
}
Be careful that a lot of error checks are not done in this code. This is just a working code where I am using docker client lib successfully.
Let me know what you think.
Thanks to -
"github.com/jhoonb/archivex"

Build Docker Image From Go Code

I'm trying to build a Docker image using the Docker API and Docker Go libraries (https://github.com/docker/engine-api/). Code example:
package main
import (
"fmt"
"github.com/docker/engine-api/client"
"github.com/docker/engine-api/types"
"golang.org/x/net/context"
)
func main() {
defaultHeaders := map[string]string{"User-Agent": "engine-api-cli-1.0"}
cli, err := client.NewClient("unix:///var/run/docker.sock", "v1.22", nil, defaultHeaders)
if err != nil {
panic(err)
}
fmt.Print(cli.ClientVersion())
opt := types.ImageBuildOptions{
CPUSetCPUs: "2",
CPUSetMems: "12",
CPUShares: 20,
CPUQuota: 10,
CPUPeriod: 30,
Memory: 256,
MemorySwap: 512,
ShmSize: 10,
CgroupParent: "cgroup_parent",
Dockerfile: "dockerSrc/docker-debug-container/Dockerfile",
}
_, err = cli.ImageBuild(context.Background(), nil, opt)
if err == nil || err.Error() != "Error response from daemon: Server error" {
fmt.Printf("expected a Server Error, got %v", err)
}
}
The error is always same:
Error response from daemon: Cannot locate specified Dockerfile: dockerSrc/docker-debug-container/Dockerfile
or
Error response from daemon: Cannot locate specified Dockerfile: Dockerfile
Things I've checked:
The folder exists in build path
I tried both relative and absolute path
There are no softlinks in the path
I tried the same folder for binary and Dockerfile
docker build <path> works
and bunch of other stuff
My other option was to use RemoteContext which looks like it works, but only for fully self contained dockerfiles, and not the ones with "local file presence".
Update:
Tried passing tar as buffer, but got the same result with the following:
dockerBuildContext, err := os.Open("<path to>/docker-debug- container/docker-debug-container.tar")
defer dockerBuildContext.Close()
opt := types.ImageBuildOptions{
Context: dockerBuildContext,
CPUSetCPUs: "2",
CPUSetMems: "12",
CPUShares: 20,
CPUQuota: 10,
CPUPeriod: 30,
Memory: 256,
MemorySwap: 512,
ShmSize: 10,
CgroupParent: "cgroup_parent",
// Dockerfile: "Dockerfile",
}
_, err = cli.ImageBuild(context.Background(), nil, opt)
The following works for me;
package main
import (
"archive/tar"
"bytes"
"context"
"io"
"io/ioutil"
"log"
"os"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
)
func main() {
ctx := context.Background()
cli, err := client.NewEnvClient()
if err != nil {
log.Fatal(err, " :unable to init client")
}
buf := new(bytes.Buffer)
tw := tar.NewWriter(buf)
defer tw.Close()
dockerFile := "myDockerfile"
dockerFileReader, err := os.Open("/path/to/dockerfile")
if err != nil {
log.Fatal(err, " :unable to open Dockerfile")
}
readDockerFile, err := ioutil.ReadAll(dockerFileReader)
if err != nil {
log.Fatal(err, " :unable to read dockerfile")
}
tarHeader := &tar.Header{
Name: dockerFile,
Size: int64(len(readDockerFile)),
}
err = tw.WriteHeader(tarHeader)
if err != nil {
log.Fatal(err, " :unable to write tar header")
}
_, err = tw.Write(readDockerFile)
if err != nil {
log.Fatal(err, " :unable to write tar body")
}
dockerFileTarReader := bytes.NewReader(buf.Bytes())
imageBuildResponse, err := cli.ImageBuild(
ctx,
dockerFileTarReader,
types.ImageBuildOptions{
Context: dockerFileTarReader,
Dockerfile: dockerFile,
Remove: true})
if err != nil {
log.Fatal(err, " :unable to build docker image")
}
defer imageBuildResponse.Body.Close()
_, err = io.Copy(os.Stdout, imageBuildResponse.Body)
if err != nil {
log.Fatal(err, " :unable to read image build response")
}
}
#Mangirdas: staring at a screen long enough DOES help - at least in my case. I have been stuck with the same issue for some time now.
You were right to use the tar file (your second example). If you look at the API doc here https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#/build-image-from-a-dockerfile you can see that it expects a tar.
What really helped me was looking at other implementations of the client, perl and ruby in my case. Both create a tar on the fly when being asked to build an image from a directory.
Anyway, you only need to put your dockerBuildContext somewhere else (see the cli.ImageBuild())
dockerBuildContext, err := os.Open("/Path/to/your/docker/tarfile.tar")
defer dockerBuildContext.Close()
buildOptions := types.ImageBuildOptions{
Dockerfile: "Dockerfile", // optional, is the default
}
buildResponse, err := cli.ImageBuild(context.Background(), dockerBuildContext, buildOptions)
if err != nil {
log.Fatal(err)
}
defer buildResponse.Body.Close()
I am not there with naming the images properly yet, but at least I can create them...
Hope this helps.
Cheers
The Docker package has a function for creating a TAR from a file path. It's whats used by the CLI. It's not in the client package so it need to be installed separately:
import (
"github.com/mitchellh/go-homedir"
"github.com/docker/docker/pkg/archive"
)
func GetContext(filePath string) io.Reader {
// Use homedir.Expand to resolve paths like '~/repos/myrepo'
filePath, _ := homedir.Expand(filePath)
ctx, _ := archive.TarWithOptions(filePath, &archive.TarOptions{})
return ctx
}
cli.ImageBuild(context.Background(), GetContext("~/repos/myrepo"), types.ImageBuildOptions{...})
I agree with Marcus Havranek's answer, that method has worked for me. Just want to add how to add a name to an image, since that seemed like an open question:
buildOptions := types.ImageBuildOptions{
Tags: []string{"imagename"},
}
Hope this helps!
Combining a few of the answers, and adding how to correctly parse the returned JSON using DisplayJSONMessagesToStream.
package main
import (
"os"
"log"
"github.com/docker/docker/api/types"
"github.com/docker/docker/pkg/archive"
"github.com/docker/docker/pkg/jsonmessage"
"github.com/docker/docker/pkg/term"
"golang.org/x/net/context"
)
// Build a dockerfile if it exists
func Build(dockerFilePath, buildContextPath string, tags []string) {
ctx := context.Background()
cli := getCLI()
buildOpts := types.ImageBuildOptions{
Dockerfile: dockerFilePath,
Tags: tags,
}
buildCtx, _ := archive.TarWithOptions(buildContextPath, &archive.TarOptions{})
resp, err := cli.ImageBuild(ctx, buildCtx, buildOpts)
if err != nil {
log.Fatalf("build error - %s", err)
}
defer resp.Body.Close()
termFd, isTerm := term.GetFdInfo(os.Stderr)
jsonmessage.DisplayJSONMessagesStream(resp.Body, os.Stderr, termFd, isTerm, nil)
}
I've left our a few convenience functions like getCLI but I'm sure you have your own equivalents.
I encounter same problem. Finally find out the tar file should be docker build context even with Dockerfile.
Here is my code,
package main
import (
"log"
"os"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
"golang.org/x/net/context"
)
func main() {
dockerBuildContext, err := os.Open("/Users/elsvent/workspace/Go/src/test/test.tar")
defer dockerBuildContext.Close()
buildOptions := types.ImageBuildOptions{
SuppressOutput: true,
PullParent: true,
Tags: []string{"xxx"},
Dockerfile: "test/Dockerfile",
}
defaultHeaders := map[string]string{"Content-Type": "application/tar"}
cli, _ := client.NewClient("unix:///var/run/docker.sock", "v1.24", nil, defaultHeaders)
buildResp, err := cli.ImageBuild(context.Background(), dockerBuildContext, buildOptions)
if err != nil {
log.Fatal(err)
}
defer buildResp.Body.Close()
}
opt := types.ImageBuildOptions{
Dockerfile: "Dockerfile",
}
filePath, _ = homedir.Expand(".")
buildCtx, _ := archive.TarWithOptions(filePath, &archive.TarOptions{})
x, err := cli.ImageBuild(context.Background(), buildCtx, opt)
io.Copy(os.Stdout, x.Body)

How can I create a simple client app with the Kubernetes Go library?

I'm struggling with the Kubernetes Go library. The docs--at least the ones I found--appear out-of-date with the library itself. The example provided does not build because of issues with the imports. I'm just trying to do something simple: get a Service object by name and print some attributes (like nodePort). I just need a simple example of library usage to get me going.
I could easily do this using the RESTful API but that feels like re-inventing the wheel.
So after a little experimentation and a hint from the k8s Slack channel, I have this example. Perhaps someone can update the example with a proper import path.
package main
import (
"fmt"
"log"
"github.com/kubernetes/kubernetes/pkg/api"
client "github.com/kubernetes/kubernetes/pkg/client/unversioned"
)
func main() {
config := client.Config{
Host: "http://my-kube-api-server.me:8080",
}
c, err := client.New(&config)
if err != nil {
log.Fatalln("Can't connect to Kubernetes API:", err)
}
s, err := c.Services(api.NamespaceDefault).Get("some-service-name")
if err != nil {
log.Fatalln("Can't get service:", err)
}
fmt.Println("Name:", s.Name)
for p, _ := range s.Spec.Ports {
fmt.Println("Port:", s.Spec.Ports[p].Port)
fmt.Println("NodePort:", s.Spec.Ports[p].NodePort)
}
}
Here's how to do it with the latest Go client.
If you're inside the k8s cluster:
package main
import (
"fmt"
"k8s.io/client-go/1.5/kubernetes"
"k8s.io/client-go/1.5/pkg/api/v1"
"k8s.io/client-go/1.5/rest"
)
func main() {
config, err = rest.InClusterConfig()
if err != nil {
return nil, err
}
c, err := kubernetes.NewForConfig(config)
if err != nil {
return nil, err
}
// Get Pod by name
pod, err := c.Pods(v1.NamespaceDefault).Get("my-pod")
if err != nil {
fmt.Println(err)
return
}
// Print its creation time
fmt.Println(pod.GetCreationTimestamp())
}
And if you're outside of the cluster:
package main
import (
"fmt"
"k8s.io/client-go/1.5/kubernetes"
"k8s.io/client-go/1.5/pkg/api/v1"
"k8s.io/client-go/1.5/tools/clientcmd"
)
func main() {
config, err := clientcmd.BuildConfigFromFlags("", <kube-config-path>)
if err != nil {
return nil, err
}
c, err := kubernetes.NewForConfig(config)
if err != nil {
return nil, err
}
// Get Pod by name
pod, err := c.Pods(v1.NamespaceDefault).Get("my-pod")
if err != nil {
fmt.Println(err)
return
}
// Print its creation time
fmt.Println(pod.GetCreationTimestamp())
}
I have gone into more detail on this in a blog post.
With kubernetes go client, it could be done this way:
package main
import (
"flag"
"fmt"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/pkg/api/v1"
"k8s.io/client-go/tools/clientcmd"
)
var (
kubeconfig = flag.String("kubeconfig", "./config", "absolute path to the kubeconfig file")
)
func main() {
flag.Parse()
// uses the current context in kubeconfig
config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
if err != nil {
panic(err.Error())
}
// creates the clientset
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
panic(err.Error())
}
services, err := clientset.Core().Services("").List(v1.ListOptions{})
if err != nil {
panic(err.Error())
}
fmt.Printf("There are %d pods in the cluster\n", len(services.Items))
for _, s := range services.Items {
for p, _ := range s.Spec.Ports {
fmt.Println("Port:", s.Spec.Ports[p].Port)
fmt.Println("NodePort:", s.Spec.Ports[p].NodePort)
}
}
}

Resources