diff --git a/go.mod b/go.mod index 41b82b77..c83307cc 100644 --- a/go.mod +++ b/go.mod @@ -32,9 +32,11 @@ require ( github.com/mholt/certmagic v0.8.3 github.com/micro/cli v0.2.0 github.com/micro/mdns v0.3.0 + github.com/miekg/dns v1.1.15 github.com/mitchellh/hashstructure v1.0.0 github.com/nats-io/nats.go v1.9.1 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/technoweenie/multipartstreamer v1.0.1 // indirect go.uber.org/zap v1.12.0 // indirect diff --git a/go.sum b/go.sum index 0ca438cd..33216a46 100644 --- a/go.sum +++ b/go.sum @@ -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/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/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/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= diff --git a/runtime/default.go b/runtime/default.go index 6fdb2953..18d8d7a9 100644 --- a/runtime/default.go +++ b/runtime/default.go @@ -21,6 +21,7 @@ type runtime struct { // indicates if we're running running bool // the service map + // TODO: track different versions of the same service services map[string]*service } @@ -175,6 +176,56 @@ func (r *runtime) Create(s *Service, opts ...CreateOption) error { 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 func (r *runtime) Delete(s *Service) error { r.Lock() @@ -199,26 +250,6 @@ func (r *runtime) Delete(s *Service) error { 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 func (r *runtime) List() ([]*Service, error) { var services []*Service diff --git a/runtime/kubernetes/client/api/api_test.go b/runtime/kubernetes/client/api/api_test.go new file mode 100644 index 00000000..474dff55 --- /dev/null +++ b/runtime/kubernetes/client/api/api_test.go @@ -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() + } +} diff --git a/runtime/kubernetes/client/api/request.go b/runtime/kubernetes/client/api/request.go index 20158806..bfb8b117 100644 --- a/runtime/kubernetes/client/api/request.go +++ b/runtime/kubernetes/client/api/request.go @@ -3,12 +3,12 @@ package api import ( "bytes" "encoding/json" + "errors" "fmt" "io" "net/http" "net/url" - "github.com/micro/go-micro/runtime/kubernetes/client/watch" "github.com/micro/go-micro/util/log" ) @@ -33,7 +33,6 @@ type Request struct { type Params struct { LabelSelector map[string]string Annotations map[string]string - Watch bool } // verb sets method @@ -58,9 +57,8 @@ func (r *Request) Put() *Request { } // Patch request -// https://github.com/kubernetes/kubernetes/blob/master/docs/devel/api-conventions.md#patch-operations func (r *Request) Patch() *Request { - return r.verb("PATCH").SetHeader("Content-Type", "application/strategic-merge-patch+json") + return r.verb("PATCH") } // Delete request @@ -87,15 +85,33 @@ func (r *Request) Name(s string) *Request { return r } -// Body pass in a body to set, this is for POST, PUT -// and PATCH requests +// Body pass in a body to set, this is for POST, PUT and PATCH requests func (r *Request) Body(in interface{}) *Request { b := new(bytes.Buffer) - if err := json.NewEncoder(b).Encode(&in); err != nil { + // 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 { + r.err = err + return r + } + 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("Patch body: %v", b) + + log.Debugf("Request body: %v", b) r.body = b return r } @@ -120,12 +136,12 @@ func (r *Request) SetHeader(key, value string) *Request { func (r *Request) request() (*http.Request, error) { var url string switch r.resource { - case "pods": + case "pod", "service", "endpoint": // /api/v1/namespaces/{namespace}/pods - url = fmt.Sprintf("%s/api/v1/namespaces/%s/%s/", r.host, r.namespace, r.resource) - case "deployments": + url = fmt.Sprintf("%s/api/v1/namespaces/%s/%ss/", r.host, r.namespace, r.resource) + case "deployment": // /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 @@ -179,24 +195,6 @@ func (r *Request) Do() *Response { 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 ... type Options struct { Host string diff --git a/runtime/kubernetes/client/api/response.go b/runtime/kubernetes/client/api/response.go index 8700115f..ceed48db 100644 --- a/runtime/kubernetes/client/api/response.go +++ b/runtime/kubernetes/client/api/response.go @@ -11,9 +11,9 @@ import ( // Errors ... var ( - ErrNotFound = errors.New("kubernetes: not found") + ErrNotFound = errors.New("kubernetes: resource not found") 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 @@ -89,6 +89,7 @@ func newResponse(res *http.Response, err error) *Response { log.Log("kubernetes: request failed with body:") log.Log(string(b)) } - r.err = ErrOther + r.err = ErrUnknown + return r } diff --git a/runtime/kubernetes/client/client.go b/runtime/kubernetes/client/client.go index d201604e..bfebad9b 100644 --- a/runtime/kubernetes/client/client.go +++ b/runtime/kubernetes/client/client.go @@ -1,6 +1,7 @@ package client import ( + "bytes" "crypto/tls" "errors" "io/ioutil" @@ -13,6 +14,7 @@ import ( ) 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") @@ -23,9 +25,7 @@ type client struct { opts *api.Options } -// NewClientInCluster should work similarily to the official api -// NewInClient by setting up a client configuration for use within -// a k8s pod. +// 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") @@ -34,7 +34,7 @@ func NewClientInCluster() *client { log.Fatal(err) } 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")) @@ -90,26 +90,72 @@ func detectNamespace() (string, error) { } } -// UpdateDeployment patches kubernetes deployment with metadata provided in body -func (c *client) UpdateDeployment(name string, body interface{}) error { +// 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). - Patch(). - Resource("deployments"). - Name(name). - Body(body). + Post(). + SetHeader("Content-Type", "application/yaml"). + Resource(r.Kind). + Body(b). Do(). Error() } -// ListDeployments lists all kubernetes deployments with given labels -func (c *client) ListDeployments(labels map[string]string) (*DeploymentList, error) { - var deployments DeploymentList - err := api.NewRequest(c.opts). +// 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("deployments"). + Resource(r.Kind). Params(&api.Params{LabelSelector: labels}). Do(). - Into(&deployments) - - return &deployments, err + 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).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) } diff --git a/runtime/kubernetes/client/kubernetes.go b/runtime/kubernetes/client/kubernetes.go index afed5ec2..4c378ea1 100644 --- a/runtime/kubernetes/client/kubernetes.go +++ b/runtime/kubernetes/client/kubernetes.go @@ -1,43 +1,132 @@ +// Package client provides an implementation of a restricted subset of kubernetes API 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 type Kubernetes interface { - // UpdateDeployment patches deployment annotations with new metadata - UpdateDeployment(string, interface{}) error - // ListDeployments lists all micro deployments - ListDeployments(labels map[string]string) (*DeploymentList, error) + // Create creates new API resource + Create(*Resource) error + // Get queries API resrouces + 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 -type Template struct { - Metadata *Metadata `json:"metadata,omitempty"` +// DefaultService returns default micro kubernetes service definition +func DefaultService(name, version string) *Service { + 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 -type Spec struct { - Template *Template `json:"template,omitempty"` -} +// DefaultService returns default micro kubernetes deployment definition +func DefaultDeployment(name, version string) *Deployment { + Labels := map[string]string{ + "name": name, + "version": version, + "micro": "service", + } -// Metadata defines api request metadata -type Metadata struct { - Name string `json:"name,omitempty"` - Labels map[string]string `json:"labels,omitempty"` - Annotations map[string]string `json:"annotations,omitempty"` -} + // API deployment object name joins name and version over "=" + depName := strings.Join([]string{name, version}, "-") -// DeploymentList -type DeploymentList struct { - Items []Deployment `json:"items"` -} + Metadata := &Metadata{ + Name: depName, + Namespace: "default", + Version: version, + Labels: Labels, + } -// Deployment is Kubernetes deployment -type Deployment struct { - Metadata *Metadata `json:"metadata"` - Status *Status `json:"status"` -} + // TODO: we need to figure out this version stuff + // might be worth adding Build to runtime.Service + buildTime, err := strconv.ParseInt(version, 10, 64) + 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 -type Status struct { - Replicas int `json:"replicas"` - AvailableReplicas int `json:"availablereplicas"` + // TODO: change the image name here + Spec := &DeploymentSpec{ + Replicas: 1, + 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, + } } diff --git a/runtime/kubernetes/client/templates.go b/runtime/kubernetes/client/templates.go new file mode 100644 index 00000000..5292ac4f --- /dev/null +++ b/runtime/kubernetes/client/templates.go @@ -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 }} +` diff --git a/runtime/kubernetes/client/types.go b/runtime/kubernetes/client/types.go new file mode 100644 index 00000000..882b826e --- /dev/null +++ b/runtime/kubernetes/client/types.go @@ -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"` +} diff --git a/runtime/kubernetes/client/utils.go b/runtime/kubernetes/client/utils.go index ec250aca..b70ca44f 100644 --- a/runtime/kubernetes/client/utils.go +++ b/runtime/kubernetes/client/utils.go @@ -5,9 +5,22 @@ import ( "encoding/pem" "errors" "fmt" + "io" "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 // https://github.com/kubernetes/kubernetes/blob/7a725418af4661067b56506faabc2d44c6d7703a/pkg/util/crypto/crypto.go diff --git a/runtime/kubernetes/client/utils_test.go b/runtime/kubernetes/client/utils_test.go new file mode 100644 index 00000000..0e69c012 --- /dev/null +++ b/runtime/kubernetes/client/utils_test.go @@ -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) + } +} diff --git a/runtime/kubernetes/client/watch/body.go b/runtime/kubernetes/client/watch/body.go deleted file mode 100644 index 3eaa6d43..00000000 --- a/runtime/kubernetes/client/watch/body.go +++ /dev/null @@ -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 -} diff --git a/runtime/kubernetes/client/watch/watch.go b/runtime/kubernetes/client/watch/watch.go deleted file mode 100644 index f8a178b8..00000000 --- a/runtime/kubernetes/client/watch/watch.go +++ /dev/null @@ -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"` -} diff --git a/runtime/kubernetes/client/watch/watch_test.go b/runtime/kubernetes/client/watch/watch_test.go deleted file mode 100644 index ac34141c..00000000 --- a/runtime/kubernetes/client/watch/watch_test.go +++ /dev/null @@ -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) -} diff --git a/runtime/kubernetes/kubernetes.go b/runtime/kubernetes/kubernetes.go index 27369ac9..1939c46a 100644 --- a/runtime/kubernetes/kubernetes.go +++ b/runtime/kubernetes/kubernetes.go @@ -14,18 +14,31 @@ import ( "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 { sync.RWMutex // options configure runtime options runtime.Options // indicates if we're running running bool - // used to start new services - start chan *runtime.Service + // task queue for kubernetes services + queue chan *task // used to stop the runtime closed chan bool - // service tracks deployed services - services map[string]*runtime.Service // client is kubernetes client client client.Kubernetes } @@ -44,11 +57,10 @@ func NewRuntime(opts ...runtime.Option) runtime.Runtime { client := client.NewClientInCluster() return &kubernetes{ - options: options, - closed: make(chan bool), - start: make(chan *runtime.Service, 128), - services: make(map[string]*runtime.Service), - client: client, + options: options, + closed: make(chan bool), + queue: make(chan *task, 128), + client: client, } } @@ -64,33 +76,109 @@ func (k *kubernetes) Init(opts ...runtime.Option) error { return nil } -// Registers a service +// Creates a service func (k *kubernetes) Create(s *runtime.Service, opts ...runtime.CreateOption) error { k.Lock() 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 for _, o := range opts { o(&options) } - // save service - k.services[s.Name] = s + svcName := s.Name + 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 - 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 } @@ -100,61 +188,37 @@ func (k *kubernetes) Delete(s *runtime.Service) error { k.Lock() defer k.Unlock() - // TODO: - // * delete service - // * delete dpeloyment + // create new kubernetes micro service + service := newService(s, runtime.CreateOptions{}) - // NOTE: we are tracking this in memory for now - if s, ok := k.services[s.Name]; ok { - delete(k.services, s.Name) - return nil + log.Debugf("Runtime queueing service %s for delete action", service.Name) + + // queue service for removal + k.queue <- &task{ + action: stop, + service: service, } 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 func (k *kubernetes) List() ([]*runtime.Service, error) { - labels := map[string]string{ - "micro": "service", + serviceList := new(client.ServiceList) + r := &client.Resource{ + Kind: "service", + Value: serviceList, } - // list all micro core deployments - deployments, err := k.client.ListDeployments(labels) - if err != nil { + + if err := k.client.List(r); err != nil { 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"]) if err != nil { 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 { select { case <-t.C: - // check running services - services, err := k.List() - if err != nil { - log.Debugf("Runtime failed listing running services: %v", err) - continue + // TODO: figure out what to do here + // - do we even need the ticker for k8s services? + case task := <-k.queue: + 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 + } + case stop: + log.Debugf("Runtime stopping service: %s", task.service.Name) + if err := task.service.Stop(k.client); err != nil { + log.Debugf("Runtime failed to stop service %s: %v", task.service.Name, err) + 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) } - // TODO: for now we just log the running services - // * make sure all core deployments exist - // * make sure all core services are exposed - for _, service := range services { - log.Debugf("Runtime found running service: %v", service) - } - 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: // NOTE: we only handle Update events for now 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) continue } - buildTime := 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() + unixTimeUTC := time.Unix(updateTimeStamp, 0) if len(event.Service) > 0 { - service, ok := k.services[event.Service] - if !ok { - log.Debugf("Runtime unknown service: %s", event.Service) - k.Unlock() + s := &runtime.Service{ + Name: event.Service, + Version: event.Version, + } + // 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 } - 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: log.Debugf("Runtime stopped") diff --git a/runtime/kubernetes/service.go b/runtime/kubernetes/service.go new file mode 100644 index 00000000..0c02d1b9 --- /dev/null +++ b/runtime/kubernetes/service.go @@ -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 +} diff --git a/runtime/options.go b/runtime/options.go index 82284b36..e031656d 100644 --- a/runtime/options.go +++ b/runtime/options.go @@ -54,3 +54,18 @@ func WithOutput(out io.Writer) CreateOption { 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 + } +} diff --git a/runtime/runtime.go b/runtime/runtime.go index ca5762bb..36836392 100644 --- a/runtime/runtime.go +++ b/runtime/runtime.go @@ -12,17 +12,19 @@ var ( type Runtime interface { // Init initializes runtime Init(...Option) error - // Registers a service + // Create registers a service Create(*Service, ...CreateOption) error - // Remove a service - Delete(*Service) error + // Get returns service or fails with error + Get(string, ...GetOption) ([]*Service, error) // Update the service in place Update(*Service) error + // Remove a service + Delete(*Service) error // List the managed services List() ([]*Service, error) - // starts the runtime + // Start starts the runtime Start() error - // Shutdown the runtime + // Stop shuts down the runtime Stop() error } diff --git a/runtime/service/handler/handler.go b/runtime/service/handler/handler.go index 8f9abc38..aecef40b 100644 --- a/runtime/service/handler/handler.go +++ b/runtime/service/handler/handler.go @@ -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 { if req.Service == nil { 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) - err := r.Runtime.Create(service) + err := r.Runtime.Create(service, options...) if err != nil { 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 } +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 { if req.Service == nil { return errors.BadRequest("go.micro.runtime", "blank service") diff --git a/runtime/service/proto/runtime.micro.go b/runtime/service/proto/runtime.micro.go index 86c00c21..b5a3c4f5 100644 --- a/runtime/service/proto/runtime.micro.go +++ b/runtime/service/proto/runtime.micro.go @@ -1,5 +1,5 @@ // 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 @@ -35,6 +35,7 @@ var _ server.Option type RuntimeService interface { 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) Update(ctx context.Context, in *UpdateRequest, opts ...client.CallOption) (*UpdateResponse, 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 } +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) { req := c.c.NewRequest(c.name, "Runtime.Delete", in) out := new(DeleteResponse) @@ -102,6 +113,7 @@ func (c *runtimeService) List(ctx context.Context, in *ListRequest, opts ...clie type RuntimeHandler interface { Create(context.Context, *CreateRequest, *CreateResponse) error + Get(context.Context, *GetRequest, *GetResponse) error Delete(context.Context, *DeleteRequest, *DeleteResponse) error Update(context.Context, *UpdateRequest, *UpdateResponse) 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 { type runtime interface { 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 Update(ctx context.Context, in *UpdateRequest, out *UpdateResponse) 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) } +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 { return h.RuntimeHandler.Delete(ctx, in, out) } diff --git a/runtime/service/proto/runtime.pb.go b/runtime/service/proto/runtime.pb.go index 2ff2cae8..b34df91d 100644 --- a/runtime/service/proto/runtime.pb.go +++ b/runtime/service/proto/runtime.pb.go @@ -1,13 +1,11 @@ // 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 import ( - context "context" fmt "fmt" proto "github.com/golang/protobuf/proto" - grpc "google.golang.org/grpc" math "math" ) @@ -42,7 +40,7 @@ func (m *Service) Reset() { *m = Service{} } func (m *Service) String() string { return proto.CompactTextString(m) } func (*Service) ProtoMessage() {} func (*Service) Descriptor() ([]byte, []int) { - return fileDescriptor_4bc91a8efec81434, []int{0} + return fileDescriptor_86e2dd377c869464, []int{0} } func (m *Service) XXX_Unmarshal(b []byte) error { @@ -98,9 +96,9 @@ func (m *Service) GetExec() string { return "" } -type Options struct { +type CreateOptions struct { // 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 Env []string `protobuf:"bytes,2,rep,name=env,proto3" json:"env,omitempty"` // output to send to @@ -110,46 +108,46 @@ type Options struct { XXX_sizecache int32 `json:"-"` } -func (m *Options) Reset() { *m = Options{} } -func (m *Options) String() string { return proto.CompactTextString(m) } -func (*Options) ProtoMessage() {} -func (*Options) Descriptor() ([]byte, []int) { - return fileDescriptor_4bc91a8efec81434, []int{1} +func (m *CreateOptions) Reset() { *m = CreateOptions{} } +func (m *CreateOptions) String() string { return proto.CompactTextString(m) } +func (*CreateOptions) ProtoMessage() {} +func (*CreateOptions) Descriptor() ([]byte, []int) { + return fileDescriptor_86e2dd377c869464, []int{1} } -func (m *Options) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Options.Unmarshal(m, b) +func (m *CreateOptions) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_CreateOptions.Unmarshal(m, b) } -func (m *Options) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Options.Marshal(b, m, deterministic) +func (m *CreateOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_CreateOptions.Marshal(b, m, deterministic) } -func (m *Options) XXX_Merge(src proto.Message) { - xxx_messageInfo_Options.Merge(m, src) +func (m *CreateOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_CreateOptions.Merge(m, src) } -func (m *Options) XXX_Size() int { - return xxx_messageInfo_Options.Size(m) +func (m *CreateOptions) XXX_Size() int { + return xxx_messageInfo_CreateOptions.Size(m) } -func (m *Options) XXX_DiscardUnknown() { - xxx_messageInfo_Options.DiscardUnknown(m) +func (m *CreateOptions) XXX_DiscardUnknown() { + 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 { return m.Command } - return "" + return nil } -func (m *Options) GetEnv() []string { +func (m *CreateOptions) GetEnv() []string { if m != nil { return m.Env } return nil } -func (m *Options) GetOutput() string { +func (m *CreateOptions) GetOutput() string { if m != nil { return m.Output } @@ -157,18 +155,18 @@ func (m *Options) GetOutput() string { } type CreateRequest struct { - Service *Service `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"` - Options *Options `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Service *Service `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"` + Options *CreateOptions `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *CreateRequest) Reset() { *m = CreateRequest{} } func (m *CreateRequest) String() string { return proto.CompactTextString(m) } func (*CreateRequest) ProtoMessage() {} func (*CreateRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_4bc91a8efec81434, []int{2} + return fileDescriptor_86e2dd377c869464, []int{2} } func (m *CreateRequest) XXX_Unmarshal(b []byte) error { @@ -196,7 +194,7 @@ func (m *CreateRequest) GetService() *Service { return nil } -func (m *CreateRequest) GetOptions() *Options { +func (m *CreateRequest) GetOptions() *CreateOptions { if m != nil { return m.Options } @@ -213,7 +211,7 @@ func (m *CreateResponse) Reset() { *m = CreateResponse{} } func (m *CreateResponse) String() string { return proto.CompactTextString(m) } func (*CreateResponse) ProtoMessage() {} func (*CreateResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_4bc91a8efec81434, []int{3} + return fileDescriptor_86e2dd377c869464, []int{3} } func (m *CreateResponse) XXX_Unmarshal(b []byte) error { @@ -234,6 +232,132 @@ func (m *CreateResponse) XXX_DiscardUnknown() { 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 { Service *Service `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` @@ -245,7 +369,7 @@ func (m *DeleteRequest) Reset() { *m = DeleteRequest{} } func (m *DeleteRequest) String() string { return proto.CompactTextString(m) } func (*DeleteRequest) ProtoMessage() {} func (*DeleteRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_4bc91a8efec81434, []int{4} + return fileDescriptor_86e2dd377c869464, []int{7} } 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 (*DeleteResponse) ProtoMessage() {} func (*DeleteResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_4bc91a8efec81434, []int{5} + return fileDescriptor_86e2dd377c869464, []int{8} } 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 (*UpdateRequest) ProtoMessage() {} func (*UpdateRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_4bc91a8efec81434, []int{6} + return fileDescriptor_86e2dd377c869464, []int{9} } 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 (*UpdateResponse) ProtoMessage() {} func (*UpdateResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_4bc91a8efec81434, []int{7} + return fileDescriptor_86e2dd377c869464, []int{10} } 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 (*ListRequest) ProtoMessage() {} func (*ListRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_4bc91a8efec81434, []int{8} + return fileDescriptor_86e2dd377c869464, []int{11} } 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 (*ListResponse) ProtoMessage() {} func (*ListResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_4bc91a8efec81434, []int{9} + return fileDescriptor_86e2dd377c869464, []int{12} } func (m *ListResponse) XXX_Unmarshal(b []byte) error { @@ -446,9 +570,12 @@ func (m *ListResponse) GetServices() []*Service { func init() { 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((*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((*DeleteResponse)(nil), "go.micro.runtime.DeleteResponse") proto.RegisterType((*UpdateRequest)(nil), "go.micro.runtime.UpdateRequest") @@ -457,205 +584,35 @@ func init() { proto.RegisterType((*ListResponse)(nil), "go.micro.runtime.ListResponse") } -func init() { - proto.RegisterFile("micro/go-micro/runtime/service/proto/runtime.proto", fileDescriptor_4bc91a8efec81434) -} - -var fileDescriptor_4bc91a8efec81434 = []byte{ - // 379 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x93, 0xcb, 0x4e, 0xc2, 0x40, - 0x14, 0x86, 0x81, 0x22, 0x95, 0x83, 0x18, 0x32, 0x0b, 0x33, 0x92, 0xa8, 0x4d, 0x57, 0x6c, 0x2c, - 0x09, 0xc4, 0x27, 0x10, 0xe3, 0x46, 0x62, 0x52, 0xe3, 0x03, 0xd4, 0x72, 0x82, 0x4d, 0x6c, 0xa7, - 0xce, 0x4c, 0x51, 0x9f, 0xce, 0x57, 0x33, 0x73, 0x43, 0x81, 0xe2, 0x86, 0xdd, 0xb9, 0x7e, 0xff, - 0x99, 0xbf, 0x29, 0x4c, 0xf2, 0x2c, 0xe5, 0x6c, 0xbc, 0x64, 0xd7, 0x26, 0xe0, 0x55, 0x21, 0xb3, - 0x1c, 0xc7, 0x02, 0xf9, 0x2a, 0x4b, 0x71, 0x5c, 0x72, 0x26, 0xd7, 0xd5, 0x48, 0x67, 0x64, 0xb0, - 0x64, 0x91, 0x9e, 0x8e, 0x6c, 0x3d, 0xfc, 0x00, 0xff, 0xc9, 0x2c, 0x10, 0x02, 0xed, 0x22, 0xc9, - 0x91, 0x36, 0x83, 0xe6, 0xa8, 0x1b, 0xeb, 0x98, 0x50, 0xf0, 0x57, 0xc8, 0x45, 0xc6, 0x0a, 0xda, - 0xd2, 0x65, 0x97, 0x92, 0x33, 0xe8, 0x08, 0x56, 0xf1, 0x14, 0xa9, 0xa7, 0x1b, 0x36, 0x53, 0x94, - 0x32, 0x91, 0xaf, 0xb4, 0x6d, 0x28, 0x2a, 0x56, 0x35, 0xfc, 0xc4, 0x94, 0x1e, 0x99, 0x9a, 0x8a, - 0xc3, 0x39, 0xf8, 0x8f, 0xa5, 0xcc, 0x58, 0x21, 0x94, 0x48, 0xca, 0xf2, 0x3c, 0x29, 0x16, 0x56, - 0xdb, 0xa5, 0x64, 0x00, 0x1e, 0x16, 0x2b, 0xda, 0x0a, 0xbc, 0x51, 0x37, 0x56, 0xa1, 0x92, 0x65, - 0x95, 0x2c, 0x2b, 0xe9, 0x64, 0x4d, 0x16, 0x7e, 0x41, 0xff, 0x96, 0x63, 0x22, 0x31, 0xc6, 0xf7, - 0x0a, 0x85, 0x24, 0x53, 0xf0, 0xad, 0x13, 0x1a, 0xda, 0x9b, 0x9c, 0x47, 0xdb, 0x8f, 0x8f, 0xec, - 0xcb, 0x63, 0x37, 0xa9, 0x96, 0x98, 0x39, 0x4a, 0x3f, 0xb7, 0x76, 0xc9, 0x5e, 0x1d, 0xbb, 0xc9, - 0x70, 0x00, 0xa7, 0x4e, 0x5a, 0x94, 0xac, 0x10, 0x18, 0xce, 0xa0, 0x3f, 0xc3, 0x37, 0x3c, 0xec, - 0x18, 0xc5, 0x75, 0x94, 0x5f, 0xee, 0x73, 0xb9, 0x48, 0x0e, 0xe7, 0x3a, 0x8a, 0xe5, 0xf6, 0xa1, - 0xf7, 0x90, 0x09, 0x69, 0xa9, 0xe1, 0x1d, 0x9c, 0x98, 0xd4, 0xb4, 0xc9, 0x0d, 0x1c, 0xdb, 0x5d, - 0x41, 0x9b, 0x81, 0xf7, 0xbf, 0xcc, 0x7a, 0x74, 0xf2, 0xdd, 0x02, 0x3f, 0x36, 0x5d, 0x32, 0x87, - 0x8e, 0xf1, 0x88, 0x5c, 0xed, 0xae, 0x6e, 0x7c, 0xb8, 0x61, 0xb0, 0x7f, 0xc0, 0x9e, 0xdb, 0x50, - 0x38, 0x63, 0x4d, 0x1d, 0x6e, 0xc3, 0xfa, 0x3a, 0xdc, 0x96, 0xab, 0x1a, 0x67, 0x1c, 0xa9, 0xc3, - 0x6d, 0x38, 0x5e, 0x87, 0xdb, 0x32, 0xb3, 0x41, 0xee, 0xa1, 0xad, 0xfc, 0x23, 0x17, 0xbb, 0xb3, - 0x7f, 0x6c, 0x1e, 0x5e, 0xee, 0x6b, 0x3b, 0xd0, 0x4b, 0x47, 0xff, 0xb5, 0xd3, 0x9f, 0x00, 0x00, - 0x00, 0xff, 0xff, 0x69, 0x49, 0x0f, 0xe1, 0xeb, 0x03, 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", +func init() { proto.RegisterFile("runtime.proto", fileDescriptor_86e2dd377c869464) } + +var fileDescriptor_86e2dd377c869464 = []byte{ + // 425 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x54, 0xc9, 0xcb, 0xd3, 0x40, + 0x14, 0xff, 0xda, 0xd4, 0x44, 0x5f, 0x8c, 0x94, 0x39, 0xc8, 0x58, 0x5c, 0xca, 0x1c, 0xc4, 0x53, + 0x0e, 0x2d, 0x0a, 0x9e, 0x8d, 0xf4, 0x62, 0x11, 0x52, 0x04, 0xaf, 0x31, 0x7d, 0x68, 0xc0, 0x64, + 0x62, 0x66, 0x52, 0xbd, 0x79, 0xf2, 0xff, 0x96, 0xd9, 0xba, 0xa4, 0x93, 0xef, 0xd2, 0xdb, 0xbc, + 0xa5, 0xbf, 0xf7, 0x5b, 0x4a, 0x20, 0xe9, 0xfa, 0x46, 0x56, 0x35, 0xa6, 0x6d, 0xc7, 0x25, 0x27, + 0xf3, 0xef, 0x3c, 0xad, 0xab, 0xb2, 0xe3, 0xa9, 0xed, 0xb3, 0xdf, 0x10, 0xed, 0xb0, 0x3b, 0x54, + 0x25, 0x12, 0x02, 0xb3, 0xa6, 0xa8, 0x91, 0x4e, 0x96, 0x93, 0x37, 0x8f, 0x72, 0xfd, 0x26, 0x14, + 0xa2, 0x03, 0x76, 0xa2, 0xe2, 0x0d, 0x9d, 0xea, 0xb6, 0x2b, 0xc9, 0x53, 0x08, 0x05, 0xef, 0xbb, + 0x12, 0x69, 0xa0, 0x07, 0xb6, 0x52, 0x28, 0x6d, 0x21, 0x7f, 0xd0, 0x99, 0x41, 0x51, 0x6f, 0xd5, + 0xc3, 0x3f, 0x58, 0xd2, 0x07, 0xa6, 0xa7, 0xde, 0x6c, 0x07, 0xc9, 0x87, 0x0e, 0x0b, 0x89, 0x9f, + 0x5b, 0x59, 0xf1, 0x46, 0xa8, 0x53, 0x25, 0xaf, 0xeb, 0xa2, 0xd9, 0xd3, 0xc9, 0x32, 0x50, 0xa7, + 0x6c, 0x49, 0xe6, 0x10, 0x60, 0x73, 0xa0, 0x53, 0xdd, 0x55, 0x4f, 0x75, 0x9c, 0xf7, 0xb2, 0xed, + 0xa5, 0x3b, 0x6e, 0x2a, 0xf6, 0xd7, 0x81, 0xe6, 0xf8, 0xab, 0x47, 0x21, 0xc9, 0x1a, 0x22, 0x61, + 0xe4, 0x69, 0x59, 0xf1, 0xea, 0x59, 0x3a, 0xb4, 0x20, 0xb5, 0xfa, 0x73, 0xb7, 0x49, 0xde, 0x43, + 0xc4, 0x0d, 0x29, 0x2d, 0x3a, 0x5e, 0xbd, 0xba, 0xfe, 0xd1, 0x05, 0xf7, 0xdc, 0xed, 0xb3, 0x39, + 0x3c, 0x71, 0x04, 0x44, 0xcb, 0x1b, 0x81, 0xec, 0x35, 0xc0, 0x06, 0xe5, 0x99, 0x48, 0xbf, 0x9f, + 0xec, 0xab, 0xde, 0x73, 0xbc, 0x7d, 0x59, 0xbc, 0x1b, 0xd2, 0x7a, 0x7e, 0x4d, 0xeb, 0x74, 0xea, + 0xc4, 0x29, 0x83, 0x58, 0x23, 0x1b, 0x42, 0xe4, 0x2d, 0x3c, 0xb4, 0x42, 0x85, 0x36, 0xfa, 0x5e, + 0x4f, 0x8e, 0xab, 0x2c, 0x83, 0x24, 0xc3, 0x9f, 0x78, 0x9b, 0xb5, 0xca, 0x1f, 0x87, 0x62, 0xfd, + 0xc9, 0x20, 0xf9, 0xd2, 0xee, 0x8b, 0xdb, 0x71, 0x1d, 0x8a, 0xc5, 0x4d, 0x20, 0xfe, 0x54, 0x09, + 0x67, 0x28, 0xfb, 0x08, 0x8f, 0x4d, 0x79, 0x93, 0x0b, 0xab, 0x7f, 0x01, 0x44, 0xb9, 0x99, 0x92, + 0x2d, 0x84, 0x26, 0x6b, 0x32, 0xfa, 0xff, 0xb0, 0xd7, 0x17, 0xcb, 0xf1, 0x05, 0x4b, 0xf7, 0x8e, + 0x64, 0x10, 0x6c, 0x50, 0x12, 0x7f, 0xa8, 0x0e, 0xe8, 0xc5, 0xc8, 0xf4, 0x88, 0xb2, 0x85, 0xd0, + 0x18, 0xec, 0x23, 0x75, 0x11, 0xa0, 0x8f, 0xd4, 0x20, 0x1b, 0x0d, 0x67, 0x7c, 0xf5, 0xc1, 0x5d, + 0xe4, 0xe6, 0x83, 0x1b, 0x44, 0x72, 0x47, 0x36, 0x30, 0x53, 0x29, 0x10, 0x8f, 0x8c, 0xb3, 0xb0, + 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, } diff --git a/runtime/service/proto/runtime.proto b/runtime/service/proto/runtime.proto index eb5bb128..4a6b1046 100644 --- a/runtime/service/proto/runtime.proto +++ b/runtime/service/proto/runtime.proto @@ -4,6 +4,7 @@ package go.micro.runtime; service Runtime { rpc Create(CreateRequest) returns (CreateResponse) {}; + rpc Get(GetRequest) returns (GetResponse) {}; rpc Delete(DeleteRequest) returns (DeleteResponse) {}; rpc Update(UpdateRequest) returns (UpdateResponse) {}; rpc List(ListRequest) returns (ListResponse) {}; @@ -22,9 +23,9 @@ message Service { string exec = 5; } -message Options { +message CreateOptions { // command to pass in - string command = 1; + repeated string command = 1; // environment to pass in repeated string env = 2; // output to send to @@ -33,11 +34,25 @@ message Options { message CreateRequest { Service service = 1; - Options options = 2; + CreateOptions options = 2; } 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 { Service service = 1; }