github.com/mirantis/virtlet@v1.5.2-0.20191204181327-1659b8a48e9b/pkg/tools/gen.go (about)

     1  /*
     2  Copyright 2018 Mirantis
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package tools
    18  
    19  import (
    20  	"errors"
    21  	"io"
    22  
    23  	"github.com/spf13/cobra"
    24  	apps "k8s.io/api/apps/v1"
    25  	v1 "k8s.io/api/core/v1"
    26  	"k8s.io/apimachinery/pkg/runtime"
    27  
    28  	"github.com/Mirantis/virtlet/pkg/config"
    29  	"github.com/Mirantis/virtlet/pkg/version"
    30  )
    31  
    32  const (
    33  	sourceYamlFile = "deploy/data/virtlet-ds.yaml"
    34  	virtletImage   = "mirantis/virtlet"
    35  )
    36  
    37  // genCommand is used to generate Kubernetes YAML for Virtlet deployment
    38  type genCommand struct {
    39  	out    io.Writer
    40  	dev    bool
    41  	compat bool
    42  	crd    bool
    43  	tag    string
    44  }
    45  
    46  // NewGenCmd returns a cobra.Command that generates Kubernetes YAML for Virtlet
    47  // deployment.
    48  func NewGenCmd(out io.Writer) *cobra.Command {
    49  	g := &genCommand{out: out}
    50  	cmd := &cobra.Command{
    51  		Use:   "gen",
    52  		Short: "Generate Kubernetes YAML for Virtlet deployment",
    53  		Long:  "This command produces YAML suitable for use with kubectl apply -f -",
    54  		RunE: func(cmd *cobra.Command, args []string) error {
    55  			if len(args) != 0 {
    56  				return errors.New("This command does not accept arguments")
    57  			}
    58  			return g.Run()
    59  		},
    60  	}
    61  	cmd.Flags().BoolVar(&g.dev, "dev", false, "Development mode for use with kubeadm-dind-cluster")
    62  	cmd.Flags().BoolVar(&g.compat, "compat", false, "Produce YAML that's compatible with older Kubernetes versions")
    63  	cmd.Flags().BoolVar(&g.crd, "crd", false, "Dump CRD definitions only")
    64  	cmd.Flags().StringVar(&g.tag, "tag", version.Get().ImageTag, "Set virtlet image tag")
    65  	return cmd
    66  }
    67  
    68  func (g *genCommand) getYaml() ([]byte, error) {
    69  	var objs []runtime.Object
    70  	if !g.crd {
    71  		bs, err := Asset(sourceYamlFile)
    72  		if err != nil {
    73  			return bs, err
    74  		}
    75  
    76  		if objs, err = LoadYaml(bs); err != nil {
    77  			return nil, err
    78  		}
    79  		if len(objs) == 0 {
    80  			return nil, errors.New("source yaml is empty")
    81  		}
    82  
    83  		ds, ok := objs[0].(*apps.DaemonSet)
    84  		if !ok {
    85  			return nil, errors.New("the first object is not a DaemonSet")
    86  		}
    87  		if g.dev {
    88  			applyDev(ds)
    89  		}
    90  		if g.compat {
    91  			applyCompat(ds)
    92  		}
    93  		if g.tag != "" {
    94  			applyTag(ds, g.tag)
    95  		}
    96  	}
    97  
    98  	objs = append(objs, config.GetCRDDefinitions()...)
    99  	return ToYaml(objs)
   100  }
   101  
   102  // Run executes the command.
   103  func (g *genCommand) Run() error {
   104  	bs, err := g.getYaml()
   105  	if err != nil {
   106  		return err
   107  	}
   108  	if _, err := g.out.Write(bs); err != nil {
   109  		return err
   110  	}
   111  	return nil
   112  }
   113  
   114  func walkContainers(ds *apps.DaemonSet, toCall func(c *v1.Container)) {
   115  	initContainers := ds.Spec.Template.Spec.InitContainers
   116  	for n := range initContainers {
   117  		toCall(&initContainers[n])
   118  	}
   119  	containers := ds.Spec.Template.Spec.Containers
   120  	for n := range containers {
   121  		toCall(&containers[n])
   122  	}
   123  }
   124  
   125  func walkMounts(ds *apps.DaemonSet, toCall func(m *v1.VolumeMount)) {
   126  	walkContainers(ds, func(c *v1.Container) {
   127  		for i := range c.VolumeMounts {
   128  			toCall(&c.VolumeMounts[i])
   129  		}
   130  	})
   131  }
   132  
   133  func applyDev(ds *apps.DaemonSet) {
   134  	ds.Spec.Template.Spec.Volumes = append(ds.Spec.Template.Spec.Volumes, v1.Volume{
   135  		Name: "dind",
   136  		VolumeSource: v1.VolumeSource{
   137  			HostPath: &v1.HostPathVolumeSource{
   138  				Path: "/dind",
   139  			},
   140  		},
   141  	})
   142  	volMount := v1.VolumeMount{
   143  		Name:      "dind",
   144  		MountPath: "/dind",
   145  	}
   146  	walkContainers(ds, func(c *v1.Container) {
   147  		c.VolumeMounts = append(c.VolumeMounts, volMount)
   148  	})
   149  }
   150  
   151  func applyCompat(ds *apps.DaemonSet) {
   152  	walkMounts(ds, func(v *v1.VolumeMount) {
   153  		if v.Name == "run" || v.Name == "k8s-pods-dir" {
   154  			v.MountPath += ":shared"
   155  			v.MountPropagation = nil
   156  		}
   157  	})
   158  }
   159  
   160  func applyTag(ds *apps.DaemonSet, tag string) {
   161  	walkContainers(ds, func(c *v1.Container) {
   162  		if c.Image == virtletImage {
   163  			c.Image += ":" + tag
   164  		}
   165  	})
   166  }