Update the runtime for k8s name formatting and move Get to Read endpoint (#978)
* Update the runtime for k8s name formatting and move Get to Read endpoint * strip regex validation
This commit is contained in:
@@ -2,7 +2,6 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -18,8 +17,6 @@ const (
|
||||
var (
|
||||
// DefaultImage is default micro image
|
||||
DefaultImage = "micro/go-micro"
|
||||
// ServiceRegexp is used to validate service name
|
||||
ServiceRegexp = regexp.MustCompile("^" + dns1123LabelFmt + "$")
|
||||
)
|
||||
|
||||
// Kubernetes client
|
||||
@@ -36,9 +33,9 @@ type Kubernetes interface {
|
||||
List(*Resource) error
|
||||
}
|
||||
|
||||
// DefaultService returns default micro kubernetes service definition
|
||||
func DefaultService(name, version string) *Service {
|
||||
log.Debugf("kubernetes default service: name: %s, version: %s", name, version)
|
||||
// NewService returns default micro kubernetes service definition
|
||||
func NewService(name, version string) *Service {
|
||||
log.Tracef("kubernetes default service: name: %s, version: %s", name, version)
|
||||
|
||||
Labels := map[string]string{
|
||||
"name": name,
|
||||
@@ -73,9 +70,9 @@ func DefaultService(name, version string) *Service {
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultService returns default micro kubernetes deployment definition
|
||||
func DefaultDeployment(name, version, source string) *Deployment {
|
||||
log.Debugf("kubernetes default deployment: name: %s, version: %s, source: %s", name, version, source)
|
||||
// NewService returns default micro kubernetes deployment definition
|
||||
func NewDeployment(name, version string) *Deployment {
|
||||
log.Tracef("kubernetes default deployment: name: %s, version: %s", name, version)
|
||||
|
||||
Labels := map[string]string{
|
||||
"name": name,
|
||||
@@ -90,15 +87,11 @@ func DefaultDeployment(name, version, source string) *Deployment {
|
||||
}
|
||||
|
||||
Metadata := &Metadata{
|
||||
Name: depName,
|
||||
Namespace: "default",
|
||||
Version: version,
|
||||
Labels: Labels,
|
||||
Annotations: map[string]string{
|
||||
"source": source,
|
||||
"owner": "micro",
|
||||
"group": "micro",
|
||||
},
|
||||
Name: depName,
|
||||
Namespace: "default",
|
||||
Version: version,
|
||||
Labels: Labels,
|
||||
Annotations: map[string]string{},
|
||||
}
|
||||
|
||||
// TODO: we need to figure out this version stuff
|
||||
@@ -108,7 +101,7 @@ func DefaultDeployment(name, version, source string) *Deployment {
|
||||
buildUnixTimeUTC := time.Unix(buildTime, 0)
|
||||
Metadata.Annotations["build"] = buildUnixTimeUTC.Format(time.RFC3339)
|
||||
} else {
|
||||
log.Debugf("could not parse build: %v", err)
|
||||
log.Tracef("could not parse build: %v", err)
|
||||
}
|
||||
|
||||
// enable go modules by default
|
||||
@@ -129,7 +122,7 @@ func DefaultDeployment(name, version, source string) *Deployment {
|
||||
Name: name,
|
||||
Image: DefaultImage,
|
||||
Env: []EnvVar{env},
|
||||
Command: []string{"go", "run", source},
|
||||
Command: []string{"go", "run", "main.go"},
|
||||
Ports: []ContainerPort{{
|
||||
Name: name + "-port",
|
||||
ContainerPort: 8080,
|
||||
|
@@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
@@ -85,3 +86,17 @@ func CertsFromPEM(pemCerts []byte) ([]*x509.Certificate, error) {
|
||||
}
|
||||
return certs, nil
|
||||
}
|
||||
|
||||
// Format is used to format a string value into a k8s valid name
|
||||
func Format(v string) string {
|
||||
// to lower case
|
||||
v = strings.ToLower(v)
|
||||
// dots to dashes
|
||||
v = strings.ReplaceAll(v, ".", "-")
|
||||
// limit to 253 chars
|
||||
if len(v) > 253 {
|
||||
v = v[:253]
|
||||
}
|
||||
// return new name
|
||||
return v
|
||||
}
|
@@ -8,19 +8,38 @@ import (
|
||||
func TestTemplates(t *testing.T) {
|
||||
name := "foo"
|
||||
version := "123"
|
||||
source := "github.com/foo/bar"
|
||||
|
||||
// Render default service
|
||||
s := DefaultService(name, version)
|
||||
s := NewService(name, version)
|
||||
bs := new(bytes.Buffer)
|
||||
if err := renderTemplate(templates["service"], bs, s); err != nil {
|
||||
t.Errorf("Failed to render kubernetes service: %v", err)
|
||||
}
|
||||
|
||||
// Render default deployment
|
||||
d := DefaultDeployment(name, version, source)
|
||||
d := NewDeployment(name, version)
|
||||
bd := new(bytes.Buffer)
|
||||
if err := renderTemplate(templates["deployment"], bd, d); err != nil {
|
||||
t.Errorf("Failed to render kubernetes deployment: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatName(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
expect string
|
||||
}{
|
||||
{"foobar", "foobar"},
|
||||
{"foo-bar", "foo-bar"},
|
||||
{"foo.bar", "foo-bar"},
|
||||
{"Foo.Bar", "foo-bar"},
|
||||
{"go.micro.foo.bar", "go-micro-foo-bar"},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
v := Format(test.name)
|
||||
if v != test.expect {
|
||||
t.Fatalf("Expected name %s for %s got: %s", test.expect, test.name, v)
|
||||
}
|
||||
}
|
||||
}
|
@@ -5,7 +5,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -86,19 +85,19 @@ func (k *kubernetes) Create(s *runtime.Service, opts ...runtime.CreateOption) er
|
||||
o(&options)
|
||||
}
|
||||
|
||||
svcName := s.Name
|
||||
// quickly prevalidate the name and version
|
||||
name := s.Name
|
||||
if len(s.Version) > 0 {
|
||||
svcName = strings.Join([]string{s.Name, s.Version}, "-")
|
||||
name = name + "-" + s.Version
|
||||
}
|
||||
|
||||
if !client.ServiceRegexp.MatchString(svcName) {
|
||||
return fmt.Errorf("invalid service name: %s", svcName)
|
||||
}
|
||||
// format as we'll format in the deployment
|
||||
name = client.Format(name)
|
||||
|
||||
// create new kubernetes micro service
|
||||
service := newService(s, options)
|
||||
|
||||
log.Debugf("Runtime queueing service %s for start action", service.Name)
|
||||
log.Debugf("Runtime queueing service %s version %s for start action", service.Name, service.Version)
|
||||
|
||||
// push into start queue
|
||||
k.queue <- &task{
|
||||
@@ -109,9 +108,9 @@ func (k *kubernetes) Create(s *runtime.Service, opts ...runtime.CreateOption) er
|
||||
return nil
|
||||
}
|
||||
|
||||
// getMicroService queries kubernetes for micro service
|
||||
// getService queries kubernetes for micro service
|
||||
// NOTE: this function is not thread-safe
|
||||
func (k *kubernetes) getMicroService(labels map[string]string) ([]*runtime.Service, error) {
|
||||
func (k *kubernetes) getService(labels map[string]string) ([]*runtime.Service, error) {
|
||||
// get the service status
|
||||
serviceList := new(client.ServiceList)
|
||||
r := &client.Resource{
|
||||
@@ -137,31 +136,55 @@ func (k *kubernetes) getMicroService(labels map[string]string) ([]*runtime.Servi
|
||||
|
||||
// collect info from kubernetes service
|
||||
for _, kservice := range serviceList.Items {
|
||||
// name of the service
|
||||
name := kservice.Metadata.Labels["name"]
|
||||
// version of the service
|
||||
version := kservice.Metadata.Labels["version"]
|
||||
svcMap[name] = &runtime.Service{
|
||||
|
||||
// save as service
|
||||
svcMap[name+version] = &runtime.Service{
|
||||
Name: name,
|
||||
Version: version,
|
||||
Metadata: make(map[string]string),
|
||||
}
|
||||
|
||||
// copy annotations metadata into service metadata
|
||||
for k, v := range kservice.Metadata.Annotations {
|
||||
svcMap[name].Metadata[k] = v
|
||||
svcMap[name+version].Metadata[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// collect additional info from kubernetes deployment
|
||||
for _, kdep := range depList.Items {
|
||||
// name of the service
|
||||
name := kdep.Metadata.Labels["name"]
|
||||
if svc, ok := svcMap[name]; ok {
|
||||
// set the service source
|
||||
// versio of the service
|
||||
version := kdep.Metadata.Labels["version"]
|
||||
|
||||
// access existing service map based on name + version
|
||||
if svc, ok := svcMap[name+version]; ok {
|
||||
// we're expecting our own service name in metadata
|
||||
if _, ok := kdep.Metadata.Annotations["name"]; !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// set the service name, version and source
|
||||
// based on existing annotations we stored
|
||||
svc.Name = kdep.Metadata.Annotations["name"]
|
||||
svc.Version = kdep.Metadata.Annotations["version"]
|
||||
svc.Source = kdep.Metadata.Annotations["source"]
|
||||
|
||||
// delete from metadata
|
||||
delete(kdep.Metadata.Annotations, "name")
|
||||
delete(kdep.Metadata.Annotations, "version")
|
||||
delete(kdep.Metadata.Annotations, "source")
|
||||
|
||||
// copy all annotations metadata into service metadata
|
||||
for k, v := range kdep.Metadata.Annotations {
|
||||
svc.Metadata[k] = v
|
||||
}
|
||||
|
||||
// parse out deployment status
|
||||
// parse out deployment status and inject into service metadata
|
||||
if len(kdep.Status.Conditions) > 0 {
|
||||
status := kdep.Status.Conditions[0].Type
|
||||
// pick the last known condition type and mark the service status with it
|
||||
@@ -186,6 +209,7 @@ func (k *kubernetes) getMicroService(labels map[string]string) ([]*runtime.Servi
|
||||
|
||||
// collect all the services and return
|
||||
services := make([]*runtime.Service, 0, len(serviceList.Items))
|
||||
|
||||
for _, service := range svcMap {
|
||||
services = append(services, service)
|
||||
}
|
||||
@@ -193,8 +217,8 @@ func (k *kubernetes) getMicroService(labels map[string]string) ([]*runtime.Servi
|
||||
return services, nil
|
||||
}
|
||||
|
||||
// Get returns all instances of given service
|
||||
func (k *kubernetes) Get(name string, opts ...runtime.GetOption) ([]*runtime.Service, error) {
|
||||
// Read returns all instances of given service
|
||||
func (k *kubernetes) Read(name string, opts ...runtime.ReadOption) ([]*runtime.Service, error) {
|
||||
k.Lock()
|
||||
defer k.Unlock()
|
||||
|
||||
@@ -203,13 +227,16 @@ func (k *kubernetes) Get(name string, opts ...runtime.GetOption) ([]*runtime.Ser
|
||||
return nil, errors.New("missing service name")
|
||||
}
|
||||
|
||||
// format the name
|
||||
name = client.Format(name)
|
||||
|
||||
// set the default labels
|
||||
labels := map[string]string{
|
||||
"micro": "service",
|
||||
"name": name,
|
||||
}
|
||||
|
||||
var options runtime.GetOptions
|
||||
var options runtime.ReadOptions
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
@@ -221,7 +248,7 @@ func (k *kubernetes) Get(name string, opts ...runtime.GetOption) ([]*runtime.Ser
|
||||
|
||||
log.Debugf("Runtime querying service %s", name)
|
||||
|
||||
return k.getMicroService(labels)
|
||||
return k.getService(labels)
|
||||
}
|
||||
|
||||
// List the managed services
|
||||
@@ -235,7 +262,7 @@ func (k *kubernetes) List() ([]*runtime.Service, error) {
|
||||
|
||||
log.Debugf("Runtime listing all micro services")
|
||||
|
||||
return k.getMicroService(labels)
|
||||
return k.getService(labels)
|
||||
}
|
||||
|
||||
// Update the service in place
|
||||
|
@@ -18,9 +18,23 @@ type service struct {
|
||||
}
|
||||
|
||||
func newService(s *runtime.Service, c runtime.CreateOptions) *service {
|
||||
kservice := client.DefaultService(s.Name, s.Version)
|
||||
kdeploy := client.DefaultDeployment(s.Name, s.Version, s.Source)
|
||||
// use pre-formatted name/version
|
||||
name := client.Format(s.Name)
|
||||
version := client.Format(s.Version)
|
||||
|
||||
kservice := client.NewService(name, version)
|
||||
kdeploy := client.NewDeployment(name, version)
|
||||
|
||||
// attach our values to the deployment; name, version, source
|
||||
kdeploy.Metadata.Annotations["name"] = s.Name
|
||||
kdeploy.Metadata.Annotations["version"] = s.Version
|
||||
kdeploy.Metadata.Annotations["source"] = s.Source
|
||||
|
||||
// associate owner:group to be later augmented
|
||||
kdeploy.Metadata.Annotations["owner"] = "micro"
|
||||
kdeploy.Metadata.Annotations["group"] = "micro"
|
||||
|
||||
// define the environment values used by the container
|
||||
env := make([]client.EnvVar, 0, len(c.Env))
|
||||
for _, evar := range c.Env {
|
||||
evarPair := strings.Split(evar, "=")
|
||||
@@ -35,10 +49,11 @@ func newService(s *runtime.Service, c runtime.CreateOptions) *service {
|
||||
// if Exec/Command has been supplied override the default command
|
||||
if len(s.Exec) > 0 {
|
||||
kdeploy.Spec.Template.PodSpec.Containers[0].Command = s.Exec
|
||||
} else {
|
||||
if len(c.Command) > 0 {
|
||||
kdeploy.Spec.Template.PodSpec.Containers[0].Command = c.Command
|
||||
}
|
||||
} else if len(c.Command) > 0 {
|
||||
kdeploy.Spec.Template.PodSpec.Containers[0].Command = c.Command
|
||||
} else if len(s.Source) > 0 {
|
||||
// default command for our k8s service should be source
|
||||
kdeploy.Spec.Template.PodSpec.Containers[0].Command = []string{"go", "run", s.Source}
|
||||
}
|
||||
|
||||
return &service{
|
||||
|
Reference in New Issue
Block a user