func (m *manager) Update(resource runtime.Resource, opts ...runtime.UpdateOption) error {
	var options runtime.UpdateOptions
	for _, o := range opts {
		o(&options)
	}
	if len(options.Namespace) == 0 {
		options.Namespace = namespace.DefaultNamespace
	}
	switch resource.Type() {
	case runtime.TypeNamespace:
		namespace, ok := resource.(*runtime.Namespace)
		if !ok {
			return runtime.ErrInvalidResource
		}
		if options.Namespace != "" {
			namespace.Name = options.Namespace
		}
		return runtime.DefaultRuntime.Update(namespace)
	case runtime.TypeNetworkPolicy:
		networkPolicy, ok := resource.(*runtime.NetworkPolicy)
		if !ok {
			return runtime.ErrInvalidResource
		}
		if options.Namespace != "" {
			networkPolicy.Namespace = options.Namespace
		}
		return runtime.DefaultRuntime.Update(networkPolicy)
	case runtime.TypeResourceQuota:
		resourceQuota, ok := resource.(*runtime.ResourceQuota)
		if !ok {
			return runtime.ErrInvalidResource
		}
		if options.Namespace != "" {
			resourceQuota.Namespace = options.Namespace
		}
		return runtime.DefaultRuntime.Update(resourceQuota)
	case runtime.TypeService:
		srv, ok := resource.(*runtime.Service)
		if !ok {
			return runtime.ErrInvalidResource
		}
		if len(srv.Version) == 0 {
			srv.Version = "latest"
		}
		srvs, err := m.readServices(options.Namespace, &runtime.Service{
			Name:    srv.Name,
			Version: srv.Version,
		})
		if err != nil {
			return err
		}
		if len(srvs) == 0 {
			return runtime.ErrNotFound
		}
		service := srvs[0]
		service.Service.Source = srv.Source
		service.UpdatedAt = time.Now()
		if options.Instances > 0 {
			service.Options.Instances = options.Instances
		}
		if len(options.Entrypoint) > 0 {
			service.Options.Entrypoint = options.Entrypoint
		}
		if len(options.Secrets) > 0 {
			service.Options.Secrets = options.Secrets
		}
		if build.DefaultBuilder == nil {
			var err error
			service.Service.Source, err = m.checkoutSource(service)
			if err != nil {
				return err
			}
			if err := m.updateServiceInRuntime(service); err != nil {
				return err
			}
			service.Status = runtime.Starting
			service.Error = ""
			return m.writeService(service)
		}
		service.Status = runtime.Pending
		if err := m.writeService(service); err != nil {
			return err
		}
		go m.buildAndUpdate(service)
		return nil
	default:
		return runtime.ErrInvalidResource
	}
}
Update a resource
package manager
import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"strings"
	"time"
	"github.com/micro/micro/v3/service/auth"
	"github.com/micro/micro/v3/service/build"
	"github.com/micro/micro/v3/service/build/util/tar"
	"github.com/micro/micro/v3/service/client"
	"github.com/micro/micro/v3/service/logger"
	"github.com/micro/micro/v3/service/runtime"
	kclient "github.com/micro/micro/v3/service/runtime/kubernetes/client"
	"github.com/micro/micro/v3/service/runtime/source/git"
	"github.com/micro/micro/v3/service/store"
	"github.com/micro/micro/v3/util/namespace"
)
const (
	servicePrefix = "service:"
)
type service struct {
	Service   *runtime.Service       `json:"service"`
	Options   *runtime.CreateOptions `json:"options"`
	Status    runtime.ServiceStatus  `json:"status"`
	UpdatedAt time.Time              `json:"last_updated"`
	Error     string                 `json:"error"`
}
func (s *service) Key() string {
	return servicePrefix + s.Options.Namespace + ":" + s.Service.Name + ":" + s.Service.Version
}
func unique(stringSlice []string) []string {
	keys := make(map[string]bool)
	list := []string{}
	for _, entry := range stringSlice {
		if _, value := keys[entry]; !value {
			keys[entry] = true
			list = append(list, entry)
		}
	}
	return list
}
func (m *manager) checkServices() {
	nss, err := m.listNamespaces()
	if err != nil {
		logger.Warnf("Error listing namespaces: %v", err)
		return
	}
	for _, ns := range nss {
		srvs, err := m.readServices(ns, &runtime.Service{})
		if err != nil {
			logger.Warnf("Error reading services from the %v namespace: %v", ns, err)
			return
		}
		running := map[string]*runtime.Service{}
		curr, _ := runtime.Read(runtime.ReadNamespace(ns))
		for _, v := range curr {
			running[v.Name+":"+v.Version] = v
		}
		for _, srv := range srvs {
			if _, ok := running[srv.Service.Name+":"+srv.Service.Version]; ok {
				continue
			}
			if srv.Status == runtime.Error {
				continue
			}
			if srv.Status == runtime.Building {
				continue
			}
			if srv.Status == runtime.Stopped {
				continue
			}
			if err := m.createServiceInRuntime(srv); err != nil {
				if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
					logger.Errorf("Error restarting service: %v", err)
				}
			}
		}
	}
}
func (m *manager) writeService(srv *service) error {
	bytes, err := json.Marshal(srv)
	if err != nil {
		return err
	}
	return store.Write(&store.Record{Key: srv.Key(), Value: bytes})
}
func (m *manager) deleteService(srv *service) error {
	return store.Delete(srv.Key())
}
func (m *manager) readServices(namespace string, srv *runtime.Service) ([]*service, error) {
	prefix := servicePrefix + namespace + ":"
	if len(srv.Name) > 0 {
		prefix += srv.Name + ":"
	}
	if len(srv.Name) > 0 && len(srv.Version) > 0 {
		prefix += srv.Version
	}
	recs, err := store.Read(prefix, store.ReadPrefix())
	if err != nil {
		return nil, err
	} else if len(recs) == 0 {
		return make([]*service, 0), nil
	}
	srvs := make([]*service, 0, len(recs))
	for _, r := range recs {
		var s *service
		if err := json.Unmarshal(r.Value, &s); err != nil {
			return nil, err
		}
		srvs = append(srvs, s)
	}
	return srvs, nil
}
func (m *manager) listNamespaces() ([]string, error) {
	recs, err := store.Read(servicePrefix, store.ReadPrefix())
	if err != nil {
		return nil, err
	}
	if len(recs) == 0 {
		return []string{namespace.DefaultNamespace}, nil
	}
	namespaces := make([]string, 0, len(recs))
	for _, rec := range recs {
		if comps := strings.Split(rec.Key, ":"); len(comps) == 4 {
			namespaces = append(namespaces, comps[1])
		} else {
			return nil, fmt.Errorf("Invalid key: %v", rec.Key)
		}
	}
	return unique(namespaces), nil
}
func (m *manager) buildAndRun(srv *service) {
	if err := m.build(srv); err != nil {
		return
	}
	srv.Status = runtime.Starting
	m.writeService(srv)
	if err := m.createServiceInRuntime(srv); err != nil {
		srv.Status = runtime.Error
		srv.Error = fmt.Sprintf("Error creating service: %v", err)
		m.writeService(srv)
	}
}
func (m *manager) buildAndUpdate(srv *service) {
	if err := m.build(srv); err != nil {
		return
	}
	srv.Status = runtime.Starting
	m.writeService(srv)
	if err := m.updateServiceInRuntime(srv); err != nil {
		srv.Status = runtime.Error
		srv.Error = fmt.Sprintf("Error updating service: %v", err)
		m.writeService(srv)
	}
}
func (m *manager) build(srv *service) error {
	logger.Infof("Preparing to build %v:%v", srv.Service.Name, srv.Service.Version)
	srv.Status = runtime.Building
	m.writeService(srv)
	handleError := func(err error, msg string) {
		logger.Warnf("Build failed %v:%v: %v %v", srv.Service.Name, srv.Service.Version, msg, err)
		srv.Status = runtime.Error
		srv.Error = fmt.Sprintf("%v: %v", msg, err)
		m.writeService(srv)
	}
	var source io.Reader
	var err error
	if strings.HasPrefix(srv.Service.Source, "source://") {
		nsOpt := store.BlobNamespace(srv.Options.Namespace)
		source, err = store.DefaultBlobStore.Read(srv.Service.Source, nsOpt)
	} else {
		gitSrc, err := git.ParseSource(srv.Service.Source)
		if err != nil {
			handleError(err, "Error parsing git source")
			return err
		}
		if len(srv.Options.Entrypoint) == 0 {
			srv.Options.Entrypoint = gitSrc.Folder
		}
		gitSrc.Ref = srv.Service.Version
		dir, err := git.CheckoutSource(gitSrc, srv.Options.Secrets)
		if err != nil {
			handleError(err, "Error fetching git source")
			return err
		}
		source, err = tar.Archive(dir)
	}
	if err != nil {
		handleError(err, "Error loading source")
		return err
	}
	logger.Infof("Build starting %v:%v", srv.Service.Name, srv.Service.Version)
	build, err := build.DefaultBuilder.Build(source,
		build.Archive("tar"),
		build.Entrypoint(srv.Options.Entrypoint),
	)
	logger.Infof("Build finished %v:%v %v", srv.Service.Name, srv.Service.Version, err)
	if err != nil {
		handleError(err, "Error building service")
		return err
	}
	if m.Runtime.String() != "local" {
		logger.Infof("Uploading build %v:%v", srv.Service.Name, srv.Service.Version)
		nsOpt := store.BlobNamespace(srv.Options.Namespace)
		key := fmt.Sprintf("build://%v:%v", srv.Service.Name, srv.Service.Version)
		if err := store.DefaultBlobStore.Write(key, build, nsOpt); err != nil {
			handleError(err, "Error uploading build")
			return err
		}
	}
	return nil
}
func (m *manager) updateServiceInRuntime(srv *service) error {
	options := []runtime.UpdateOption{
		runtime.UpdateEntrypoint(srv.Options.Entrypoint),
		runtime.UpdateNamespace(srv.Options.Namespace),
	}
	for key, value := range srv.Options.Secrets {
		options = append(options, runtime.UpdateSecret(key, value))
	}
	return m.Runtime.Update(srv.Service, options...)
}
func (m *manager) createServiceInRuntime(srv *service) error {
	acc, err := m.generateAccount(srv)
	if err != nil {
		return err
	}
	options := []runtime.CreateOption{
		runtime.CreateEntrypoint(srv.Options.Entrypoint),
		runtime.CreateImage(srv.Options.Image),
		runtime.CreateType(srv.Options.Type),
		runtime.CreateNamespace(srv.Options.Namespace),
		runtime.WithArgs(srv.Options.Args...),
		runtime.WithCommand(srv.Options.Command...),
		runtime.WithEnv(m.runtimeEnv(srv.Service, srv.Options)),
		runtime.CreateInstances(srv.Options.Instances),
	}
	for key, value := range srv.Options.Secrets {
		options = append(options, runtime.WithSecret(key, value))
	}
	if len(acc.ID) > 0 && len(acc.Secret) > 0 {
		options = append(options, runtime.WithSecret("MICRO_AUTH_ID", acc.ID))
		options = append(options, runtime.WithSecret("MICRO_AUTH_SECRET", acc.Secret))
	}
	return m.Runtime.Create(srv.Service, options...)
}
func (m *manager) checkoutSource(srv *service) (string, error) {
	if strings.HasPrefix(srv.Service.Source, "source://") {
		return m.checkoutBlobSource(srv)
	} else {
		return m.checkoutGitSource(srv)
	}
}
func (m *manager) checkoutBlobSource(srv *service) (string, error) {
	nsOpt := store.BlobNamespace(srv.Options.Namespace)
	source, err := store.DefaultBlobStore.Read(srv.Service.Source, nsOpt)
	if err != nil {
		return "", err
	}
	dir, err := ioutil.TempDir(os.TempDir(), "blob-*")
	if err != nil {
		return "", err
	}
	if err := tar.Unarchive(source, dir); err != nil {
		return "", err
	}
	return dir, nil
}
func (m *manager) checkoutGitSource(srv *service) (string, error) {
	gitSrc, err := git.ParseSource(srv.Service.Source)
	if err != nil {
		return "", err
	}
	gitSrc.Ref = srv.Service.Version
	dir, err := git.CheckoutSource(gitSrc, srv.Options.Secrets)
	if err != nil {
		return "", err
	}
	if len(srv.Options.Entrypoint) == 0 {
		srv.Options.Entrypoint = gitSrc.Folder
	}
	return dir, nil
}
func (m *manager) runtimeEnv(srv *runtime.Service, options *runtime.CreateOptions) []string {
	setEnv := func(p []string, env map[string]string) {
		for _, v := range p {
			parts := strings.Split(v, "=")
			if len(parts) <= 1 {
				continue
			}
			env[parts[0]] = strings.Join(parts[1:], "=")
		}
	}
	env := map[string]string{
		"MICRO_PROFILE": "service",
		"MICRO_SERVICE_NAME":    srv.Name,
		"MICRO_SERVICE_VERSION": srv.Version,
		"MICRO_PROXY": client.DefaultClient.Options().Proxy,
	}
	if runtime.DefaultRuntime.String() == "kubernetes" {
		env["MICRO_SERVICE_ADDRESS"] = ":8080"
	}
	setEnv(options.Env, env)
	if len(options.Namespace) > 0 {
		env["MICRO_NAMESPACE"] = options.Namespace
	}
	var vars []string
	for k, v := range env {
		vars = append(vars, k+"="+v)
	}
	return vars
}
func (m *manager) generateAccount(srv *service) (*auth.Account, error) {
	accName := srv.Service.Name + "-" + srv.Service.Version
	opts := []auth.GenerateOption{
		auth.WithIssuer(srv.Options.Namespace),
		auth.WithScopes("service"),
		auth.WithType("service"),
	}
	acc, err := auth.Generate(accName, opts...)
	if err != nil {
		if logger.V(logger.WarnLevel, logger.DefaultLogger) {
			logger.Warnf("Error generating account %v: %v", accName, err)
		}
		return nil, err
	}
	if logger.V(logger.DebugLevel, logger.DefaultLogger) {
		logger.Debugf("Generated auth account: %v, secret: [len: %v]", acc.ID, len(acc.Secret))
	}
	return acc, nil
}
func (m *manager) cleanupBlobStore(srv *service) {
	opt := store.BlobNamespace(srv.Options.Namespace)
	srcKey := fmt.Sprintf("source://%v:%v", srv.Service.Name, srv.Service.Version)
	if err := store.DefaultBlobStore.Delete(srcKey, opt); err != nil && err != store.ErrNotFound {
		logger.Warnf("Error deleting source %v: %v", srcKey, err)
	}
	if build.DefaultBuilder == nil {
		return
	}
	buildKey := fmt.Sprintf("build://%v:%v", srv.Service.Name, srv.Service.Version)
	if err := store.DefaultBlobStore.Delete(buildKey, opt); err != nil && err != store.ErrNotFound {
		logger.Warnf("Error deleting build %v: %v", srcKey, err)
	}
}
func (m *manager) Init(...runtime.Option) error {
	return nil
}
func (m *manager) Create(resource runtime.Resource, opts ...runtime.CreateOption) error {
	var options runtime.CreateOptions
	for _, o := range opts {
		o(&options)
	}
	if len(options.Namespace) == 0 {
		options.Namespace = namespace.DefaultNamespace
	}
	switch resource.Type() {
	case runtime.TypeNamespace:
		namespace, ok := resource.(*runtime.Namespace)
		if !ok {
			return runtime.ErrInvalidResource
		}
		if options.Namespace != "" {
			namespace.Name = options.Namespace
		}
		return runtime.DefaultRuntime.Create(namespace)
	case runtime.TypeNetworkPolicy:
		networkPolicy, ok := resource.(*runtime.NetworkPolicy)
		if !ok {
			return runtime.ErrInvalidResource
		}
		if options.Namespace != "" {
			networkPolicy.Namespace = options.Namespace
		}
		return runtime.DefaultRuntime.Create(networkPolicy)
	case runtime.TypeResourceQuota:
		resourceQuota, ok := resource.(*runtime.ResourceQuota)
		if !ok {
			return runtime.ErrInvalidResource
		}
		if options.Namespace != "" {
			resourceQuota.Namespace = options.Namespace
		}
		return runtime.DefaultRuntime.Create(resourceQuota)
	case runtime.TypeService:
		srv, ok := resource.(*runtime.Service)
		if !ok {
			return runtime.ErrInvalidResource
		}
		if srv.Metadata == nil {
			srv.Metadata = make(map[string]string)
		}
		if len(srv.Version) == 0 {
			srv.Version = "latest"
		}
		service := &service{
			Service:   srv,
			Options:   &options,
			UpdatedAt: time.Now(),
		}
		if build.DefaultBuilder == nil {
			var err error
			srv.Source, err = m.checkoutSource(service)
			if err != nil {
				return err
			}
			if err := m.createServiceInRuntime(service); err != nil && err != runtime.ErrAlreadyExists {
				return err
			}
			return m.writeService(service)
		}
		service.Status = runtime.Pending
		if err := m.writeService(service); err != nil {
			return err
		}
		go m.buildAndRun(service)
		return nil
	default:
		return runtime.ErrInvalidResource
	}
}
func (m *manager) Read(opts ...runtime.ReadOption) ([]*runtime.Service, error) {
	var options runtime.ReadOptions
	for _, o := range opts {
		o(&options)
	}
	if len(options.Namespace) == 0 {
		options.Namespace = namespace.DefaultNamespace
	}
	srvs, err := m.readServices(options.Namespace, &runtime.Service{
		Name:    options.Service,
		Version: options.Version,
	})
	if err != nil {
		return nil, err
	}
	rSrvs, err := m.Runtime.Read(opts...)
	if err != nil {
		return nil, err
	}
	rSrvMap := make(map[string]*runtime.Service, len(rSrvs))
	for _, s := range rSrvs {
		rSrvMap[s.Name+":"+s.Version] = s
	}
	result := make([]*runtime.Service, len(srvs))
	for i, s := range srvs {
		result[i] = s.Service
		if s.Status != runtime.Unknown {
			result[i].Status = s.Status
		}
		if len(s.Error) > 0 {
			result[i].Metadata["error"] = s.Error
		}
		if !s.UpdatedAt.IsZero() {
			result[i].Metadata["started"] = s.UpdatedAt.Format(time.RFC3339)
		}
		rs, ok := rSrvMap[kclient.Format(s.Service.Name)+":"+kclient.Format(s.Service.Version)]
		if !ok {
			continue
		}
		result[i].Status = rs.Status
		if rs.Metadata != nil && len(rs.Metadata["error"]) > 0 {
			result[i].Metadata["status"] = rs.Metadata["error"]
		}
	}
	return result, nil
}