github.com/emc-advanced-dev/unik@v0.0.0-20190717152701-a58d3e8e33b7/cmd/run.go (about) 1 package cmd 2 3 import ( 4 "fmt" 5 "os" 6 "strings" 7 8 "github.com/sirupsen/logrus" 9 "github.com/spf13/cobra" 10 11 "bufio" 12 "net" 13 14 "github.com/emc-advanced-dev/pkg/errors" 15 "github.com/solo-io/unik/pkg/client" 16 ) 17 18 var instanceName, imageName string 19 var volumes, envPairs []string 20 var instanceMemory, debugPort int 21 22 var runCmd = &cobra.Command{ 23 Use: "run", 24 Short: "Run a unikernel instance from a compiled image", 25 Long: `Deploys a running instance from a unik-compiled unikernel disk image. 26 The instance will be deployed on the provider the image was compiled for. 27 e.g. if the image was compiled for virtualbox, unik will attempt to deploy 28 the image on the configured virtualbox environment. 29 30 'unik run' requires a unik-managed volume (see 'unik volumes' and 'unik create volume') 31 to be attached and mounted to each mount point specified at image compilation time. 32 This means that if the image was compiled with two mount points, /data1 and /data2, 33 'unik run' requires 2 available volumes to be attached to the instance at runtime, which 34 must be specified with the flags --vol SOME_VOLUME_NAME:/data1 --vol ANOTHER_VOLUME_NAME:/data2 35 If no mount points are required for the image, volumes cannot be attached. 36 37 environment variables can be set at runtime through the use of the -env flag. 38 39 Example usage: 40 unik run --instanceName newInstance --imageName myImage --vol myVol:/mount1 --vol yourVol:/mount2 --env foo=bar --env another=one --instanceMemory 1234 41 42 # will create and run an instance of myImage on the provider environment myImage is compiled for 43 # instance will be named newInstance 44 # instance will attempt to mount unik-managed volume myVol to /mount1 45 # instance will attempt to mount unik-managed volume yourVol to /mount2 46 # instance will boot with env variable 'foo' set to 'bar' 47 # instance will boot with env variable 'another' set to 'one' 48 # instance will get 1234 MB of memory 49 50 # note that run must take exactly one --vol argument for each mount point defined in the image specification 51 `, 52 Run: func(cmd *cobra.Command, args []string) { 53 if err := func() error { 54 if instanceName == "" { 55 return errors.New("--instanceName must be set", nil) 56 } 57 if imageName == "" { 58 return errors.New("--imageName must be set", nil) 59 } 60 if err := readClientConfig(); err != nil { 61 return err 62 } 63 if host == "" { 64 host = clientConfig.Host 65 } 66 67 mountPointsToVols := make(map[string]string) 68 for _, vol := range volumes { 69 pair := strings.SplitN(vol, ":", 2) 70 if len(pair) != 2 { 71 return errors.New(fmt.Sprintf("invalid format for vol flag: %s", vol), nil) 72 } 73 volId := pair[0] 74 mnt := pair[1] 75 mountPointsToVols[mnt] = volId 76 } 77 78 env := make(map[string]string) 79 for _, e := range envPairs { 80 pair := strings.Split(e, "=") 81 if len(pair) != 2 { 82 return errors.New(fmt.Sprintf("invalid format for env flag: %s", e), nil) 83 } 84 key := pair[0] 85 val := pair[1] 86 env[key] = val 87 } 88 89 logrus.WithFields(logrus.Fields{ 90 "instanceName": instanceName, 91 "imageName": imageName, 92 "env": env, 93 "mounts": mountPointsToVols, 94 "host": host, 95 }).Infof("running unik run") 96 instance, err := client.UnikClient(host).Instances().Run(instanceName, imageName, mountPointsToVols, env, instanceMemory, noCleanup, debugMode) 97 if err != nil { 98 return errors.New("running image failed: %v", err) 99 } 100 printInstances(instance) 101 if debugMode { 102 logrus.Infof("attaching debugger to instance %s ...", instance.Name) 103 connectDebugger() 104 } 105 return nil 106 }(); err != nil { 107 logrus.Errorf("failed running instance: %v", err) 108 os.Exit(-1) 109 } 110 }, 111 } 112 113 func init() { 114 RootCmd.AddCommand(runCmd) 115 runCmd.Flags().StringVar(&instanceName, "instanceName", "", "<string,required> name to give the instance. must be unique") 116 runCmd.Flags().StringVar(&imageName, "imageName", "", "<string,required> image to use") 117 runCmd.Flags().StringSliceVar(&envPairs, "env", []string{}, "<string,repeated> set any number of environment variables for the instance. must be in the format KEY=VALUE") 118 runCmd.Flags().StringSliceVar(&volumes, "vol", []string{}, `<string,repeated> each --vol flag specifies one volume id and the corresponding mount point to attach 119 to the instance at boot time. volumes must be attached to the instance for each mount point expected by the image. 120 run 'unik image <image_name>' to see the mount points required for the image. 121 specified in the format 'volume_id:mount_point'`) 122 runCmd.Flags().IntVar(&instanceMemory, "instanceMemory", 0, "<int, optional> amount of memory (in MB) to assign to the instance. if none is given, the provider default will be used") 123 runCmd.Flags().BoolVar(&noCleanup, "no-cleanup", false, "<bool, optional> for debugging; do not clean up artifacts for instances that fail to launch") 124 runCmd.Flags().BoolVar(&debugMode, "debug-mode", false, "<bool, optional> runs the instance in Debug mode so GDB can be attached. Currently only supported on QEMU provider") 125 runCmd.Flags().IntVar(&debugPort, "debug-port", 3001, "<int, optional> target port for debugger tcp connections. used in conjunction with --debug-mode") 126 } 127 128 func connectDebugger() { 129 addr := fmt.Sprintf("%v:%v", strings.Split(host, ":")[0], debugPort) 130 conn, err := net.Dial("tcp", addr) 131 if err != nil { 132 logrus.Errorf("failed to initiate tcp connection: %v", err) 133 os.Exit(-1) 134 } 135 if _, err := conn.Write([]byte("GET / HTTP/1.0\r\n\r\n")); err != nil { 136 logrus.Errorf("failed to initialize debgger connection: %v", err) 137 os.Exit(-1) 138 } 139 140 go func() { 141 reader := bufio.NewReader(conn) 142 for { 143 data, err := reader.ReadBytes('\n') 144 if err != nil { 145 logrus.Errorf("disconnected from debugger: %v", err) 146 os.Exit(0) 147 } 148 fmt.Print(string(data)) 149 } 150 }() 151 152 reader := bufio.NewReader(os.Stdin) 153 logrus.Infof("Connected to %s", host) 154 for { 155 data, err := reader.ReadBytes('\n') 156 if err != nil { 157 logrus.Errorf("failed reading stdin: %v", err) 158 os.Exit(-1) 159 } 160 if _, err := conn.Write(data); err != nil { 161 logrus.Errorf("writing to tcp connection: %v", err) 162 os.Exit(-1) 163 } 164 } 165 }