Setting labels on GCP Compute Instances using Golang - go

I was looking for ways to set or update instance Labels on GCP Compute Instance and labelFingerprint confused me.
then I figure it out and I'm putting the code in the answer section.

I used this simple code to add new labels to GCP instances.
package main
import (
"context"
"log"
"os"
"golang.org/x/oauth2/google"
"google.golang.org/api/compute/v1"
)
func main() {
addLabelToGCPInstances()
}
func addLabelToGCPInstances() error {
// You can pass these as args
project := "Your GCP Project ID"
zone := "europe-west2-a"
instance := "milad-test-instance"
prodLablesMap := map[string]string{
"production": "true",
"environment": "production",
}
ctx := context.Background()
os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "gke.json")
c, err := google.DefaultClient(ctx, compute.CloudPlatformScope)
if err != nil {
return err
}
computeService, err := compute.New(c)
if err != nil {
return err
}
respInstance, err := computeService.Instances.Get(project, zone, instance).Context(ctx).Do()
if err != nil {
log.Fatal(err)
}
rb := &compute.InstancesSetLabelsRequest{
Labels: prodLablesMap,
LabelFingerprint: respInstance.LabelFingerprint,
}
respLabels, err := computeService.Instances.SetLabels(project, zone, instance, rb).Context(ctx).Do()
if err != nil {
log.Fatal(err)
}
_ = respLabels
return err
}
This is just an example you can work around and do more error handling and etc.

Related

GCP - get project NAT GW's

We have account on GCP which contain valid cloud Nat, now we want to get those values via
GCP sdk, I've tried the following and get empty response (maybe I use the wrong API and it not ListExternalVpnGatewaysRequest)
package main
import (
"context"
"fmt"
compute "cloud.google.com/go/compute/apiv1"
"google.golang.org/api/iterator"
computepb "google.golang.org/genproto/googleapis/cloud/compute/v1"
)
func main() {
ctx := context.Background()
c, err := compute.NewExternalVpnGatewaysRESTClient(ctx)
if err != nil {
fmt.Println(err)
}
defer c.Close()
proj := "dev-proj"
req := &computepb.ListExternalVpnGatewaysRequest{
//Filter: new(string),
//MaxResults: new(uint32),
//OrderBy: new(string),
//PageToken: new(string),
Project: proj,
//ReturnPartialSuccess: new(bool),
}
it := c.List(ctx, req)
for {
resp, err := it.Next()
if err == iterator.Done {
break
}
if err != nil {
fmt.Println(err)
}
// TODO: Use resp.
_ = resp
fmt.Println(resp)
}
}
I need to get the following values using GCP GO SDK
update
I tried the following as-is and I got error
package main
import (
"context"
"fmt"
"google.golang.org/api/compute/v1"
"log"
)
func main() {
project := "my-proj"
region := "my-region"
ctx := context.Background()
computeService, err := compute.New(ctx)
if err != nil {
log.Fatal(err)
}
req := computeService.Routers.List(project, region)
if err := req.Pages(ctx, func(page *compute.RouterList) error {
for _, router := range page.Items {
// process each `router` resource:
fmt.Printf("%#v\n", router)
// NAT Gateways are found in router.nats
}
return nil
}); err != nil {
log.Fatal(err)
}
}
Error is: ./main.go:16:36: cannot use ctx (type context.Context) as type *http.Client in argument to compute.New
A VPN Gateway is not the same as a NAT Gateway.
Use this code to list routers. Within the list of routers, is the NAT Gateways
import "google.golang.org/api/compute/v1"
// Replace with valid values for your project
project := "my-project"
region := "my-region"
ctx := context.Background()
c, err := google.DefaultClient(ctx, compute.CloudPlatformScope)
if err != nil {
log.Fatal(err)
}
computeService, err := compute.New(c)
if err != nil {
log.Fatal(err)
}
req := computeService.Routers.List(project, region)
if err := req.Pages(ctx, func(page *compute.RouterList) error {
for _, router := range page.Items {
// process each `router` resource:
fmt.Printf("%#v\n", router)
// NAT Gateways are found in router.nats
}
return nil
}); err != nil {
log.Fatal(err)
}
SDK Documentation

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)
}
}
}

How to achieve automatic authentication using GCE Go client oauth2 authentication

This code is on the basis of golang.org/x/oauth2 example test. I am trying to get instance information from Google Compute Engine using Go client. Do I have to use oauth2 authentication? There is a generated link after Visit the URL for the auth dialog:
https://accounts.google.com/o/oauth2/auth?access_type=offline&client_id=xxx&redirect_uri=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcompute&response_type=code&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcompute&state=state
and it redirect to https://www.googleapis.com/auth/compute which shows a 'compute'.
How do I achieve automatic authentication?
package main
import (
"context"
"fmt"
"log"
"golang.org/x/oauth2"
"google.golang.org/api/compute/v1"
)
type GCE struct {
*compute.Service
}
var ctx = context.Background()
func initGCE() *GCE {
conf := &oauth2.Config{
ClientID: "xxx",
ClientSecret: "xxx",
Scopes: []string{compute.ComputeScope},
Endpoint: oauth2.Endpoint{
AuthURL: "https://accounts.google.com/o/oauth2/auth",
TokenURL: "https://accounts.google.com/o/oauth2/auth",
},
RedirectURL: "https://www.googleapis.com/auth/compute",
}
url := conf.AuthCodeURL("state", oauth2.AccessTypeOffline)
fmt.Printf("Visit the URL for the auth dialog: %v", url)
var code string
if _, err := fmt.Scan(&code); err != nil {
log.Fatal(err)
}
tok, err := conf.Exchange(ctx, code)
if err != nil {
log.Fatal(err)
}
service, err := compute.New(conf.Client(ctx, tok))
if err != nil {
log.Fatal(err)
}
return &GCE{service}
}
func (g *GCE) Instance() {
project := "arctic-cyclist-189707"
zone := "us-east1-b"
instance := "centos7"
resp, err := g.Instances.Get(project, zone, instance).Context(ctx).Do()
if err != nil {
fmt.Println(err)
return
} else {
fmt.Printf("%#v\n", resp)
}
}
Solved by using code example
https://cloud.google.com/compute/docs/reference/latest/instances/get#examples
just set "GOOGLE_APPLICATION_CREDENTIALS" environment varibles as google.DefaultClient() requires.
package main
import (
"context"
"fmt"
"log"
"golang.org/x/oauth2/google"
"google.golang.org/api/compute/v1"
)
type GCE struct {
*compute.Service
}
var ctx = context.Background()
func initGCE() *GCE {
c, err := google.DefaultClient(ctx, compute.CloudPlatformScope)
if err != nil {
log.Fatal(err)
}
computeService, err := compute.New(c)
if err != nil {
log.Fatal(err)
}
return &GCE{computeService}
}
func (g *GCE) Instance(project, zone, instance string) {
resp, err := g.Instances.Get(project, zone, instance).Context(ctx).Do()
if err != nil {
log.Fatal(err)
}
fmt.Printf("%#v\n", resp)
}
Thanks for your reply.

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)
}
}
}

Go to check EC2 environment? Or DNS domain name?

I want to see if the program is being run in EC2 or not.
One way is to run hostname -d in EC2 to get the DNS domain name.
How do I get this DNS domain name in Go.
I looked at the net package using http://golang.org/pkg/net/#LookupNS
But I need to pass an argument.
How do I check if it's in EC2 or not?
Thanks
You can see if there is an interface with a specific name with this function:
package main
import (
"log"
"net"
"strings"
)
func trace(fmt string, args ...interface{}) {
log.Printf(fmt, args...)
}
func HasAddrWithName(name string) (bool, error) {
ifaces, err := net.Interfaces()
if err != nil {
return false, err
}
for _, iface := range ifaces {
addrs, err := iface.Addrs()
if err != nil {
trace("%s", err)
continue
}
for _, addr := range addrs {
ipaddr, _, err := net.ParseCIDR(addr.String())
if err != nil {
trace("%s", err)
continue
}
hosts, err := net.LookupAddr(ipaddr.String())
if err != nil {
trace("%s", err)
continue
}
for idx, h := range hosts {
trace("%d: %s\n", idx, h)
if strings.Contains(h, name) {
return true, nil
}
}
}
}
return false, nil
}
func main() {
hasAddr, err := HasAddrWithName(".ec2.internal")
if err != nil {
log.Fatal(err)
}
if hasAddr {
log.Println("inside ec2")
return
}
log.Println("Not inside ec2")
}
The function will try to find all the interface an resolve the ip to a dns name. if the name contains the specific string returns true.
The right way, IMO, is to try and hit the metadata API at http://169.254.169.254/latest/meta-data from the machine itself. The worrisome part is that you feel the need to know this in code. I am not quite sure what the use case for this is but it seems to me that there ought to be a way for you to know this outside of your code.
Nevertheless:
package main
import (
"fmt"
"io/ioutil"
"net/http"
"os"
"time"
)
func main() {
/* if you just need the hostname */
name, _ := os.Hostname()
fmt.Println(name)
/* if you must hit the EC2 metadata API */
client := http.Client{
Timeout: time.Duration(2 * time.Second),
}
resp, err := client.Get("http://169.254.169.254/latest/meta-data/public-hostname")
if err != nil {
fmt.Println("Probably not on EC2")
fmt.Println(err)
return
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
}

Resources