func NewNetworkPolicy(name, namespace string, allowedLabels map[string]string) *NetworkPolicy {
	np := &NetworkPolicy{
		Metadata: &Metadata{
			Name:      name,
			Namespace: namespace,
		},
		Spec: &NetworkPolicySpec{
			Ingress: []NetworkPolicyRule{
				{
					From: []IngressRuleSelector{
						{  
							PodSelector: &Selector{},
						},
					},
				},
				{
					From: []IngressRuleSelector{
						{
							NamespaceSelector: &Selector{
								MatchLabels: allowedLabels,
							},
						},
					},
				},
			},
			PodSelector: &Selector{},
			PolicyTypes: []string{"Ingress"},
		},
	}
	return np
}
NewNetworkPolicy returns a network policy allowing ingress from the given labels
package client
import (
	"bytes"
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path"
	"regexp"
	"strconv"
	"strings"
	"github.com/micro/micro/v3/service/logger"
	"github.com/micro/micro/v3/service/runtime"
	"github.com/micro/micro/v3/service/runtime/kubernetes/api"
)
var (
	serviceAccountPath = "/var/run/secrets/kubernetes.io/serviceaccount"
	ErrReadNamespace = errors.New("Could not read namespace from service account secret")
	DefaultImage = "micro/cells:v3"
	DefaultNamespace = "default"
	DefaultPort = 8080
)
type client struct {
	opts *api.Options
}
type Client interface {
	Create(*Resource, ...CreateOption) error
	Get(*Resource, ...GetOption) error
	Update(*Resource, ...UpdateOption) error
	Delete(*Resource, ...DeleteOption) error
	List(*Resource, ...ListOption) error
	Log(*Resource, ...LogOption) (io.ReadCloser, error)
	Watch(*Resource, ...WatchOption) (Watcher, error)
}
func (c *client) Create(r *Resource, opts ...CreateOption) error {
	options := CreateOptions{
		Namespace: c.opts.Namespace,
	}
	for _, o := range opts {
		o(&options)
	}
	b := new(bytes.Buffer)
	if err := renderTemplate(r.Kind, b, r.Value); err != nil {
		return err
	}
	return api.NewRequest(c.opts).
		Post().
		SetHeader("Content-Type", "application/yaml").
		Namespace(options.Namespace).
		Resource(r.Kind).
		Body(b).
		Do().
		Error()
}
var (
	nameRegex = regexp.MustCompile("[^a-zA-Z0-9]+")
)
func (c *client) Get(r *Resource, opts ...GetOption) error {
	options := GetOptions{
		Namespace: c.opts.Namespace,
	}
	for _, o := range opts {
		o(&options)
	}
	return api.NewRequest(c.opts).
		Get().
		Resource(r.Kind).
		Namespace(options.Namespace).
		Params(&api.Params{LabelSelector: options.Labels}).
		Do().
		Into(r.Value)
}
func (c *client) Log(r *Resource, opts ...LogOption) (io.ReadCloser, error) {
	options := LogOptions{
		Namespace: c.opts.Namespace,
	}
	for _, o := range opts {
		o(&options)
	}
	req := api.NewRequest(c.opts).
		Get().
		Resource(r.Kind).
		SubResource("log").
		Name(r.Name).
		Namespace(options.Namespace)
	if options.Params != nil {
		req.Params(&api.Params{Additional: options.Params})
	}
	resp, err := req.Raw()
	if err != nil {
		return nil, err
	}
	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		resp.Body.Close()
		return nil, errors.New(resp.Request.URL.String() + ": " + resp.Status)
	}
	return resp.Body, nil
}
func (c *client) Update(r *Resource, opts ...UpdateOption) error {
	options := UpdateOptions{
		Namespace: c.opts.Namespace,
	}
	for _, o := range opts {
		o(&options)
	}
	req := api.NewRequest(c.opts).
		Patch().
		SetHeader("Content-Type", "application/strategic-merge-patch+json").
		Resource(r.Kind).
		Name(r.Name).
		Namespace(options.Namespace)
	switch r.Kind {
	case "service":
		req.Body(r.Value.(*Service))
	case "deployment":
		req.Body(r.Value.(*Deployment))
	case "pod":
		req.Body(r.Value.(*Pod))
	case "networkpolicy", "networkpolicies":
		req.Body(r.Value.(*NetworkPolicy))
	case "resourcequota":
		req.Body(r.Value.(*ResourceQuota))
	default:
		return errors.New("unsupported resource")
	}
	return req.Do().Error()
}
func (c *client) Delete(r *Resource, opts ...DeleteOption) error {
	options := DeleteOptions{
		Namespace: c.opts.Namespace,
	}
	for _, o := range opts {
		o(&options)
	}
	return api.NewRequest(c.opts).
		Delete().
		Resource(r.Kind).
		Name(r.Name).
		Namespace(options.Namespace).
		Do().
		Error()
}
func (c *client) List(r *Resource, opts ...ListOption) error {
	options := ListOptions{
		Namespace: c.opts.Namespace,
	}
	for _, o := range opts {
		o(&options)
	}
	return c.Get(r, GetNamespace(options.Namespace))
}
func (c *client) Watch(r *Resource, opts ...WatchOption) (Watcher, error) {
	options := WatchOptions{
		Namespace: c.opts.Namespace,
	}
	for _, o := range opts {
		o(&options)
	}
	params := &api.Params{Additional: map[string]string{
		"watch": "true",
	}}
	if options.Params != nil {
		for k, v := range options.Params {
			params.Additional[k] = v
		}
	}
	req := api.NewRequest(c.opts).
		Get().
		Resource(r.Kind).
		Name(r.Name).
		Namespace(options.Namespace).
		Params(params)
	return newWatcher(req)
}
func NewService(s *runtime.Service, opts *runtime.CreateOptions) *Resource {
	labels := map[string]string{
		"name":    Format(s.Name),
		"version": Format(s.Version),
		"micro":   Format(opts.Type),
	}
	metadata := &Metadata{
		Name:      Format(s.Name),
		Namespace: Format(opts.Namespace),
		Version:   Format(s.Version),
		Labels:    labels,
	}
	port := DefaultPort
	if len(opts.Port) > 0 {
		port, _ = strconv.Atoi(opts.Port)
	}
	return &Resource{
		Kind: "service",
		Name: metadata.Name,
		Value: &Service{
			Metadata: metadata,
			Spec: &ServiceSpec{
				Type:     "ClusterIP",
				Selector: labels,
				Ports: []ServicePort{{
					"service-port", port, "",
				}},
			},
		},
	}
}
func NewDeployment(s *runtime.Service, opts *runtime.CreateOptions) *Resource {
	labels := map[string]string{
		"name":    Format(s.Name),
		"version": Format(s.Version),
		"micro":   Format(opts.Type),
	}
	annotations := map[string]string{
		"name":    s.Name,
		"version": s.Version,
		"source":  s.Source,
	}
	for k, v := range s.Metadata {
		annotations[k] = v
	}
	metadata := &Metadata{
		Name:        fmt.Sprintf("%v-%v", Format(s.Name), Format(s.Version)),
		Namespace:   Format(opts.Namespace),
		Version:     Format(s.Version),
		Labels:      labels,
		Annotations: annotations,
	}
	image := opts.Image
	if len(image) == 0 {
		image = DefaultImage
	}
	env := make([]EnvVar, 0, len(opts.Env))
	for _, evar := range opts.Env {
		if comps := strings.Split(evar, "="); len(comps) == 2 {
			env = append(env, EnvVar{Name: comps[0], Value: comps[1]})
		}
	}
	for key := range opts.Secrets {
		env = append(env, EnvVar{
			Name: key,
			ValueFrom: &EnvVarSource{
				SecretKeyRef: &SecretKeySelector{
					Name: metadata.Name,
					Key:  key,
				},
			},
		})
	}
	var resReqs *ResourceRequirements
	if opts.Resources != nil {
		resReqs = &ResourceRequirements{Limits: &ResourceLimits{}}
		if opts.Resources.CPU > 0 {
			resReqs.Limits.CPU = fmt.Sprintf("%vm", opts.Resources.CPU)
		}
		if opts.Resources.Mem > 0 {
			resReqs.Limits.Memory = fmt.Sprintf("%vMi", opts.Resources.Mem)
		}
		if opts.Resources.Disk > 0 {
			resReqs.Limits.EphemeralStorage = fmt.Sprintf("%vMi", opts.Resources.Disk)
		}
	}
	port := DefaultPort
	if len(opts.Port) > 0 {
		port, _ = strconv.Atoi(opts.Port)
	}
	replicas := 1
	if opts.Instances > 1 {
		replicas = int(opts.Instances)
	}
	return &Resource{
		Kind: "deployment",
		Name: metadata.Name,
		Value: &Deployment{
			Metadata: metadata,
			Spec: &DeploymentSpec{
				Replicas: replicas,
				Selector: &LabelSelector{
					MatchLabels: labels,
				},
				Template: &Template{
					Metadata: metadata,
					PodSpec: &PodSpec{
						ServiceAccountName: opts.ServiceAccount,
						Containers: []Container{{
							Name:    Format(s.Name),
							Image:   image,
							Env:     env,
							Command: opts.Command,
							Args:    opts.Args,
							Ports: []ContainerPort{{
								Name:          "service-port",
								ContainerPort: port,
							}},
							ReadinessProbe: &Probe{
								TCPSocket: &TCPSocketAction{
									Port: port,
								},
								PeriodSeconds:       10,
								InitialDelaySeconds: 10,
							},
							Resources: resReqs,
						}},
					},
				},
			},
		},
	}
}
func NewLocalClient(hosts ...string) *client {
	if len(hosts) == 0 {
		hosts[0] = "http://localhost:8001"
	}
	return &client{
		opts: &api.Options{
			Client:    http.DefaultClient,
			Host:      hosts[0],
			Namespace: "default",
		},
	}
}
func NewClusterClient() *client {
	host := "https://" + os.Getenv("KUBERNETES_SERVICE_HOST") + ":" + os.Getenv("KUBERNETES_SERVICE_PORT")
	s, err := os.Stat(serviceAccountPath)
	if err != nil {
		logger.Fatal(err)
	}
	if s == nil || !s.IsDir() {
		logger.Fatal(errors.New("service account not found"))
	}
	token, err := ioutil.ReadFile(path.Join(serviceAccountPath, "token"))
	if err != nil {
		logger.Fatal(err)
	}
	t := string(token)
	crt, err := CertPoolFromFile(path.Join(serviceAccountPath, "ca.crt"))
	if err != nil {
		logger.Fatal(err)
	}
	c := &http.Client{
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{
				RootCAs: crt,
			},
			DisableCompression: true,
		},
	}
	return &client{
		opts: &api.Options{
			Client:      c,
			Host:        host,
			BearerToken: &t,
			Namespace:   DefaultNamespace,
		},
	}
}