156 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			156 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package client
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"crypto/tls"
 | 
						|
	"errors"
 | 
						|
	"io/ioutil"
 | 
						|
	"net/http"
 | 
						|
	"os"
 | 
						|
	"path"
 | 
						|
 | 
						|
	"github.com/micro/go-micro/runtime/kubernetes/client/api"
 | 
						|
	"github.com/micro/go-micro/util/log"
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	// path to kubernetes service account token
 | 
						|
	serviceAccountPath = "/var/run/secrets/kubernetes.io/serviceaccount"
 | 
						|
	// ErrReadNamespace is returned when the names could not be read from service account
 | 
						|
	ErrReadNamespace = errors.New("Could not read namespace from service account secret")
 | 
						|
)
 | 
						|
 | 
						|
// Client ...
 | 
						|
type client struct {
 | 
						|
	opts *api.Options
 | 
						|
}
 | 
						|
 | 
						|
// NewClientInCluster creates a Kubernetes client for use from within a k8s pod.
 | 
						|
func NewClientInCluster() *client {
 | 
						|
	host := "https://" + os.Getenv("KUBERNETES_SERVICE_HOST") + ":" + os.Getenv("KUBERNETES_SERVICE_PORT")
 | 
						|
 | 
						|
	s, err := os.Stat(serviceAccountPath)
 | 
						|
	if err != nil {
 | 
						|
		log.Fatal(err)
 | 
						|
	}
 | 
						|
	if s == nil || !s.IsDir() {
 | 
						|
		log.Fatal(errors.New("service account not found"))
 | 
						|
	}
 | 
						|
 | 
						|
	token, err := ioutil.ReadFile(path.Join(serviceAccountPath, "token"))
 | 
						|
	if err != nil {
 | 
						|
		log.Fatal(err)
 | 
						|
	}
 | 
						|
	t := string(token)
 | 
						|
 | 
						|
	ns, err := detectNamespace()
 | 
						|
	if err != nil {
 | 
						|
		log.Fatal(err)
 | 
						|
	}
 | 
						|
 | 
						|
	crt, err := CertPoolFromFile(path.Join(serviceAccountPath, "ca.crt"))
 | 
						|
	if err != nil {
 | 
						|
		log.Fatal(err)
 | 
						|
	}
 | 
						|
 | 
						|
	c := &http.Client{
 | 
						|
		Transport: &http.Transport{
 | 
						|
			TLSClientConfig: &tls.Config{
 | 
						|
				RootCAs: crt,
 | 
						|
			},
 | 
						|
			DisableCompression: true,
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	return &client{
 | 
						|
		opts: &api.Options{
 | 
						|
			Client:      c,
 | 
						|
			Host:        host,
 | 
						|
			Namespace:   ns,
 | 
						|
			BearerToken: &t,
 | 
						|
		},
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func detectNamespace() (string, error) {
 | 
						|
	nsPath := path.Join(serviceAccountPath, "namespace")
 | 
						|
 | 
						|
	// Make sure it's a file and we can read it
 | 
						|
	if s, e := os.Stat(nsPath); e != nil {
 | 
						|
		return "", e
 | 
						|
	} else if s.IsDir() {
 | 
						|
		return "", ErrReadNamespace
 | 
						|
	}
 | 
						|
 | 
						|
	// Read the file, and cast to a string
 | 
						|
	if ns, e := ioutil.ReadFile(nsPath); e != nil {
 | 
						|
		return string(ns), e
 | 
						|
	} else {
 | 
						|
		return string(ns), nil
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Create creates new API object
 | 
						|
func (c *client) Create(r *Resource) error {
 | 
						|
	b := new(bytes.Buffer)
 | 
						|
	if err := renderTemplate(r.Kind, b, r.Value); err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	return api.NewRequest(c.opts).
 | 
						|
		Post().
 | 
						|
		SetHeader("Content-Type", "application/yaml").
 | 
						|
		Resource(r.Kind).
 | 
						|
		Body(b).
 | 
						|
		Do().
 | 
						|
		Error()
 | 
						|
}
 | 
						|
 | 
						|
// Get queries API objects and stores the result in r
 | 
						|
func (c *client) Get(r *Resource, labels map[string]string) error {
 | 
						|
	return api.NewRequest(c.opts).
 | 
						|
		Get().
 | 
						|
		Resource(r.Kind).
 | 
						|
		Params(&api.Params{LabelSelector: labels}).
 | 
						|
		Do().
 | 
						|
		Into(r.Value)
 | 
						|
}
 | 
						|
 | 
						|
// Update updates API object
 | 
						|
func (c *client) Update(r *Resource) error {
 | 
						|
	req := api.NewRequest(c.opts).
 | 
						|
		Patch().
 | 
						|
		SetHeader("Content-Type", "application/strategic-merge-patch+json").
 | 
						|
		Resource(r.Kind).
 | 
						|
		Name(r.Name)
 | 
						|
 | 
						|
	switch r.Kind {
 | 
						|
	case "service":
 | 
						|
		req.Body(r.Value.(*Service))
 | 
						|
	case "deployment":
 | 
						|
		req.Body(r.Value.(*Deployment))
 | 
						|
	default:
 | 
						|
		return errors.New("unsupported resource")
 | 
						|
	}
 | 
						|
 | 
						|
	return req.Do().Error()
 | 
						|
}
 | 
						|
 | 
						|
// Delete removes API object
 | 
						|
func (c *client) Delete(r *Resource) error {
 | 
						|
	return api.NewRequest(c.opts).
 | 
						|
		Delete().
 | 
						|
		Resource(r.Kind).
 | 
						|
		Name(r.Name).
 | 
						|
		Do().
 | 
						|
		Error()
 | 
						|
}
 | 
						|
 | 
						|
// List lists API objects and stores the result in r
 | 
						|
func (c *client) List(r *Resource) error {
 | 
						|
	labels := map[string]string{
 | 
						|
		"micro": "service",
 | 
						|
	}
 | 
						|
	return c.Get(r, labels)
 | 
						|
}
 |