[WIP] Micro Runtime (#947)

* Add Get() and GetOptions.

* Removed watcher. Outline of client. YAML templates

* Added default service and deployment templates and types

* Added API tests and cleaned up errors.

* Small refactoring. Template package is no more.

* Ripped out existing code in preparation to small rework

* Reshuffled the source code to make it organized better

* Create service and deployment in kubernetes runtime

* Major cleanup and refactoring of Kubernetes runtime

* Service now handles low level K8s API calls across both K8s deployment
an service API objects
* Runtime has a task queue that serves for queueing runtime action
requests
* General refactoring

* No need for Lock in k8s service

* Added kubernetes runtime env var to default deployment

* Enable running different versions of the same service

* Can't delete services through labels

* Proto cruft. Added runtime.CreateOptions implementation in proto

* Removed proxy service from default env variables

* Make service name mandatory param to Get method

* Get Delete changes from https://github.com/micro/go-micro/pull/945

* Replaced template files with global variables

* Validate service names before sending K8s API request

* Refactored Kubernetes API client. Fixed typos.

* Added client.Resource to make API resources more explicit in code
This commit is contained in:
Milos Gajdos 2019-11-15 13:41:40 +00:00 committed by Asim Aslam
parent 0af8be35bb
commit 97c1300f53
23 changed files with 1284 additions and 646 deletions

2
go.mod
View File

@ -32,9 +32,11 @@ require (
github.com/mholt/certmagic v0.8.3 github.com/mholt/certmagic v0.8.3
github.com/micro/cli v0.2.0 github.com/micro/cli v0.2.0
github.com/micro/mdns v0.3.0 github.com/micro/mdns v0.3.0
github.com/miekg/dns v1.1.15
github.com/mitchellh/hashstructure v1.0.0 github.com/mitchellh/hashstructure v1.0.0
github.com/nats-io/nats.go v1.9.1 github.com/nats-io/nats.go v1.9.1
github.com/nlopes/slack v0.6.0 github.com/nlopes/slack v0.6.0
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c
github.com/pkg/errors v0.8.1 github.com/pkg/errors v0.8.1
github.com/technoweenie/multipartstreamer v1.0.1 // indirect github.com/technoweenie/multipartstreamer v1.0.1 // indirect
go.uber.org/zap v1.12.0 // indirect go.uber.org/zap v1.12.0 // indirect

2
go.sum
View File

@ -283,6 +283,8 @@ github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mo
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/oracle/oci-go-sdk v7.0.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= github.com/oracle/oci-go-sdk v7.0.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=
github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014/go.mod h1:joRatxRJaZBsY3JAOEMcoOp05CnZzsx4scTxi95DHyQ= github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014/go.mod h1:joRatxRJaZBsY3JAOEMcoOp05CnZzsx4scTxi95DHyQ=
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=

View File

@ -21,6 +21,7 @@ type runtime struct {
// indicates if we're running // indicates if we're running
running bool running bool
// the service map // the service map
// TODO: track different versions of the same service
services map[string]*service services map[string]*service
} }
@ -175,6 +176,56 @@ func (r *runtime) Create(s *Service, opts ...CreateOption) error {
return nil return nil
} }
// Get returns all instances of requested service
// If no service name is provided we return all the track services.
func (r *runtime) Get(name string, opts ...GetOption) ([]*Service, error) {
r.Lock()
defer r.Unlock()
if len(name) == 0 {
return nil, errors.New("missing service name")
}
gopts := GetOptions{}
for _, o := range opts {
o(&gopts)
}
var services []*Service
// if we track the service check if the version is provided
if s, ok := r.services[name]; ok {
if len(gopts.Version) > 0 {
if s.Version == gopts.Version {
services = append(services, s.Service)
}
return services, nil
}
// no version has sbeen requested, just append the service
services = append(services, s.Service)
}
return services, nil
}
// Update attemps to update the service
func (r *runtime) Update(s *Service) error {
var opts []CreateOption
// check if the service already exists
r.RLock()
if service, ok := r.services[s.Name]; ok {
opts = append(opts, WithOutput(service.output))
}
r.RUnlock()
// delete the service
if err := r.Delete(s); err != nil {
return err
}
// create new service
return r.Create(s, opts...)
}
// Delete removes the service from the runtime and stops it // Delete removes the service from the runtime and stops it
func (r *runtime) Delete(s *Service) error { func (r *runtime) Delete(s *Service) error {
r.Lock() r.Lock()
@ -199,26 +250,6 @@ func (r *runtime) Delete(s *Service) error {
return nil return nil
} }
// Update attemps to update the service
func (r *runtime) Update(s *Service) error {
var opts []CreateOption
// check if the service already exists
r.RLock()
if service, ok := r.services[s.Name]; ok {
opts = append(opts, WithOutput(service.output))
}
r.RUnlock()
// delete the service
if err := r.Delete(s); err != nil {
return err
}
// create new service
return r.Create(s, opts...)
}
// List returns a slice of all services tracked by the runtime // List returns a slice of all services tracked by the runtime
func (r *runtime) List() ([]*Service, error) { func (r *runtime) List() ([]*Service, error) {
var services []*Service var services []*Service

View File

@ -0,0 +1,169 @@
package api
import (
"encoding/json"
"net/http"
"net/http/httptest"
"reflect"
"testing"
)
type testcase struct {
Token string
ReqFn func(opts *Options) *Request
Method string
URI string
Body interface{}
Header map[string]string
Assert func(req *http.Request) bool
}
type assertFn func(req *http.Request) bool
var tests = []testcase{
testcase{
ReqFn: func(opts *Options) *Request {
return NewRequest(opts).Get().Resource("service")
},
Method: "GET",
URI: "/api/v1/namespaces/default/services/",
},
testcase{
ReqFn: func(opts *Options) *Request {
return NewRequest(opts).Get().Resource("service").Name("foo")
},
Method: "GET",
URI: "/api/v1/namespaces/default/services/foo",
},
testcase{
ReqFn: func(opts *Options) *Request {
return NewRequest(opts).Get().Resource("service").Namespace("test").Name("bar")
},
Method: "GET",
URI: "/api/v1/namespaces/test/services/bar",
},
testcase{
ReqFn: func(opts *Options) *Request {
return NewRequest(opts).Get().Resource("deployment").Name("foo")
},
Method: "GET",
URI: "/apis/apps/v1/namespaces/default/deployments/foo",
},
testcase{
ReqFn: func(opts *Options) *Request {
return NewRequest(opts).Get().Resource("deployment").Namespace("test").Name("foo")
},
Method: "GET",
URI: "/apis/apps/v1/namespaces/test/deployments/foo",
},
testcase{
ReqFn: func(opts *Options) *Request {
return NewRequest(opts).Get().Resource("pod").Params(&Params{LabelSelector: map[string]string{"foo": "bar"}})
},
Method: "GET",
URI: "/api/v1/namespaces/default/pods/?labelSelector=foo%3Dbar",
},
testcase{
ReqFn: func(opts *Options) *Request {
return NewRequest(opts).Post().Resource("service").Name("foo").Body(map[string]string{"foo": "bar"})
},
Method: "POST",
URI: "/api/v1/namespaces/default/services/foo",
Body: map[string]string{"foo": "bar"},
},
testcase{
ReqFn: func(opts *Options) *Request {
return NewRequest(opts).Post().Resource("deployment").Namespace("test").Name("foo").Body(map[string]string{"foo": "bar"})
},
Method: "POST",
URI: "/apis/apps/v1/namespaces/test/deployments/foo",
Body: map[string]string{"foo": "bar"},
},
testcase{
ReqFn: func(opts *Options) *Request {
return NewRequest(opts).Put().Resource("endpoint").Name("baz").Body(map[string]string{"bam": "bar"})
},
Method: "PUT",
URI: "/api/v1/namespaces/default/endpoints/baz",
Body: map[string]string{"bam": "bar"},
},
testcase{
ReqFn: func(opts *Options) *Request {
return NewRequest(opts).Patch().Resource("endpoint").Name("baz").Body(map[string]string{"bam": "bar"})
},
Method: "PATCH",
URI: "/api/v1/namespaces/default/endpoints/baz",
Body: map[string]string{"bam": "bar"},
},
testcase{
ReqFn: func(opts *Options) *Request {
return NewRequest(opts).Patch().Resource("endpoint").Name("baz").SetHeader("foo", "bar")
},
Method: "PATCH",
URI: "/api/v1/namespaces/default/endpoints/baz",
Header: map[string]string{"foo": "bar"},
},
testcase{
ReqFn: func(opts *Options) *Request {
return NewRequest(opts).Patch().Resource("deployment").Name("baz").SetHeader("foo", "bar")
},
Method: "PATCH",
URI: "/apis/apps/v1/namespaces/default/deployments/baz",
Header: map[string]string{"foo": "bar"},
},
}
var wrappedHandler = func(test *testcase, t *testing.T) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
auth := r.Header.Get("Authorization")
if len(test.Token) > 0 && (len(auth) == 0 || auth != "Bearer "+test.Token) {
t.Errorf("test case token (%s) did not match expected token (%s)", "Bearer "+test.Token, auth)
}
if len(test.Method) > 0 && test.Method != r.Method {
t.Errorf("test case Method (%s) did not match expected Method (%s)", test.Method, r.Method)
}
if len(test.URI) > 0 && test.URI != r.URL.RequestURI() {
t.Errorf("test case URI (%s) did not match expected URI (%s)", test.URI, r.URL.RequestURI())
}
if test.Body != nil {
var res map[string]string
decoder := json.NewDecoder(r.Body)
if err := decoder.Decode(&res); err != nil {
t.Errorf("decoding body failed: %v", err)
}
if !reflect.DeepEqual(res, test.Body) {
t.Error("body did not match")
}
}
if test.Header != nil {
for k, v := range test.Header {
if r.Header.Get(k) != v {
t.Error("header did not exist")
}
}
}
w.WriteHeader(http.StatusOK)
})
}
func TestRequest(t *testing.T) {
for _, test := range tests {
ts := httptest.NewServer(wrappedHandler(&test, t))
req := test.ReqFn(&Options{
Host: ts.URL,
Client: &http.Client{},
BearerToken: &test.Token,
Namespace: "default",
})
res := req.Do()
if res.Error() != nil {
t.Errorf("request failed with %v", res.Error())
}
ts.Close()
}
}

View File

@ -3,12 +3,12 @@ package api
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"net/url" "net/url"
"github.com/micro/go-micro/runtime/kubernetes/client/watch"
"github.com/micro/go-micro/util/log" "github.com/micro/go-micro/util/log"
) )
@ -33,7 +33,6 @@ type Request struct {
type Params struct { type Params struct {
LabelSelector map[string]string LabelSelector map[string]string
Annotations map[string]string Annotations map[string]string
Watch bool
} }
// verb sets method // verb sets method
@ -58,9 +57,8 @@ func (r *Request) Put() *Request {
} }
// Patch request // Patch request
// https://github.com/kubernetes/kubernetes/blob/master/docs/devel/api-conventions.md#patch-operations
func (r *Request) Patch() *Request { func (r *Request) Patch() *Request {
return r.verb("PATCH").SetHeader("Content-Type", "application/strategic-merge-patch+json") return r.verb("PATCH")
} }
// Delete request // Delete request
@ -87,15 +85,33 @@ func (r *Request) Name(s string) *Request {
return r return r
} }
// Body pass in a body to set, this is for POST, PUT // Body pass in a body to set, this is for POST, PUT and PATCH requests
// and PATCH requests
func (r *Request) Body(in interface{}) *Request { func (r *Request) Body(in interface{}) *Request {
b := new(bytes.Buffer) b := new(bytes.Buffer)
// if we're not sending YAML request, we encode to JSON
if r.header.Get("Content-Type") != "application/yaml" {
if err := json.NewEncoder(b).Encode(&in); err != nil { if err := json.NewEncoder(b).Encode(&in); err != nil {
r.err = err r.err = err
return r return r
} }
log.Debugf("Patch body: %v", b) log.Debugf("Request body: %v", b)
r.body = b
return r
}
// if application/yaml is set, we assume we get a raw bytes so we just copy over
body, ok := in.(io.Reader)
if !ok {
r.err = errors.New("invalid data")
return r
}
// copy over data to the bytes buffer
if _, err := io.Copy(b, body); err != nil {
r.err = err
return r
}
log.Debugf("Request body: %v", b)
r.body = b r.body = b
return r return r
} }
@ -120,12 +136,12 @@ func (r *Request) SetHeader(key, value string) *Request {
func (r *Request) request() (*http.Request, error) { func (r *Request) request() (*http.Request, error) {
var url string var url string
switch r.resource { switch r.resource {
case "pods": case "pod", "service", "endpoint":
// /api/v1/namespaces/{namespace}/pods // /api/v1/namespaces/{namespace}/pods
url = fmt.Sprintf("%s/api/v1/namespaces/%s/%s/", r.host, r.namespace, r.resource) url = fmt.Sprintf("%s/api/v1/namespaces/%s/%ss/", r.host, r.namespace, r.resource)
case "deployments": case "deployment":
// /apis/apps/v1/namespaces/{namespace}/deployments/{name} // /apis/apps/v1/namespaces/{namespace}/deployments/{name}
url = fmt.Sprintf("%s/apis/apps/v1/namespaces/%s/%s/", r.host, r.namespace, r.resource) url = fmt.Sprintf("%s/apis/apps/v1/namespaces/%s/%ss/", r.host, r.namespace, r.resource)
} }
// append resourceName if it is present // append resourceName if it is present
@ -179,24 +195,6 @@ func (r *Request) Do() *Response {
return newResponse(res, err) return newResponse(res, err)
} }
// Watch builds and triggers the request, but
// will watch instead of return an object
func (r *Request) Watch() (watch.Watch, error) {
if r.err != nil {
return nil, r.err
}
r.params.Set("watch", "true")
req, err := r.request()
if err != nil {
return nil, err
}
w, err := watch.NewBodyWatcher(req, r.client)
return w, err
}
// Options ... // Options ...
type Options struct { type Options struct {
Host string Host string

View File

@ -11,9 +11,9 @@ import (
// Errors ... // Errors ...
var ( var (
ErrNotFound = errors.New("kubernetes: not found") ErrNotFound = errors.New("kubernetes: resource not found")
ErrDecode = errors.New("kubernetes: error decoding") ErrDecode = errors.New("kubernetes: error decoding")
ErrOther = errors.New("kubernetes: unknown error") ErrUnknown = errors.New("kubernetes: unknown error")
) )
// Status is an object that is returned when a request // Status is an object that is returned when a request
@ -89,6 +89,7 @@ func newResponse(res *http.Response, err error) *Response {
log.Log("kubernetes: request failed with body:") log.Log("kubernetes: request failed with body:")
log.Log(string(b)) log.Log(string(b))
} }
r.err = ErrOther r.err = ErrUnknown
return r return r
} }

View File

@ -1,6 +1,7 @@
package client package client
import ( import (
"bytes"
"crypto/tls" "crypto/tls"
"errors" "errors"
"io/ioutil" "io/ioutil"
@ -13,6 +14,7 @@ import (
) )
var ( var (
// path to kubernetes service account token
serviceAccountPath = "/var/run/secrets/kubernetes.io/serviceaccount" serviceAccountPath = "/var/run/secrets/kubernetes.io/serviceaccount"
// ErrReadNamespace is returned when the names could not be read from service account // ErrReadNamespace is returned when the names could not be read from service account
ErrReadNamespace = errors.New("Could not read namespace from service account secret") ErrReadNamespace = errors.New("Could not read namespace from service account secret")
@ -23,9 +25,7 @@ type client struct {
opts *api.Options opts *api.Options
} }
// NewClientInCluster should work similarily to the official api // NewClientInCluster creates a Kubernetes client for use from within a k8s pod.
// NewInClient by setting up a client configuration for use within
// a k8s pod.
func NewClientInCluster() *client { func NewClientInCluster() *client {
host := "https://" + os.Getenv("KUBERNETES_SERVICE_HOST") + ":" + os.Getenv("KUBERNETES_SERVICE_PORT") host := "https://" + os.Getenv("KUBERNETES_SERVICE_HOST") + ":" + os.Getenv("KUBERNETES_SERVICE_PORT")
@ -34,7 +34,7 @@ func NewClientInCluster() *client {
log.Fatal(err) log.Fatal(err)
} }
if s == nil || !s.IsDir() { if s == nil || !s.IsDir() {
log.Fatal(errors.New("no k8s service account found")) log.Fatal(errors.New("service account not found"))
} }
token, err := ioutil.ReadFile(path.Join(serviceAccountPath, "token")) token, err := ioutil.ReadFile(path.Join(serviceAccountPath, "token"))
@ -90,26 +90,72 @@ func detectNamespace() (string, error) {
} }
} }
// UpdateDeployment patches kubernetes deployment with metadata provided in body // Create creates new API object
func (c *client) UpdateDeployment(name string, body interface{}) error { 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). return api.NewRequest(c.opts).
Patch(). Post().
Resource("deployments"). SetHeader("Content-Type", "application/yaml").
Name(name). Resource(r.Kind).
Body(body). Body(b).
Do(). Do().
Error() Error()
} }
// ListDeployments lists all kubernetes deployments with given labels // Get queries API objects and stores the result in r
func (c *client) ListDeployments(labels map[string]string) (*DeploymentList, error) { func (c *client) Get(r *Resource, labels map[string]string) error {
var deployments DeploymentList return api.NewRequest(c.opts).
err := api.NewRequest(c.opts).
Get(). Get().
Resource("deployments"). Resource(r.Kind).
Params(&api.Params{LabelSelector: labels}). Params(&api.Params{LabelSelector: labels}).
Do(). Do().
Into(&deployments) Into(r.Value)
}
return &deployments, err
// 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).Spec)
case "deployment":
req.Body(r.Value.(*Deployment).Spec)
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 api.NewRequest(c.opts).
Get().
Resource(r.Kind).
Params(&api.Params{LabelSelector: labels}).
Do().
Into(r.Value)
} }

View File

@ -1,43 +1,132 @@
// Package client provides an implementation of a restricted subset of kubernetes API client
package client package client
import (
"regexp"
"strconv"
"strings"
"time"
"github.com/micro/go-micro/util/log"
)
const (
// https://github.com/kubernetes/apimachinery/blob/master/pkg/util/validation/validation.go#L134
dns1123LabelFmt string = "[a-z0-9]([-a-z0-9]*[a-z0-9])?"
)
var (
// DefaultImage is default micro image
DefaultImage = "micro/micro"
// ServiceRegexp is used to validate service name
ServiceRegexp = regexp.MustCompile("^" + dns1123LabelFmt + "$")
)
// Kubernetes client // Kubernetes client
type Kubernetes interface { type Kubernetes interface {
// UpdateDeployment patches deployment annotations with new metadata // Create creates new API resource
UpdateDeployment(string, interface{}) error Create(*Resource) error
// ListDeployments lists all micro deployments // Get queries API resrouces
ListDeployments(labels map[string]string) (*DeploymentList, error) Get(*Resource, map[string]string) error
// Update patches existing API object
Update(*Resource) error
// Delete deletes API resource
Delete(*Resource) error
// List lists API resources
List(*Resource) error
} }
// Template is micro deployment template // DefaultService returns default micro kubernetes service definition
type Template struct { func DefaultService(name, version string) *Service {
Metadata *Metadata `json:"metadata,omitempty"` Labels := map[string]string{
"name": name,
"version": version,
"micro": "service",
}
svcName := name
if len(version) > 0 {
// API service object name joins name and version over "-"
svcName = strings.Join([]string{name, version}, "-")
}
Metadata := &Metadata{
Name: svcName,
Namespace: "default",
Version: version,
Labels: Labels,
}
Spec := &ServiceSpec{
Type: "ClusterIP",
Selector: Labels,
Ports: []ServicePort{{
name + "-port", 9090, "",
}},
}
return &Service{
Metadata: Metadata,
Spec: Spec,
}
} }
// Spec defines micro deployment spec // DefaultService returns default micro kubernetes deployment definition
type Spec struct { func DefaultDeployment(name, version string) *Deployment {
Template *Template `json:"template,omitempty"` Labels := map[string]string{
} "name": name,
"version": version,
"micro": "service",
}
// Metadata defines api request metadata // API deployment object name joins name and version over "="
type Metadata struct { depName := strings.Join([]string{name, version}, "-")
Name string `json:"name,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
Annotations map[string]string `json:"annotations,omitempty"`
}
// DeploymentList Metadata := &Metadata{
type DeploymentList struct { Name: depName,
Items []Deployment `json:"items"` Namespace: "default",
} Version: version,
Labels: Labels,
}
// Deployment is Kubernetes deployment // TODO: we need to figure out this version stuff
type Deployment struct { // might be worth adding Build to runtime.Service
Metadata *Metadata `json:"metadata"` buildTime, err := strconv.ParseInt(version, 10, 64)
Status *Status `json:"status"` if err == nil {
} buildUnixTimeUTC := time.Unix(buildTime, 0)
Metadata.Annotations = map[string]string{
"build": buildUnixTimeUTC.Format(time.RFC3339),
}
} else {
log.Debugf("Runtime could not parse build: %v", err)
}
// Status is Kubernetes deployment status // TODO: change the image name here
type Status struct { Spec := &DeploymentSpec{
Replicas int `json:"replicas"` Replicas: 1,
AvailableReplicas int `json:"availablereplicas"` Selector: &LabelSelector{
MatchLabels: Labels,
},
Template: &Template{
Metadata: Metadata,
PodSpec: &PodSpec{
Containers: []Container{{
Name: name,
Image: DefaultImage,
Env: []EnvVar{},
Command: []string{"go", "run", "main.go"},
Ports: []ContainerPort{{
Name: name + "-port",
ContainerPort: 8080,
}},
}},
},
},
}
return &Deployment{
Metadata: Metadata,
Spec: Spec,
}
} }

View File

@ -0,0 +1,93 @@
package client
var templates = map[string]string{
"deployments": deploymentTmpl,
"services": serviceTmpl,
}
var deploymentTmpl = `
apiVersion: apps/v1
kind: Deployment
metadata:
name: "{{ .Metadata.Name }}"
namespace: "{{ .Metadata.Namespace }}"
labels:
{{- with .Metadata.Labels }}
{{- range $key, $value := . }}
{{ $key }}: "{{ $value }}"
{{- end }}
{{- end }}
spec:
replicas: {{ .Spec.Replicas }}
selector:
matchLabels:
{{- with .Spec.Selector.MatchLabels }}
{{- range $key, $value := . }}
{{ $key }}: "{{ $value }}"
{{- end }}
{{- end }}
template:
metadata:
labels:
{{- with .Spec.Template.Metadata.Labels }}
{{- range $key, $value := . }}
{{ $key }}: "{{ $value }}"
{{- end }}
{{- end }}
spec:
containers:
{{- with .Spec.Template.PodSpec.Containers }}
{{- range . }}
- name: {{ .Name }}
env:
{{- with .Env }}
{{- range . }}
- name: "{{ .Name }}"
value: "{{ .Value }}"
{{- end }}
{{- end }}
command:
{{- range .Command }}
- {{.}}
{{- end }}
image: {{ .Image }}
imagePullPolicy: Always
ports:
{{- with .Ports }}
{{- range . }}
- containerPort: {{ .ContainerPort }}
name: {{ .Name }}
{{- end}}
{{- end}}
{{- end }}
{{- end}}
`
var serviceTmpl = `
apiVersion: v1
kind: Service
metadata:
name: "{{ .Metadata.Name }}"
namespace: "{{ .Metadata.Namespace }}"
labels:
{{- with .Metadata.Labels }}
{{- range $key, $value := . }}
{{ $key }}: "{{ $value }}"
{{- end }}
{{- end }}
spec:
selector:
{{- with .Spec.Selector }}
{{- range $key, $value := . }}
{{ $key }}: "{{ $value }}"
{{- end }}
{{- end }}
ports:
{{- with .Spec.Ports }}
{{- range . }}
- name: "{{ .Name }}"
port: {{ .Port }}
protocol: {{ .Protocol }}
{{- end }}
{{- end }}
`

View File

@ -0,0 +1,125 @@
package client
// Resource is API resource
type Resource struct {
Name string
Kind string
Value interface{}
}
// Metadata defines api object metadata
type Metadata struct {
Name string `json:"name,omitempty"`
Namespace string `json:"namespace,omitempty"`
Version string `json:"version,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
Annotations map[string]string `json:"annotations,omitempty"`
}
// ServicePort configures service ports
type ServicePort struct {
Name string `json:"name,omitempty"`
Port int `json:"port"`
Protocol string `json:"protocol,omitempty"`
}
// ServiceSpec provides service configuration
type ServiceSpec struct {
Type string `json:"type,omitempty"`
Selector map[string]string `json:"selector,omitempty"`
Ports []ServicePort `json:"ports,omitempty"`
}
type LoadBalancerIngress struct {
IP string `json:"ip,omitempty"`
Hostname string `json:"hostname,omitempty"`
}
type LoadBalancerStatus struct {
Ingress []LoadBalancerIngress `json:"ingress,omitempty"`
}
// ServiceStatus
type ServiceStatus struct {
LoadBalancer LoadBalancerStatus `json:"loadBalancer,omitempty"`
}
// Service is kubernetes service
type Service struct {
Metadata *Metadata `json:"metadata"`
Spec *ServiceSpec `json:"spec,omitempty"`
Status *ServiceStatus `json:"status,omitempty"`
}
// ServiceList
type ServiceList struct {
Items []Service `json:"items"`
}
// ContainerPort
type ContainerPort struct {
Name string `json:"name,omitempty"`
HostPort int `json:"hostPort,omitempty"`
ContainerPort int `json:"containerPort"`
Protocol string `json:"protocol,omitempty"`
}
// EnvVar is environment variable
type EnvVar struct {
Name string `json:"name"`
Value string `json:"value,omitempty"`
}
// Container defined container runtime values
type Container struct {
Name string `json:"name"`
Image string `json:"image"`
Env []EnvVar `json:"env,omitempty"`
Command []string `json:"command,omitempty"`
Ports []ContainerPort `json:"ports,omitempty"`
}
// PodSpec
type PodSpec struct {
Containers []Container `json:"containers"`
}
// Template is micro deployment template
type Template struct {
Metadata *Metadata `json:"metadata,omitempty"`
PodSpec *PodSpec `json:"spec,omitempty"`
}
// LabelSelector is a label query over a set of resources
// NOTE: we do not support MatchExpressions at the moment
type LabelSelector struct {
MatchLabels map[string]string `json:"matchLabels,omitempty"`
}
// DeploymentSpec defines micro deployment spec
type DeploymentSpec struct {
Replicas int `json:"replicas,omitempty"`
Selector *LabelSelector `json:"selector"`
Template *Template `json:"template,omitempty"`
}
// DeploymentStatus is returned when querying deployment
type DeploymentStatus struct {
Replicas int `json:"replicas,omitempty"`
UpdatedReplicas int `json:"updatedReplicas,omitempty"`
ReadyReplicas int `json:"readyReplicas,omitempty"`
AvailableReplicas int `json:"availableReplicas,omitempty"`
UnavailableReplicas int `json:"unavailableReplicas,omitempty"`
}
// Deployment is Kubernetes deployment
type Deployment struct {
Metadata *Metadata `json:"metadata"`
Spec *DeploymentSpec `json:"spec,omitempty"`
Status *DeploymentStatus `json:"status,omitempty"`
}
// DeploymentList
type DeploymentList struct {
Items []Deployment `json:"items"`
}

View File

@ -5,9 +5,22 @@ import (
"encoding/pem" "encoding/pem"
"errors" "errors"
"fmt" "fmt"
"io"
"io/ioutil" "io/ioutil"
"text/template"
) )
// renderTemplateFile renders template file in path into writer w with supplied data
func renderTemplate(text string, w io.Writer, data interface{}) error {
t := template.Must(template.New("kubernetes").Parse(text))
if err := t.Execute(w, data); err != nil {
return err
}
return nil
}
// COPIED FROM // COPIED FROM
// https://github.com/kubernetes/kubernetes/blob/7a725418af4661067b56506faabc2d44c6d7703a/pkg/util/crypto/crypto.go // https://github.com/kubernetes/kubernetes/blob/7a725418af4661067b56506faabc2d44c6d7703a/pkg/util/crypto/crypto.go

View File

@ -0,0 +1,25 @@
package client
import (
"bytes"
"testing"
)
func TestTemplates(t *testing.T) {
name := "foo"
version := "1.2.3"
// Render default service
s := DefaultService(name, version)
bs := new(bytes.Buffer)
if err := renderTemplate(serviceTmpl, bs, s); err != nil {
t.Errorf("Failed to render kubernetes service: %v", err)
}
// Render default deployment
d := DefaultDeployment(name, version)
bd := new(bytes.Buffer)
if err := renderTemplate(deploymentTmpl, bd, d); err != nil {
t.Errorf("Failed to render kubernetes deployment: %v", err)
}
}

View File

@ -1,74 +0,0 @@
package watch
import (
"bufio"
"encoding/json"
"net/http"
)
// bodyWatcher scans the body of a request for chunks
type bodyWatcher struct {
results chan Event
stop chan struct{}
res *http.Response
req *http.Request
}
// Changes returns the results channel
func (wr *bodyWatcher) ResultChan() <-chan Event {
return wr.results
}
// Stop cancels the request
func (wr *bodyWatcher) Stop() {
select {
case <-wr.stop:
return
default:
close(wr.stop)
close(wr.results)
}
}
func (wr *bodyWatcher) stream() {
reader := bufio.NewReader(wr.res.Body)
// stop the watcher
defer wr.Stop()
for {
// read a line
b, err := reader.ReadBytes('\n')
if err != nil {
return
}
// send the event
var event Event
if err := json.Unmarshal(b, &event); err != nil {
continue
}
wr.results <- event
}
}
// NewBodyWatcher creates a k8s body watcher for a given http request
func NewBodyWatcher(req *http.Request, client *http.Client) (Watch, error) {
stop := make(chan struct{})
req.Cancel = stop
res, err := client.Do(req)
if err != nil {
return nil, err
}
wr := &bodyWatcher{
results: make(chan Event),
stop: stop,
req: req,
res: res,
}
go wr.stream()
return wr, nil
}

View File

@ -1,26 +0,0 @@
package watch
import "encoding/json"
// Watch ...
type Watch interface {
Stop()
ResultChan() <-chan Event
}
// EventType defines the possible types of events.
type EventType string
// EventTypes used
const (
Added EventType = "ADDED"
Modified EventType = "MODIFIED"
Deleted EventType = "DELETED"
Error EventType = "ERROR"
)
// Event represents a single event to a watched resource.
type Event struct {
Type EventType `json:"type"`
Object json.RawMessage `json:"object"`
}

View File

@ -1,69 +0,0 @@
package watch
import (
"fmt"
"net/http"
"net/http/httptest"
"testing"
"time"
)
var actions = []string{
`{"type": "create", "object":{"foo": "bar"}}`,
`{"type": "delete", INVALID}`,
`{"type": "update", "object":{"foo": {"foo": "bar"}}}`,
`{"type": "delete", "object":null}`,
}
func TestBodyWatcher(t *testing.T) {
// set up server with handler to flush strings from ch.
ch := make(chan string)
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
flusher, ok := w.(http.Flusher)
if !ok {
t.Fatal("expected ResponseWriter to be a flusher")
}
fmt.Fprintf(w, "\n")
flusher.Flush()
for v := range ch {
fmt.Fprintf(w, "%s\n", v)
flusher.Flush()
time.Sleep(10 * time.Millisecond)
}
}))
defer ts.Close()
req, err := http.NewRequest("GET", ts.URL, nil)
if err != nil {
t.Fatalf("failed to create new request: %v", err)
}
// setup body watcher
w, err := NewBodyWatcher(req, http.DefaultClient)
if err != nil {
t.Fatalf("failed to create new BodyWatcher %v", err)
}
// send action strings in, and expect result back
ch <- actions[0]
if r := <-w.ResultChan(); r.Type != "create" {
t.Fatalf("expected result to be create")
}
ch <- actions[1] // should be ignored as its invalid json
ch <- actions[2]
if r := <-w.ResultChan(); r.Type != "update" {
t.Fatalf("expected result to be update")
}
ch <- actions[3]
if r := <-w.ResultChan(); r.Type != "delete" {
t.Fatalf("expected result to be delete")
}
// stop should clean up all channels.
w.Stop()
close(ch)
}

View File

@ -14,18 +14,31 @@ import (
"github.com/micro/go-micro/util/log" "github.com/micro/go-micro/util/log"
) )
// action to take on runtime service
type action int
const (
start action = iota
update
stop
)
// task is queued into runtime queue
type task struct {
action action
service *service
}
type kubernetes struct { type kubernetes struct {
sync.RWMutex sync.RWMutex
// options configure runtime // options configure runtime
options runtime.Options options runtime.Options
// indicates if we're running // indicates if we're running
running bool running bool
// used to start new services // task queue for kubernetes services
start chan *runtime.Service queue chan *task
// used to stop the runtime // used to stop the runtime
closed chan bool closed chan bool
// service tracks deployed services
services map[string]*runtime.Service
// client is kubernetes client // client is kubernetes client
client client.Kubernetes client client.Kubernetes
} }
@ -46,8 +59,7 @@ func NewRuntime(opts ...runtime.Option) runtime.Runtime {
return &kubernetes{ return &kubernetes{
options: options, options: options,
closed: make(chan bool), closed: make(chan bool),
start: make(chan *runtime.Service, 128), queue: make(chan *task, 128),
services: make(map[string]*runtime.Service),
client: client, client: client,
} }
} }
@ -64,33 +76,109 @@ func (k *kubernetes) Init(opts ...runtime.Option) error {
return nil return nil
} }
// Registers a service // Creates a service
func (k *kubernetes) Create(s *runtime.Service, opts ...runtime.CreateOption) error { func (k *kubernetes) Create(s *runtime.Service, opts ...runtime.CreateOption) error {
k.Lock() k.Lock()
defer k.Unlock() defer k.Unlock()
// TODO:
// * create service
// * create deployment
// NOTE: our services have micro- prefix
muName := strings.Split(s.Name, ".")
s.Name = "micro-" + muName[len(muName)-1]
// NOTE: we are tracking this in memory for now
if _, ok := k.services[s.Name]; ok {
return errors.New("service already registered")
}
var options runtime.CreateOptions var options runtime.CreateOptions
for _, o := range opts { for _, o := range opts {
o(&options) o(&options)
} }
// save service svcName := s.Name
k.services[s.Name] = s if len(s.Version) > 0 {
svcName = strings.Join([]string{s.Name, s.Version}, "-")
}
if !client.ServiceRegexp.MatchString(svcName) {
return fmt.Errorf("invalid service name: %s", svcName)
}
// create new kubernetes micro service
service := newService(s, options)
log.Debugf("Runtime queueing service %s for start action", service.Name)
// push into start queue // push into start queue
k.start <- k.services[s.Name] k.queue <- &task{
action: start,
service: service,
}
return nil
}
// Get returns all instances of given service
func (k *kubernetes) Get(name string, opts ...runtime.GetOption) ([]*runtime.Service, error) {
k.Lock()
defer k.Unlock()
// if no name has been passed in, return error
if len(name) == 0 {
return nil, errors.New("missing service name")
}
// set the default label
labels := map[string]string{
"micro": "service",
"name": name,
}
var options runtime.GetOptions
for _, o := range opts {
o(&options)
}
// add version to labels if a version has been supplied
if len(options.Version) > 0 {
labels["version"] = options.Version
}
log.Debugf("Runtime querying service %s", name)
serviceList := new(client.ServiceList)
r := &client.Resource{
Kind: "service",
Value: serviceList,
}
if err := k.client.Get(r, labels); err != nil {
return nil, err
}
services := make([]*runtime.Service, 0, len(serviceList.Items))
for _, kservice := range serviceList.Items {
service := &runtime.Service{
Name: kservice.Metadata.Name,
Version: kservice.Metadata.Version,
}
services = append(services, service)
}
return services, nil
}
// Update the service in place
func (k *kubernetes) Update(s *runtime.Service) error {
// parse version into human readable timestamp
updateTimeStamp, err := strconv.ParseInt(s.Version, 10, 64)
if err != nil {
return err
}
unixTimeUTC := time.Unix(updateTimeStamp, 0)
// create new kubernetes micro service
service := newService(s, runtime.CreateOptions{})
// update build time annotation
service.kdeploy.Spec.Template.Metadata.Annotations["build"] = unixTimeUTC.Format(time.RFC3339)
log.Debugf("Runtime queueing service %s for update action", service.Name)
// queue service for removal
k.queue <- &task{
action: update,
service: service,
}
return nil return nil
} }
@ -100,61 +188,37 @@ func (k *kubernetes) Delete(s *runtime.Service) error {
k.Lock() k.Lock()
defer k.Unlock() defer k.Unlock()
// TODO: // create new kubernetes micro service
// * delete service service := newService(s, runtime.CreateOptions{})
// * delete dpeloyment
// NOTE: we are tracking this in memory for now log.Debugf("Runtime queueing service %s for delete action", service.Name)
if s, ok := k.services[s.Name]; ok {
delete(k.services, s.Name) // queue service for removal
return nil k.queue <- &task{
action: stop,
service: service,
} }
return nil return nil
} }
// Update the service in place
func (k *kubernetes) Update(s *runtime.Service) error {
type body struct {
Spec *client.Spec `json:"spec"`
}
// parse version into human readable timestamp
updateTimeStamp, err := strconv.ParseInt(s.Version, 10, 64)
if err != nil {
return err
}
unixTimeUTC := time.Unix(updateTimeStamp, 0)
// metada which we will PATCH deployment with
reqBody := body{
Spec: &client.Spec{
Template: &client.Template{
Metadata: &client.Metadata{
Annotations: map[string]string{
"build": unixTimeUTC.Format(time.RFC3339),
},
},
},
},
}
return k.client.UpdateDeployment(s.Name, reqBody)
}
// List the managed services // List the managed services
func (k *kubernetes) List() ([]*runtime.Service, error) { func (k *kubernetes) List() ([]*runtime.Service, error) {
labels := map[string]string{ serviceList := new(client.ServiceList)
"micro": "service", r := &client.Resource{
Kind: "service",
Value: serviceList,
} }
// list all micro core deployments
deployments, err := k.client.ListDeployments(labels) if err := k.client.List(r); err != nil {
if err != nil {
return nil, err return nil, err
} }
log.Debugf("Runtime found %d micro deployments with labels %v", len(deployments.Items), labels) log.Debugf("Runtime found %d micro services", len(serviceList.Items))
services := make([]*runtime.Service, 0, len(deployments.Items)) services := make([]*runtime.Service, 0, len(serviceList.Items))
for _, service := range deployments.Items { for _, service := range serviceList.Items {
buildTime, err := time.Parse(time.RFC3339, service.Metadata.Annotations["build"]) buildTime, err := time.Parse(time.RFC3339, service.Metadata.Annotations["build"])
if err != nil { if err != nil {
log.Debugf("Runtime error parsing build time for %s: %v", service.Metadata.Name, err) log.Debugf("Runtime error parsing build time for %s: %v", service.Metadata.Name, err)
@ -179,23 +243,31 @@ func (k *kubernetes) run(events <-chan runtime.Event) {
for { for {
select { select {
case <-t.C: case <-t.C:
// check running services // TODO: figure out what to do here
services, err := k.List() // - do we even need the ticker for k8s services?
if err != nil { case task := <-k.queue:
log.Debugf("Runtime failed listing running services: %v", err) switch task.action {
case start:
log.Debugf("Runtime starting new service: %s", task.service.Name)
if err := task.service.Start(k.client); err != nil {
log.Debugf("Runtime failed to start service %s: %v", task.service.Name, err)
continue continue
} }
// TODO: for now we just log the running services case stop:
// * make sure all core deployments exist log.Debugf("Runtime stopping service: %s", task.service.Name)
// * make sure all core services are exposed if err := task.service.Stop(k.client); err != nil {
for _, service := range services { log.Debugf("Runtime failed to stop service %s: %v", task.service.Name, err)
log.Debugf("Runtime found running service: %v", service) continue
}
case update:
log.Debugf("Runtime updating service: %s", task.service.Name)
if err := task.service.Update(k.client); err != nil {
log.Debugf("Runtime failed to update service %s: %v", task.service.Name, err)
continue
}
default:
log.Debugf("Runtime received unknown action for service: %s", task.service.Name)
} }
case service := <-k.start:
// TODO: this is a noop for now
// * create a deployment
// * expose a service
log.Debugf("Runtime starting service: %s", service.Name)
case event := <-events: case event := <-events:
// NOTE: we only handle Update events for now // NOTE: we only handle Update events for now
log.Debugf("Runtime received notification event: %v", event) log.Debugf("Runtime received notification event: %v", event)
@ -207,50 +279,23 @@ func (k *kubernetes) run(events <-chan runtime.Event) {
log.Debugf("Runtime error parsing update build time: %v", err) log.Debugf("Runtime error parsing update build time: %v", err)
continue continue
} }
buildTime := time.Unix(updateTimeStamp, 0) unixTimeUTC := time.Unix(updateTimeStamp, 0)
processEvent := func(event runtime.Event, service *runtime.Service) error {
buildTimeStamp, err := strconv.ParseInt(service.Version, 10, 64)
if err != nil {
return err
}
muBuild := time.Unix(buildTimeStamp, 0)
if buildTime.After(muBuild) {
version := fmt.Sprintf("%d", buildTime.Unix())
muService := &runtime.Service{
Name: service.Name,
Source: service.Source,
Path: service.Path,
Exec: service.Exec,
Version: version,
}
if err := k.Update(muService); err != nil {
return err
}
service.Version = version
}
return nil
}
k.Lock()
if len(event.Service) > 0 { if len(event.Service) > 0 {
service, ok := k.services[event.Service] s := &runtime.Service{
if !ok { Name: event.Service,
log.Debugf("Runtime unknown service: %s", event.Service) Version: event.Version,
k.Unlock() }
// create new kubernetes micro service
service := newService(s, runtime.CreateOptions{})
// update build time annotation
service.kdeploy.Spec.Template.Metadata.Annotations["build"] = unixTimeUTC.Format(time.RFC3339)
log.Debugf("Runtime updating service: %s", service.Name)
if err := service.Update(k.client); err != nil {
log.Debugf("Runtime failed to update service %s: %v", service.Name, err)
continue continue
} }
if err := processEvent(event, service); err != nil {
log.Debugf("Runtime error updating service %s: %v", event.Service, err)
} }
k.Unlock()
continue
}
// if blank service was received we update all services
for _, service := range k.services {
if err := processEvent(event, service); err != nil {
log.Debugf("Runtime error updating service %s: %v", service.Name, err)
}
}
k.Unlock()
} }
case <-k.closed: case <-k.closed:
log.Debugf("Runtime stopped") log.Debugf("Runtime stopped")

View File

@ -0,0 +1,106 @@
package kubernetes
import (
"strings"
"github.com/micro/go-micro/runtime"
"github.com/micro/go-micro/runtime/kubernetes/client"
"github.com/micro/go-micro/util/log"
)
type service struct {
// service to manage
*runtime.Service
// Kubernetes service
kservice *client.Service
// Kubernetes deployment
kdeploy *client.Deployment
}
func newService(s *runtime.Service, c runtime.CreateOptions) *service {
kservice := client.DefaultService(s.Name, s.Version)
kdeploy := client.DefaultDeployment(s.Name, s.Version)
env := make([]client.EnvVar, 0, len(c.Env))
for _, evar := range c.Env {
evarPair := strings.Split(evar, "=")
env = append(env, client.EnvVar{Name: evarPair[0], Value: evarPair[1]})
}
// TODO: should we append instead of overriding?
// if environment has been supplied update deployment
if len(env) > 0 {
kdeploy.Spec.Template.PodSpec.Containers[0].Env = env
}
// if Command has been supplied override the default command
if len(c.Command) > 0 {
kdeploy.Spec.Template.PodSpec.Containers[0].Command = c.Command
}
return &service{
Service: s,
kservice: kservice,
kdeploy: kdeploy,
}
}
func deploymentResource(d *client.Deployment) *client.Resource {
return &client.Resource{
Name: d.Metadata.Name,
Kind: "deployment",
Value: d,
}
}
func serviceResource(s *client.Service) *client.Resource {
return &client.Resource{
Name: s.Metadata.Name,
Kind: "service",
Value: s,
}
}
// Start starts the Kubernetes service. It creates new kubernetes deployment and service API objects
func (s *service) Start(k client.Kubernetes) error {
// create deployment first; if we fail, we dont create service
if err := k.Create(deploymentResource(s.kdeploy)); err != nil {
log.Debugf("Runtime failed to create deployment: %v", err)
return err
}
// create service now that the deployment has been created
if err := k.Create(serviceResource(s.kservice)); err != nil {
log.Debugf("Runtime failed to create service: %v", err)
return err
}
return nil
}
func (s *service) Stop(k client.Kubernetes) error {
// first attempt to delete service
if err := k.Delete(serviceResource(s.kservice)); err != nil {
log.Debugf("Runtime failed to delete service: %v", err)
return err
}
// delete deployment once the service has been deleted
if err := k.Delete(deploymentResource(s.kdeploy)); err != nil {
log.Debugf("Runtime failed to delete deployment: %v", err)
return err
}
return nil
}
func (s *service) Update(k client.Kubernetes) error {
if err := k.Update(deploymentResource(s.kdeploy)); err != nil {
log.Debugf("Runtime failed to update deployment: %v", err)
return err
}
if err := k.Update(serviceResource(s.kservice)); err != nil {
log.Debugf("Runtime failed to update service: %v", err)
return err
}
return nil
}

View File

@ -54,3 +54,18 @@ func WithOutput(out io.Writer) CreateOption {
o.Output = out o.Output = out
} }
} }
type GetOption func(o *GetOptions)
// GetOptions queries runtime services
type GetOptions struct {
// Version queries services with given version
Version string
}
// WithVersion confifgures service version
func WithVersion(version string) GetOption {
return func(o *GetOptions) {
o.Version = version
}
}

View File

@ -12,17 +12,19 @@ var (
type Runtime interface { type Runtime interface {
// Init initializes runtime // Init initializes runtime
Init(...Option) error Init(...Option) error
// Registers a service // Create registers a service
Create(*Service, ...CreateOption) error Create(*Service, ...CreateOption) error
// Remove a service // Get returns service or fails with error
Delete(*Service) error Get(string, ...GetOption) ([]*Service, error)
// Update the service in place // Update the service in place
Update(*Service) error Update(*Service) error
// Remove a service
Delete(*Service) error
// List the managed services // List the managed services
List() ([]*Service, error) List() ([]*Service, error)
// starts the runtime // Start starts the runtime
Start() error Start() error
// Shutdown the runtime // Stop shuts down the runtime
Stop() error Stop() error
} }

View File

@ -32,14 +32,48 @@ func toService(s *pb.Service) *runtime.Service {
} }
} }
func toCreateOptions(opts *pb.CreateOptions) []runtime.CreateOption {
options := []runtime.CreateOption{}
// command options
l := len(opts.Command)
if l == 1 {
options = append(options, runtime.WithCommand(opts.Command[0]))
}
if l > 1 {
options = append(options, runtime.WithCommand(opts.Command[0], opts.Command[1:]...))
}
// env options
if len(opts.Env) > 0 {
options = append(options, runtime.WithEnv(opts.Env))
}
// TODO: output options
return options
}
func toGetOptions(opts *pb.GetOptions) []runtime.GetOption {
options := []runtime.GetOption{}
// version options
if len(opts.Version) > 0 {
options = append(options, runtime.WithVersion(opts.Version))
}
return options
}
func (r *Runtime) Create(ctx context.Context, req *pb.CreateRequest, rsp *pb.CreateResponse) error { func (r *Runtime) Create(ctx context.Context, req *pb.CreateRequest, rsp *pb.CreateResponse) error {
if req.Service == nil { if req.Service == nil {
return errors.BadRequest("go.micro.runtime", "blank service") return errors.BadRequest("go.micro.runtime", "blank service")
} }
// TODO: add opts var options []runtime.CreateOption
if req.Options != nil {
options = toCreateOptions(req.Options)
}
service := toService(req.Service) service := toService(req.Service)
err := r.Runtime.Create(service) err := r.Runtime.Create(service, options...)
if err != nil { if err != nil {
return errors.InternalServerError("go.micro.runtime", err.Error()) return errors.InternalServerError("go.micro.runtime", err.Error())
} }
@ -47,6 +81,28 @@ func (r *Runtime) Create(ctx context.Context, req *pb.CreateRequest, rsp *pb.Cre
return nil return nil
} }
func (r *Runtime) Get(ctx context.Context, req *pb.GetRequest, rsp *pb.GetResponse) error {
if len(req.Name) == 0 {
return errors.BadRequest("go.micro.runtime", "blank service")
}
var options []runtime.GetOption
if req.Options != nil {
options = toGetOptions(req.Options)
}
services, err := r.Runtime.Get(req.Name, options...)
if err != nil {
return errors.InternalServerError("go.micro.runtime", err.Error())
}
for _, service := range services {
rsp.Services = append(rsp.Services, toProto(service))
}
return nil
}
func (r *Runtime) Update(ctx context.Context, req *pb.UpdateRequest, rsp *pb.UpdateResponse) error { func (r *Runtime) Update(ctx context.Context, req *pb.UpdateRequest, rsp *pb.UpdateResponse) error {
if req.Service == nil { if req.Service == nil {
return errors.BadRequest("go.micro.runtime", "blank service") return errors.BadRequest("go.micro.runtime", "blank service")

View File

@ -1,5 +1,5 @@
// Code generated by protoc-gen-micro. DO NOT EDIT. // Code generated by protoc-gen-micro. DO NOT EDIT.
// source: micro/go-micro/runtime/service/proto/runtime.proto // source: runtime.proto
package go_micro_runtime package go_micro_runtime
@ -35,6 +35,7 @@ var _ server.Option
type RuntimeService interface { type RuntimeService interface {
Create(ctx context.Context, in *CreateRequest, opts ...client.CallOption) (*CreateResponse, error) Create(ctx context.Context, in *CreateRequest, opts ...client.CallOption) (*CreateResponse, error)
Get(ctx context.Context, in *GetRequest, opts ...client.CallOption) (*GetResponse, error)
Delete(ctx context.Context, in *DeleteRequest, opts ...client.CallOption) (*DeleteResponse, error) Delete(ctx context.Context, in *DeleteRequest, opts ...client.CallOption) (*DeleteResponse, error)
Update(ctx context.Context, in *UpdateRequest, opts ...client.CallOption) (*UpdateResponse, error) Update(ctx context.Context, in *UpdateRequest, opts ...client.CallOption) (*UpdateResponse, error)
List(ctx context.Context, in *ListRequest, opts ...client.CallOption) (*ListResponse, error) List(ctx context.Context, in *ListRequest, opts ...client.CallOption) (*ListResponse, error)
@ -68,6 +69,16 @@ func (c *runtimeService) Create(ctx context.Context, in *CreateRequest, opts ...
return out, nil return out, nil
} }
func (c *runtimeService) Get(ctx context.Context, in *GetRequest, opts ...client.CallOption) (*GetResponse, error) {
req := c.c.NewRequest(c.name, "Runtime.Get", in)
out := new(GetResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *runtimeService) Delete(ctx context.Context, in *DeleteRequest, opts ...client.CallOption) (*DeleteResponse, error) { func (c *runtimeService) Delete(ctx context.Context, in *DeleteRequest, opts ...client.CallOption) (*DeleteResponse, error) {
req := c.c.NewRequest(c.name, "Runtime.Delete", in) req := c.c.NewRequest(c.name, "Runtime.Delete", in)
out := new(DeleteResponse) out := new(DeleteResponse)
@ -102,6 +113,7 @@ func (c *runtimeService) List(ctx context.Context, in *ListRequest, opts ...clie
type RuntimeHandler interface { type RuntimeHandler interface {
Create(context.Context, *CreateRequest, *CreateResponse) error Create(context.Context, *CreateRequest, *CreateResponse) error
Get(context.Context, *GetRequest, *GetResponse) error
Delete(context.Context, *DeleteRequest, *DeleteResponse) error Delete(context.Context, *DeleteRequest, *DeleteResponse) error
Update(context.Context, *UpdateRequest, *UpdateResponse) error Update(context.Context, *UpdateRequest, *UpdateResponse) error
List(context.Context, *ListRequest, *ListResponse) error List(context.Context, *ListRequest, *ListResponse) error
@ -110,6 +122,7 @@ type RuntimeHandler interface {
func RegisterRuntimeHandler(s server.Server, hdlr RuntimeHandler, opts ...server.HandlerOption) error { func RegisterRuntimeHandler(s server.Server, hdlr RuntimeHandler, opts ...server.HandlerOption) error {
type runtime interface { type runtime interface {
Create(ctx context.Context, in *CreateRequest, out *CreateResponse) error Create(ctx context.Context, in *CreateRequest, out *CreateResponse) error
Get(ctx context.Context, in *GetRequest, out *GetResponse) error
Delete(ctx context.Context, in *DeleteRequest, out *DeleteResponse) error Delete(ctx context.Context, in *DeleteRequest, out *DeleteResponse) error
Update(ctx context.Context, in *UpdateRequest, out *UpdateResponse) error Update(ctx context.Context, in *UpdateRequest, out *UpdateResponse) error
List(ctx context.Context, in *ListRequest, out *ListResponse) error List(ctx context.Context, in *ListRequest, out *ListResponse) error
@ -129,6 +142,10 @@ func (h *runtimeHandler) Create(ctx context.Context, in *CreateRequest, out *Cre
return h.RuntimeHandler.Create(ctx, in, out) return h.RuntimeHandler.Create(ctx, in, out)
} }
func (h *runtimeHandler) Get(ctx context.Context, in *GetRequest, out *GetResponse) error {
return h.RuntimeHandler.Get(ctx, in, out)
}
func (h *runtimeHandler) Delete(ctx context.Context, in *DeleteRequest, out *DeleteResponse) error { func (h *runtimeHandler) Delete(ctx context.Context, in *DeleteRequest, out *DeleteResponse) error {
return h.RuntimeHandler.Delete(ctx, in, out) return h.RuntimeHandler.Delete(ctx, in, out)
} }

View File

@ -1,13 +1,11 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// source: micro/go-micro/runtime/service/proto/runtime.proto // source: runtime.proto
package go_micro_runtime package go_micro_runtime
import ( import (
context "context"
fmt "fmt" fmt "fmt"
proto "github.com/golang/protobuf/proto" proto "github.com/golang/protobuf/proto"
grpc "google.golang.org/grpc"
math "math" math "math"
) )
@ -42,7 +40,7 @@ func (m *Service) Reset() { *m = Service{} }
func (m *Service) String() string { return proto.CompactTextString(m) } func (m *Service) String() string { return proto.CompactTextString(m) }
func (*Service) ProtoMessage() {} func (*Service) ProtoMessage() {}
func (*Service) Descriptor() ([]byte, []int) { func (*Service) Descriptor() ([]byte, []int) {
return fileDescriptor_4bc91a8efec81434, []int{0} return fileDescriptor_86e2dd377c869464, []int{0}
} }
func (m *Service) XXX_Unmarshal(b []byte) error { func (m *Service) XXX_Unmarshal(b []byte) error {
@ -98,9 +96,9 @@ func (m *Service) GetExec() string {
return "" return ""
} }
type Options struct { type CreateOptions struct {
// command to pass in // command to pass in
Command string `protobuf:"bytes,1,opt,name=command,proto3" json:"command,omitempty"` Command []string `protobuf:"bytes,1,rep,name=command,proto3" json:"command,omitempty"`
// environment to pass in // environment to pass in
Env []string `protobuf:"bytes,2,rep,name=env,proto3" json:"env,omitempty"` Env []string `protobuf:"bytes,2,rep,name=env,proto3" json:"env,omitempty"`
// output to send to // output to send to
@ -110,46 +108,46 @@ type Options struct {
XXX_sizecache int32 `json:"-"` XXX_sizecache int32 `json:"-"`
} }
func (m *Options) Reset() { *m = Options{} } func (m *CreateOptions) Reset() { *m = CreateOptions{} }
func (m *Options) String() string { return proto.CompactTextString(m) } func (m *CreateOptions) String() string { return proto.CompactTextString(m) }
func (*Options) ProtoMessage() {} func (*CreateOptions) ProtoMessage() {}
func (*Options) Descriptor() ([]byte, []int) { func (*CreateOptions) Descriptor() ([]byte, []int) {
return fileDescriptor_4bc91a8efec81434, []int{1} return fileDescriptor_86e2dd377c869464, []int{1}
} }
func (m *Options) XXX_Unmarshal(b []byte) error { func (m *CreateOptions) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Options.Unmarshal(m, b) return xxx_messageInfo_CreateOptions.Unmarshal(m, b)
} }
func (m *Options) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *CreateOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Options.Marshal(b, m, deterministic) return xxx_messageInfo_CreateOptions.Marshal(b, m, deterministic)
} }
func (m *Options) XXX_Merge(src proto.Message) { func (m *CreateOptions) XXX_Merge(src proto.Message) {
xxx_messageInfo_Options.Merge(m, src) xxx_messageInfo_CreateOptions.Merge(m, src)
} }
func (m *Options) XXX_Size() int { func (m *CreateOptions) XXX_Size() int {
return xxx_messageInfo_Options.Size(m) return xxx_messageInfo_CreateOptions.Size(m)
} }
func (m *Options) XXX_DiscardUnknown() { func (m *CreateOptions) XXX_DiscardUnknown() {
xxx_messageInfo_Options.DiscardUnknown(m) xxx_messageInfo_CreateOptions.DiscardUnknown(m)
} }
var xxx_messageInfo_Options proto.InternalMessageInfo var xxx_messageInfo_CreateOptions proto.InternalMessageInfo
func (m *Options) GetCommand() string { func (m *CreateOptions) GetCommand() []string {
if m != nil { if m != nil {
return m.Command return m.Command
} }
return "" return nil
} }
func (m *Options) GetEnv() []string { func (m *CreateOptions) GetEnv() []string {
if m != nil { if m != nil {
return m.Env return m.Env
} }
return nil return nil
} }
func (m *Options) GetOutput() string { func (m *CreateOptions) GetOutput() string {
if m != nil { if m != nil {
return m.Output return m.Output
} }
@ -158,7 +156,7 @@ func (m *Options) GetOutput() string {
type CreateRequest struct { type CreateRequest struct {
Service *Service `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"` Service *Service `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"`
Options *Options `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"` Options *CreateOptions `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"` XXX_sizecache int32 `json:"-"`
@ -168,7 +166,7 @@ func (m *CreateRequest) Reset() { *m = CreateRequest{} }
func (m *CreateRequest) String() string { return proto.CompactTextString(m) } func (m *CreateRequest) String() string { return proto.CompactTextString(m) }
func (*CreateRequest) ProtoMessage() {} func (*CreateRequest) ProtoMessage() {}
func (*CreateRequest) Descriptor() ([]byte, []int) { func (*CreateRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_4bc91a8efec81434, []int{2} return fileDescriptor_86e2dd377c869464, []int{2}
} }
func (m *CreateRequest) XXX_Unmarshal(b []byte) error { func (m *CreateRequest) XXX_Unmarshal(b []byte) error {
@ -196,7 +194,7 @@ func (m *CreateRequest) GetService() *Service {
return nil return nil
} }
func (m *CreateRequest) GetOptions() *Options { func (m *CreateRequest) GetOptions() *CreateOptions {
if m != nil { if m != nil {
return m.Options return m.Options
} }
@ -213,7 +211,7 @@ func (m *CreateResponse) Reset() { *m = CreateResponse{} }
func (m *CreateResponse) String() string { return proto.CompactTextString(m) } func (m *CreateResponse) String() string { return proto.CompactTextString(m) }
func (*CreateResponse) ProtoMessage() {} func (*CreateResponse) ProtoMessage() {}
func (*CreateResponse) Descriptor() ([]byte, []int) { func (*CreateResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_4bc91a8efec81434, []int{3} return fileDescriptor_86e2dd377c869464, []int{3}
} }
func (m *CreateResponse) XXX_Unmarshal(b []byte) error { func (m *CreateResponse) XXX_Unmarshal(b []byte) error {
@ -234,6 +232,132 @@ func (m *CreateResponse) XXX_DiscardUnknown() {
var xxx_messageInfo_CreateResponse proto.InternalMessageInfo var xxx_messageInfo_CreateResponse proto.InternalMessageInfo
type GetOptions struct {
// version of the service
Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *GetOptions) Reset() { *m = GetOptions{} }
func (m *GetOptions) String() string { return proto.CompactTextString(m) }
func (*GetOptions) ProtoMessage() {}
func (*GetOptions) Descriptor() ([]byte, []int) {
return fileDescriptor_86e2dd377c869464, []int{4}
}
func (m *GetOptions) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetOptions.Unmarshal(m, b)
}
func (m *GetOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GetOptions.Marshal(b, m, deterministic)
}
func (m *GetOptions) XXX_Merge(src proto.Message) {
xxx_messageInfo_GetOptions.Merge(m, src)
}
func (m *GetOptions) XXX_Size() int {
return xxx_messageInfo_GetOptions.Size(m)
}
func (m *GetOptions) XXX_DiscardUnknown() {
xxx_messageInfo_GetOptions.DiscardUnknown(m)
}
var xxx_messageInfo_GetOptions proto.InternalMessageInfo
func (m *GetOptions) GetVersion() string {
if m != nil {
return m.Version
}
return ""
}
type GetRequest struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Options *GetOptions `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *GetRequest) Reset() { *m = GetRequest{} }
func (m *GetRequest) String() string { return proto.CompactTextString(m) }
func (*GetRequest) ProtoMessage() {}
func (*GetRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_86e2dd377c869464, []int{5}
}
func (m *GetRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetRequest.Unmarshal(m, b)
}
func (m *GetRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GetRequest.Marshal(b, m, deterministic)
}
func (m *GetRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_GetRequest.Merge(m, src)
}
func (m *GetRequest) XXX_Size() int {
return xxx_messageInfo_GetRequest.Size(m)
}
func (m *GetRequest) XXX_DiscardUnknown() {
xxx_messageInfo_GetRequest.DiscardUnknown(m)
}
var xxx_messageInfo_GetRequest proto.InternalMessageInfo
func (m *GetRequest) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *GetRequest) GetOptions() *GetOptions {
if m != nil {
return m.Options
}
return nil
}
type GetResponse struct {
Services []*Service `protobuf:"bytes,1,rep,name=services,proto3" json:"services,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *GetResponse) Reset() { *m = GetResponse{} }
func (m *GetResponse) String() string { return proto.CompactTextString(m) }
func (*GetResponse) ProtoMessage() {}
func (*GetResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_86e2dd377c869464, []int{6}
}
func (m *GetResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetResponse.Unmarshal(m, b)
}
func (m *GetResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GetResponse.Marshal(b, m, deterministic)
}
func (m *GetResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_GetResponse.Merge(m, src)
}
func (m *GetResponse) XXX_Size() int {
return xxx_messageInfo_GetResponse.Size(m)
}
func (m *GetResponse) XXX_DiscardUnknown() {
xxx_messageInfo_GetResponse.DiscardUnknown(m)
}
var xxx_messageInfo_GetResponse proto.InternalMessageInfo
func (m *GetResponse) GetServices() []*Service {
if m != nil {
return m.Services
}
return nil
}
type DeleteRequest struct { type DeleteRequest struct {
Service *Service `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"` Service *Service `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
@ -245,7 +369,7 @@ func (m *DeleteRequest) Reset() { *m = DeleteRequest{} }
func (m *DeleteRequest) String() string { return proto.CompactTextString(m) } func (m *DeleteRequest) String() string { return proto.CompactTextString(m) }
func (*DeleteRequest) ProtoMessage() {} func (*DeleteRequest) ProtoMessage() {}
func (*DeleteRequest) Descriptor() ([]byte, []int) { func (*DeleteRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_4bc91a8efec81434, []int{4} return fileDescriptor_86e2dd377c869464, []int{7}
} }
func (m *DeleteRequest) XXX_Unmarshal(b []byte) error { func (m *DeleteRequest) XXX_Unmarshal(b []byte) error {
@ -283,7 +407,7 @@ func (m *DeleteResponse) Reset() { *m = DeleteResponse{} }
func (m *DeleteResponse) String() string { return proto.CompactTextString(m) } func (m *DeleteResponse) String() string { return proto.CompactTextString(m) }
func (*DeleteResponse) ProtoMessage() {} func (*DeleteResponse) ProtoMessage() {}
func (*DeleteResponse) Descriptor() ([]byte, []int) { func (*DeleteResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_4bc91a8efec81434, []int{5} return fileDescriptor_86e2dd377c869464, []int{8}
} }
func (m *DeleteResponse) XXX_Unmarshal(b []byte) error { func (m *DeleteResponse) XXX_Unmarshal(b []byte) error {
@ -315,7 +439,7 @@ func (m *UpdateRequest) Reset() { *m = UpdateRequest{} }
func (m *UpdateRequest) String() string { return proto.CompactTextString(m) } func (m *UpdateRequest) String() string { return proto.CompactTextString(m) }
func (*UpdateRequest) ProtoMessage() {} func (*UpdateRequest) ProtoMessage() {}
func (*UpdateRequest) Descriptor() ([]byte, []int) { func (*UpdateRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_4bc91a8efec81434, []int{6} return fileDescriptor_86e2dd377c869464, []int{9}
} }
func (m *UpdateRequest) XXX_Unmarshal(b []byte) error { func (m *UpdateRequest) XXX_Unmarshal(b []byte) error {
@ -353,7 +477,7 @@ func (m *UpdateResponse) Reset() { *m = UpdateResponse{} }
func (m *UpdateResponse) String() string { return proto.CompactTextString(m) } func (m *UpdateResponse) String() string { return proto.CompactTextString(m) }
func (*UpdateResponse) ProtoMessage() {} func (*UpdateResponse) ProtoMessage() {}
func (*UpdateResponse) Descriptor() ([]byte, []int) { func (*UpdateResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_4bc91a8efec81434, []int{7} return fileDescriptor_86e2dd377c869464, []int{10}
} }
func (m *UpdateResponse) XXX_Unmarshal(b []byte) error { func (m *UpdateResponse) XXX_Unmarshal(b []byte) error {
@ -384,7 +508,7 @@ func (m *ListRequest) Reset() { *m = ListRequest{} }
func (m *ListRequest) String() string { return proto.CompactTextString(m) } func (m *ListRequest) String() string { return proto.CompactTextString(m) }
func (*ListRequest) ProtoMessage() {} func (*ListRequest) ProtoMessage() {}
func (*ListRequest) Descriptor() ([]byte, []int) { func (*ListRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_4bc91a8efec81434, []int{8} return fileDescriptor_86e2dd377c869464, []int{11}
} }
func (m *ListRequest) XXX_Unmarshal(b []byte) error { func (m *ListRequest) XXX_Unmarshal(b []byte) error {
@ -416,7 +540,7 @@ func (m *ListResponse) Reset() { *m = ListResponse{} }
func (m *ListResponse) String() string { return proto.CompactTextString(m) } func (m *ListResponse) String() string { return proto.CompactTextString(m) }
func (*ListResponse) ProtoMessage() {} func (*ListResponse) ProtoMessage() {}
func (*ListResponse) Descriptor() ([]byte, []int) { func (*ListResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_4bc91a8efec81434, []int{9} return fileDescriptor_86e2dd377c869464, []int{12}
} }
func (m *ListResponse) XXX_Unmarshal(b []byte) error { func (m *ListResponse) XXX_Unmarshal(b []byte) error {
@ -446,9 +570,12 @@ func (m *ListResponse) GetServices() []*Service {
func init() { func init() {
proto.RegisterType((*Service)(nil), "go.micro.runtime.Service") proto.RegisterType((*Service)(nil), "go.micro.runtime.Service")
proto.RegisterType((*Options)(nil), "go.micro.runtime.Options") proto.RegisterType((*CreateOptions)(nil), "go.micro.runtime.CreateOptions")
proto.RegisterType((*CreateRequest)(nil), "go.micro.runtime.CreateRequest") proto.RegisterType((*CreateRequest)(nil), "go.micro.runtime.CreateRequest")
proto.RegisterType((*CreateResponse)(nil), "go.micro.runtime.CreateResponse") proto.RegisterType((*CreateResponse)(nil), "go.micro.runtime.CreateResponse")
proto.RegisterType((*GetOptions)(nil), "go.micro.runtime.GetOptions")
proto.RegisterType((*GetRequest)(nil), "go.micro.runtime.GetRequest")
proto.RegisterType((*GetResponse)(nil), "go.micro.runtime.GetResponse")
proto.RegisterType((*DeleteRequest)(nil), "go.micro.runtime.DeleteRequest") proto.RegisterType((*DeleteRequest)(nil), "go.micro.runtime.DeleteRequest")
proto.RegisterType((*DeleteResponse)(nil), "go.micro.runtime.DeleteResponse") proto.RegisterType((*DeleteResponse)(nil), "go.micro.runtime.DeleteResponse")
proto.RegisterType((*UpdateRequest)(nil), "go.micro.runtime.UpdateRequest") proto.RegisterType((*UpdateRequest)(nil), "go.micro.runtime.UpdateRequest")
@ -457,205 +584,35 @@ func init() {
proto.RegisterType((*ListResponse)(nil), "go.micro.runtime.ListResponse") proto.RegisterType((*ListResponse)(nil), "go.micro.runtime.ListResponse")
} }
func init() { func init() { proto.RegisterFile("runtime.proto", fileDescriptor_86e2dd377c869464) }
proto.RegisterFile("micro/go-micro/runtime/service/proto/runtime.proto", fileDescriptor_4bc91a8efec81434)
} var fileDescriptor_86e2dd377c869464 = []byte{
// 425 bytes of a gzipped FileDescriptorProto
var fileDescriptor_4bc91a8efec81434 = []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x54, 0xc9, 0xcb, 0xd3, 0x40,
// 379 bytes of a gzipped FileDescriptorProto 0x14, 0xff, 0xda, 0xd4, 0x44, 0x5f, 0x8c, 0x94, 0x39, 0xc8, 0x58, 0x5c, 0xca, 0x1c, 0xc4, 0x53,
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x93, 0xcb, 0x4e, 0xc2, 0x40, 0x0e, 0x2d, 0x0a, 0x9e, 0x8d, 0xf4, 0x62, 0x11, 0x52, 0x04, 0xaf, 0x31, 0x7d, 0x68, 0xc0, 0x64,
0x14, 0x86, 0x81, 0x22, 0x95, 0x83, 0x18, 0x32, 0x0b, 0x33, 0x92, 0xa8, 0x4d, 0x57, 0x6c, 0x2c, 0x62, 0x66, 0x52, 0xbd, 0x79, 0xf2, 0xff, 0x96, 0xd9, 0xba, 0xa4, 0x93, 0xef, 0xd2, 0xdb, 0xbc,
0x09, 0xc4, 0x27, 0x10, 0xe3, 0x46, 0x62, 0x52, 0xe3, 0x03, 0xd4, 0x72, 0x82, 0x4d, 0x6c, 0xa7, 0xa5, 0xbf, 0xf7, 0x5b, 0x4a, 0x20, 0xe9, 0xfa, 0x46, 0x56, 0x35, 0xa6, 0x6d, 0xc7, 0x25, 0x27,
0xce, 0x4c, 0x51, 0x9f, 0xce, 0x57, 0x33, 0x73, 0x43, 0x81, 0xe2, 0x86, 0xdd, 0xb9, 0x7e, 0xff, 0xf3, 0xef, 0x3c, 0xad, 0xab, 0xb2, 0xe3, 0xa9, 0xed, 0xb3, 0xdf, 0x10, 0xed, 0xb0, 0x3b, 0x54,
0x99, 0xbf, 0x29, 0x4c, 0xf2, 0x2c, 0xe5, 0x6c, 0xbc, 0x64, 0xd7, 0x26, 0xe0, 0x55, 0x21, 0xb3, 0x25, 0x12, 0x02, 0xb3, 0xa6, 0xa8, 0x91, 0x4e, 0x96, 0x93, 0x37, 0x8f, 0x72, 0xfd, 0x26, 0x14,
0x1c, 0xc7, 0x02, 0xf9, 0x2a, 0x4b, 0x71, 0x5c, 0x72, 0x26, 0xd7, 0xd5, 0x48, 0x67, 0x64, 0xb0, 0xa2, 0x03, 0x76, 0xa2, 0xe2, 0x0d, 0x9d, 0xea, 0xb6, 0x2b, 0xc9, 0x53, 0x08, 0x05, 0xef, 0xbb,
0x64, 0x91, 0x9e, 0x8e, 0x6c, 0x3d, 0xfc, 0x00, 0xff, 0xc9, 0x2c, 0x10, 0x02, 0xed, 0x22, 0xc9, 0x12, 0x69, 0xa0, 0x07, 0xb6, 0x52, 0x28, 0x6d, 0x21, 0x7f, 0xd0, 0x99, 0x41, 0x51, 0x6f, 0xd5,
0x91, 0x36, 0x83, 0xe6, 0xa8, 0x1b, 0xeb, 0x98, 0x50, 0xf0, 0x57, 0xc8, 0x45, 0xc6, 0x0a, 0xda, 0xc3, 0x3f, 0x58, 0xd2, 0x07, 0xa6, 0xa7, 0xde, 0x6c, 0x07, 0xc9, 0x87, 0x0e, 0x0b, 0x89, 0x9f,
0xd2, 0x65, 0x97, 0x92, 0x33, 0xe8, 0x08, 0x56, 0xf1, 0x14, 0xa9, 0xa7, 0x1b, 0x36, 0x53, 0x94, 0x5b, 0x59, 0xf1, 0x46, 0xa8, 0x53, 0x25, 0xaf, 0xeb, 0xa2, 0xd9, 0xd3, 0xc9, 0x32, 0x50, 0xa7,
0x32, 0x91, 0xaf, 0xb4, 0x6d, 0x28, 0x2a, 0x56, 0x35, 0xfc, 0xc4, 0x94, 0x1e, 0x99, 0x9a, 0x8a, 0x6c, 0x49, 0xe6, 0x10, 0x60, 0x73, 0xa0, 0x53, 0xdd, 0x55, 0x4f, 0x75, 0x9c, 0xf7, 0xb2, 0xed,
0xc3, 0x39, 0xf8, 0x8f, 0xa5, 0xcc, 0x58, 0x21, 0x94, 0x48, 0xca, 0xf2, 0x3c, 0x29, 0x16, 0x56, 0xa5, 0x3b, 0x6e, 0x2a, 0xf6, 0xd7, 0x81, 0xe6, 0xf8, 0xab, 0x47, 0x21, 0xc9, 0x1a, 0x22, 0x61,
0xdb, 0xa5, 0x64, 0x00, 0x1e, 0x16, 0x2b, 0xda, 0x0a, 0xbc, 0x51, 0x37, 0x56, 0xa1, 0x92, 0x65, 0xe4, 0x69, 0x59, 0xf1, 0xea, 0x59, 0x3a, 0xb4, 0x20, 0xb5, 0xfa, 0x73, 0xb7, 0x49, 0xde, 0x43,
0x95, 0x2c, 0x2b, 0xe9, 0x64, 0x4d, 0x16, 0x7e, 0x41, 0xff, 0x96, 0x63, 0x22, 0x31, 0xc6, 0xf7, 0xc4, 0x0d, 0x29, 0x2d, 0x3a, 0x5e, 0xbd, 0xba, 0xfe, 0xd1, 0x05, 0xf7, 0xdc, 0xed, 0xb3, 0x39,
0x0a, 0x85, 0x24, 0x53, 0xf0, 0xad, 0x13, 0x1a, 0xda, 0x9b, 0x9c, 0x47, 0xdb, 0x8f, 0x8f, 0xec, 0x3c, 0x71, 0x04, 0x44, 0xcb, 0x1b, 0x81, 0xec, 0x35, 0xc0, 0x06, 0xe5, 0x99, 0x48, 0xbf, 0x9f,
0xcb, 0x63, 0x37, 0xa9, 0x96, 0x98, 0x39, 0x4a, 0x3f, 0xb7, 0x76, 0xc9, 0x5e, 0x1d, 0xbb, 0xc9, 0xec, 0xab, 0xde, 0x73, 0xbc, 0x7d, 0x59, 0xbc, 0x1b, 0xd2, 0x7a, 0x7e, 0x4d, 0xeb, 0x74, 0xea,
0x70, 0x00, 0xa7, 0x4e, 0x5a, 0x94, 0xac, 0x10, 0x18, 0xce, 0xa0, 0x3f, 0xc3, 0x37, 0x3c, 0xec, 0xc4, 0x29, 0x83, 0x58, 0x23, 0x1b, 0x42, 0xe4, 0x2d, 0x3c, 0xb4, 0x42, 0x85, 0x36, 0xfa, 0x5e,
0x18, 0xc5, 0x75, 0x94, 0x5f, 0xee, 0x73, 0xb9, 0x48, 0x0e, 0xe7, 0x3a, 0x8a, 0xe5, 0xf6, 0xa1, 0x4f, 0x8e, 0xab, 0x2c, 0x83, 0x24, 0xc3, 0x9f, 0x78, 0x9b, 0xb5, 0xca, 0x1f, 0x87, 0x62, 0xfd,
0xf7, 0x90, 0x09, 0x69, 0xa9, 0xe1, 0x1d, 0x9c, 0x98, 0xd4, 0xb4, 0xc9, 0x0d, 0x1c, 0xdb, 0x5d, 0xc9, 0x20, 0xf9, 0xd2, 0xee, 0x8b, 0xdb, 0x71, 0x1d, 0x8a, 0xc5, 0x4d, 0x20, 0xfe, 0x54, 0x09,
0x41, 0x9b, 0x81, 0xf7, 0xbf, 0xcc, 0x7a, 0x74, 0xf2, 0xdd, 0x02, 0x3f, 0x36, 0x5d, 0x32, 0x87, 0x67, 0x28, 0xfb, 0x08, 0x8f, 0x4d, 0x79, 0x93, 0x0b, 0xab, 0x7f, 0x01, 0x44, 0xb9, 0x99, 0x92,
0x8e, 0xf1, 0x88, 0x5c, 0xed, 0xae, 0x6e, 0x7c, 0xb8, 0x61, 0xb0, 0x7f, 0xc0, 0x9e, 0xdb, 0x50, 0x2d, 0x84, 0x26, 0x6b, 0x32, 0xfa, 0xff, 0xb0, 0xd7, 0x17, 0xcb, 0xf1, 0x05, 0x4b, 0xf7, 0x8e,
0x38, 0x63, 0x4d, 0x1d, 0x6e, 0xc3, 0xfa, 0x3a, 0xdc, 0x96, 0xab, 0x1a, 0x67, 0x1c, 0xa9, 0xc3, 0x64, 0x10, 0x6c, 0x50, 0x12, 0x7f, 0xa8, 0x0e, 0xe8, 0xc5, 0xc8, 0xf4, 0x88, 0xb2, 0x85, 0xd0,
0x6d, 0x38, 0x5e, 0x87, 0xdb, 0x32, 0xb3, 0x41, 0xee, 0xa1, 0xad, 0xfc, 0x23, 0x17, 0xbb, 0xb3, 0x18, 0xec, 0x23, 0x75, 0x11, 0xa0, 0x8f, 0xd4, 0x20, 0x1b, 0x0d, 0x67, 0x7c, 0xf5, 0xc1, 0x5d,
0x7f, 0x6c, 0x1e, 0x5e, 0xee, 0x6b, 0x3b, 0xd0, 0x4b, 0x47, 0xff, 0xb5, 0xd3, 0x9f, 0x00, 0x00, 0xe4, 0xe6, 0x83, 0x1b, 0x44, 0x72, 0x47, 0x36, 0x30, 0x53, 0x29, 0x10, 0x8f, 0x8c, 0xb3, 0xb0,
0x00, 0xff, 0xff, 0x69, 0x49, 0x0f, 0xe1, 0xeb, 0x03, 0x00, 0x00, 0x16, 0x2f, 0xc7, 0xc6, 0x0e, 0xe8, 0x5b, 0xa8, 0xbf, 0x67, 0xeb, 0xff, 0x01, 0x00, 0x00, 0xff,
} 0xff, 0x19, 0x21, 0xd3, 0xfb, 0xe0, 0x04, 0x00, 0x00,
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// RuntimeClient is the client API for Runtime service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type RuntimeClient interface {
Create(ctx context.Context, in *CreateRequest, opts ...grpc.CallOption) (*CreateResponse, error)
Delete(ctx context.Context, in *DeleteRequest, opts ...grpc.CallOption) (*DeleteResponse, error)
Update(ctx context.Context, in *UpdateRequest, opts ...grpc.CallOption) (*UpdateResponse, error)
List(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error)
}
type runtimeClient struct {
cc *grpc.ClientConn
}
func NewRuntimeClient(cc *grpc.ClientConn) RuntimeClient {
return &runtimeClient{cc}
}
func (c *runtimeClient) Create(ctx context.Context, in *CreateRequest, opts ...grpc.CallOption) (*CreateResponse, error) {
out := new(CreateResponse)
err := c.cc.Invoke(ctx, "/go.micro.runtime.Runtime/Create", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *runtimeClient) Delete(ctx context.Context, in *DeleteRequest, opts ...grpc.CallOption) (*DeleteResponse, error) {
out := new(DeleteResponse)
err := c.cc.Invoke(ctx, "/go.micro.runtime.Runtime/Delete", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *runtimeClient) Update(ctx context.Context, in *UpdateRequest, opts ...grpc.CallOption) (*UpdateResponse, error) {
out := new(UpdateResponse)
err := c.cc.Invoke(ctx, "/go.micro.runtime.Runtime/Update", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *runtimeClient) List(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error) {
out := new(ListResponse)
err := c.cc.Invoke(ctx, "/go.micro.runtime.Runtime/List", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// RuntimeServer is the server API for Runtime service.
type RuntimeServer interface {
Create(context.Context, *CreateRequest) (*CreateResponse, error)
Delete(context.Context, *DeleteRequest) (*DeleteResponse, error)
Update(context.Context, *UpdateRequest) (*UpdateResponse, error)
List(context.Context, *ListRequest) (*ListResponse, error)
}
func RegisterRuntimeServer(s *grpc.Server, srv RuntimeServer) {
s.RegisterService(&_Runtime_serviceDesc, srv)
}
func _Runtime_Create_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RuntimeServer).Create(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.runtime.Runtime/Create",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RuntimeServer).Create(ctx, req.(*CreateRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Runtime_Delete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DeleteRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RuntimeServer).Delete(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.runtime.Runtime/Delete",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RuntimeServer).Delete(ctx, req.(*DeleteRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Runtime_Update_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(UpdateRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RuntimeServer).Update(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.runtime.Runtime/Update",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RuntimeServer).Update(ctx, req.(*UpdateRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Runtime_List_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RuntimeServer).List(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.runtime.Runtime/List",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RuntimeServer).List(ctx, req.(*ListRequest))
}
return interceptor(ctx, in, info, handler)
}
var _Runtime_serviceDesc = grpc.ServiceDesc{
ServiceName: "go.micro.runtime.Runtime",
HandlerType: (*RuntimeServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Create",
Handler: _Runtime_Create_Handler,
},
{
MethodName: "Delete",
Handler: _Runtime_Delete_Handler,
},
{
MethodName: "Update",
Handler: _Runtime_Update_Handler,
},
{
MethodName: "List",
Handler: _Runtime_List_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "micro/go-micro/runtime/service/proto/runtime.proto",
} }

View File

@ -4,6 +4,7 @@ package go.micro.runtime;
service Runtime { service Runtime {
rpc Create(CreateRequest) returns (CreateResponse) {}; rpc Create(CreateRequest) returns (CreateResponse) {};
rpc Get(GetRequest) returns (GetResponse) {};
rpc Delete(DeleteRequest) returns (DeleteResponse) {}; rpc Delete(DeleteRequest) returns (DeleteResponse) {};
rpc Update(UpdateRequest) returns (UpdateResponse) {}; rpc Update(UpdateRequest) returns (UpdateResponse) {};
rpc List(ListRequest) returns (ListResponse) {}; rpc List(ListRequest) returns (ListResponse) {};
@ -22,9 +23,9 @@ message Service {
string exec = 5; string exec = 5;
} }
message Options { message CreateOptions {
// command to pass in // command to pass in
string command = 1; repeated string command = 1;
// environment to pass in // environment to pass in
repeated string env = 2; repeated string env = 2;
// output to send to // output to send to
@ -33,11 +34,25 @@ message Options {
message CreateRequest { message CreateRequest {
Service service = 1; Service service = 1;
Options options = 2; CreateOptions options = 2;
} }
message CreateResponse {} message CreateResponse {}
message GetOptions {
// version of the service
string version = 2;
}
message GetRequest {
string name = 1;
GetOptions options = 2;
}
message GetResponse {
repeated Service services = 1;
}
message DeleteRequest { message DeleteRequest {
Service service = 1; Service service = 1;
} }