func (n *Impl) CreatePod(ctx context.Context) error {
	pb := n.Proto
	log.Infof("Creating Pod:\n %+v", pb)
	initContainerImage := pb.Config.InitImage
	if initContainerImage == "" {
		initContainerImage = defaultInitContainerImage
	}
	pod := &corev1.Pod{
		ObjectMeta: metav1.ObjectMeta{
			Name: pb.Name,
			Labels: map[string]string{
				"app":  pb.Name,
				"topo": n.Namespace,
			},
		},
		Spec: corev1.PodSpec{
			InitContainers: []corev1.Container{{
				Name:  fmt.Sprintf("init-%s", pb.Name),
				Image: initContainerImage,
				Args: []string{
					fmt.Sprintf("%d", len(n.Proto.Interfaces)+1),
					fmt.Sprintf("%d", pb.Config.Sleep),
				},
				ImagePullPolicy: "IfNotPresent",
			}},
			Containers: []corev1.Container{{
				Name:            pb.Name,
				Image:           pb.Config.Image,
				Command:         pb.Config.Command,
				Args:            pb.Config.Args,
				Env:             ToEnvVar(pb.Config.Env),
				Resources:       ToResourceRequirements(pb.Constraints),
				ImagePullPolicy: "IfNotPresent",
				SecurityContext: &corev1.SecurityContext{
					Privileged: pointer.Bool(true),
				},
			}},
			TerminationGracePeriodSeconds: pointer.Int64(0),
			NodeSelector:                  map[string]string{},
			Affinity: &corev1.Affinity{
				PodAntiAffinity: &corev1.PodAntiAffinity{
					PreferredDuringSchedulingIgnoredDuringExecution: []corev1.WeightedPodAffinityTerm{{
						Weight: 100,
						PodAffinityTerm: corev1.PodAffinityTerm{
							LabelSelector: &metav1.LabelSelector{
								MatchExpressions: []metav1.LabelSelectorRequirement{{
									Key:      "topo",
									Operator: "In",
									Values:   []string{pb.Name},
								}},
							},
							TopologyKey: "kubernetes.io/hostname",
						},
					}},
				},
			},
		},
	}
	if pb.Config.ConfigData != nil {
		pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{
			Name: "startup-config-volume",
			VolumeSource: corev1.VolumeSource{
				ConfigMap: &corev1.ConfigMapVolumeSource{
					LocalObjectReference: corev1.LocalObjectReference{
						Name: fmt.Sprintf("%s-config", pb.Name),
					},
				},
			},
		})
		for i, c := range pod.Spec.Containers {
			pod.Spec.Containers[i].VolumeMounts = append(c.VolumeMounts, corev1.VolumeMount{
				Name:      "startup-config-volume",
				MountPath: pb.Config.ConfigPath + "/" + pb.Config.ConfigFile,
				SubPath:   pb.Config.ConfigFile,
				ReadOnly:  true,
			})
		}
	}
	sPod, err := n.KubeClient.CoreV1().Pods(n.Namespace).Create(ctx, pod, metav1.CreateOptions{})
	if err != nil {
		return err
	}
	log.Debugf("Pod created:\n%+v\n", sPod)
	return nil
}
CreatePod creates a Pod for the Node based on the underlying proto.
package node
import (
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"path/filepath"
	"sync"
	log "github.com/sirupsen/logrus"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/kubernetes/scheme"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/remotecommand"
	"k8s.io/utils/pointer"
	tpb "github.com/google/kne/proto/topo"
)
type Interface interface {
	Name() string
	GetNamespace() string
	GetProto() *tpb.Node
}
type Implementation interface {
	Create(context.Context) error
	Status(context.Context) (corev1.PodPhase, error)
	Delete(context.Context) error
	Pod(context.Context) (*corev1.Pod, error)
	Service(context.Context) (*corev1.Service, error)
}
type Certer interface {
	GenerateSelfSigned(context.Context) error
}
type ConfigPusher interface {
	ConfigPush(context.Context, io.Reader) error
}
type Resetter interface {
	ResetCfg(ctx context.Context) error
}
type Node interface {
	Interface
	Implementation
}
type NewNodeFn func(n *Impl) (Node, error)
var (
	mu          sync.Mutex
	nodeTypes   = map[tpb.Node_Type]NewNodeFn{}
	vendorTypes = map[tpb.Vendor]NewNodeFn{}
)
func Register(t tpb.Node_Type, fn NewNodeFn) {
	mu.Lock()
	if _, ok := nodeTypes[t]; ok {
		panic(fmt.Sprintf("duplicate registration for %T", t))
	}
	nodeTypes[t] = fn
	mu.Unlock()
}
func Vendor(v tpb.Vendor, fn NewNodeFn) {
	mu.Lock()
	if _, ok := vendorTypes[v]; ok {
		panic(fmt.Sprintf("duplicate registration for %T", v))
	}
	vendorTypes[v] = fn
	mu.Unlock()
}
type Impl struct {
	Namespace  string
	KubeClient kubernetes.Interface
	RestConfig *rest.Config
	Proto      *tpb.Node
	BasePath   string
}
func New(namespace string, pb *tpb.Node, kClient kubernetes.Interface, rCfg *rest.Config, bp string) (Node, error) {
	return getImpl(&Impl{
		Namespace:  namespace,
		Proto:      pb,
		KubeClient: kClient,
		RestConfig: rCfg,
		BasePath:   bp,
	})
}
func (n *Impl) GetProto() *tpb.Node {
	return n.Proto
}
func (n *Impl) GetNamespace() string {
	return n.Namespace
}
const (
	defaultInitContainerImage = "networkop/init-wait:latest"
)
func ToEnvVar(kv map[string]string) []corev1.EnvVar {
	var envVar []corev1.EnvVar
	for k, v := range kv {
		envVar = append(envVar, corev1.EnvVar{
			Name:  k,
			Value: v,
		})
	}
	return envVar
}
func ToResourceRequirements(kv map[string]string) corev1.ResourceRequirements {
	r := corev1.ResourceRequirements{
		Requests: map[corev1.ResourceName]resource.Quantity{},
	}
	if v, ok := kv["cpu"]; ok {
		r.Requests["cpu"] = resource.MustParse(v)
	}
	if v, ok := kv["memory"]; ok {
		r.Requests["memory"] = resource.MustParse(v)
	}
	return r
}
func (n *Impl) Create(ctx context.Context) error {
	if err := n.CreateConfig(ctx); err != nil {
		return fmt.Errorf("node %s failed to create config-map %w", n.Name(), err)
	}
	if err := n.CreatePod(ctx); err != nil {
		return fmt.Errorf("node %s failed to create pod %w", n.Name(), err)
	}
	if err := n.CreateService(ctx); err != nil {
		return fmt.Errorf("node %s failed to create service %w", n.Name(), err)
	}
	return nil
}
func (n *Impl) CreateConfig(ctx context.Context) error {
	pb := n.Proto
	var data []byte
	switch v := pb.Config.GetConfigData().(type) {
	case *tpb.Config_File:
		var err error
		data, err = ioutil.ReadFile(filepath.Join(n.BasePath, v.File))
		if err != nil {
			return err
		}
	case *tpb.Config_Data:
		data = v.Data
	}
	if data != nil {
		cm := &corev1.ConfigMap{
			ObjectMeta: metav1.ObjectMeta{
				Name: fmt.Sprintf("%s-config", pb.Name),
			},
			Data: map[string]string{
				pb.Config.ConfigFile: string(data),
			},
		}
		sCM, err := n.KubeClient.CoreV1().ConfigMaps(n.Namespace).Create(ctx, cm, metav1.CreateOptions{})
		if err != nil {
			return err
		}
		log.Infof("Server Config Map:\n%v\n", sCM)
	}
	return nil
}