github.com/henvic/wedeploycli@v1.7.6-0.20200319005353-3630f582f284/command/deploy/remote/remote.go (about) 1 package deployremote 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "math/rand" 8 "path/filepath" 9 "regexp" 10 "strings" 11 12 "github.com/henvic/wedeploycli/color" 13 "github.com/henvic/wedeploycli/command/canceled" 14 "github.com/henvic/wedeploycli/command/deploy/internal/getproject" 15 "github.com/henvic/wedeploycli/command/internal/we" 16 "github.com/henvic/wedeploycli/deployment" 17 "github.com/henvic/wedeploycli/deployment/transport/git" 18 "github.com/henvic/wedeploycli/deployment/transport/gogit" 19 "github.com/henvic/wedeploycli/fancy" 20 "github.com/henvic/wedeploycli/inspector" 21 "github.com/henvic/wedeploycli/isterm" 22 "github.com/henvic/wedeploycli/namesgenerator" 23 "github.com/henvic/wedeploycli/services" 24 "github.com/henvic/wedeploycli/verbose" 25 ) 26 27 // RemoteDeployment of services 28 type RemoteDeployment struct { 29 Params deployment.Params 30 31 Experimental bool 32 33 path string 34 services services.ServiceInfoList 35 remap []string 36 ctx context.Context 37 } 38 39 // Feedback about a deployment. 40 type Feedback struct { 41 GroupUID string 42 Services services.ServiceInfoList 43 } 44 45 // Run does the remote deployment procedures 46 func (rd *RemoteDeployment) Run(ctx context.Context) (f Feedback, err error) { 47 rd.ctx = ctx 48 wectx := we.Context() 49 50 if rd.path, err = getWorkingDirectory(); err != nil { 51 return f, err 52 } 53 54 rd.Params.ProjectID, err = getproject.MaybeID(rd.Params.ProjectID, rd.Params.Region) 55 56 if err != nil { 57 return f, err 58 } 59 60 err = rd.loadServicesList() 61 f.Services = rd.services 62 63 if err != nil { 64 return f, err 65 } 66 67 if len(rd.services) == 0 { 68 return f, errors.New("no service available for deployment was found") 69 } 70 71 if err = rd.checkImage(); err != nil { 72 return f, err 73 } 74 75 rd.verboseRemappedServices() 76 77 var deploy = &deployment.Deploy{ 78 ConfigContext: wectx, 79 80 Params: rd.Params, 81 Path: rd.path, 82 Services: rd.services, 83 } 84 85 err = deploy.Do(ctx, rd.getTransport()) 86 f.GroupUID = deploy.GetGroupUID() 87 return f, err 88 } 89 90 func (rd *RemoteDeployment) getTransport() deployment.Transport { 91 if rd.Experimental { 92 return &gogit.Transport{} 93 } 94 95 return &git.Transport{} 96 } 97 98 func (rd *RemoteDeployment) checkImage() error { 99 if rd.Params.Image == "" || len(rd.services) <= 1 { 100 return nil 101 } 102 103 var first = rd.services[0] 104 var pkg = first.Package() 105 var original = pkg.Image 106 var diff = []string{} 107 var f = fmt.Sprintf("%s is %s (%s)", first.ServiceID, pkg.Image, first.Location) 108 109 for _, s := range rd.services { 110 pkg := s.Package() 111 112 if original == "" { 113 original = pkg.Image 114 } 115 116 if original != pkg.Image { 117 diff = append(diff, fmt.Sprintf("%s is %s (%s)", s.ServiceID, pkg.Image, s.Location)) 118 } 119 } 120 121 // found services with duplicated ID "email" on %v and %v 122 if len(diff) != 0 { 123 diff = append([]string{f}, diff...) 124 return fmt.Errorf("refusing to overwrite image for services with different images:\n%v", 125 strings.Join(diff, "\n")) 126 } 127 128 return nil 129 } 130 131 func (rd *RemoteDeployment) verboseRemappedServices() { 132 if !verbose.Enabled { 133 return 134 } 135 136 for _, s := range rd.remap { 137 verbose.Debug("Service " + s + " had service ID mapped or inferred") 138 } 139 } 140 141 func (rd *RemoteDeployment) loadServicesList() (err error) { 142 if err = rd.loadServicesListFromPath(); err != nil { 143 return err 144 } 145 146 if err = rd.checkServiceIDs(); err != nil { 147 return err 148 } 149 150 return rd.remapServicesWithEmptyIDs() 151 } 152 153 func (rd *RemoteDeployment) getServiceIDFromBaseDirOrRandom(s services.ServiceInfo) (newServiceID string) { 154 r := regexp.MustCompile("^[0-9a-z]*$") 155 serviceID := strings.ToLower(filepath.Base(s.Location)) 156 157 if !r.MatchString(serviceID) { 158 serviceID = fmt.Sprintf("%s%d", namesgenerator.GetRandomAdjective(), rand.Intn(99)) 159 } 160 161 verbose.Debug(fmt.Sprintf("service in %v assigned with id %v", s.Location, serviceID)) 162 return serviceID 163 } 164 165 func (rd *RemoteDeployment) remapServicesWithEmptyIDs() error { 166 for k, s := range rd.services { 167 if s.ServiceID != "" { 168 continue 169 } 170 171 rd.services[k].ServiceID = rd.getServiceIDFromBaseDirOrRandom(s) 172 rd.remap = append(rd.remap, s.Location) 173 } 174 175 return nil 176 } 177 178 func (rd *RemoteDeployment) checkServiceIDs() error { 179 if len(rd.services) == 1 { 180 return rd.checkServiceParameter() 181 } 182 183 if rd.Params.ServiceID != "" { 184 return errors.New("service id parameter is not allowed when deploying multiple services") 185 } 186 187 return rd.checkEmptyIDOnMultipleDeployment() 188 } 189 190 func (rd *RemoteDeployment) checkEmptyIDOnMultipleDeployment() error { 191 var empty []services.ServiceInfo 192 for _, s := range rd.services { 193 if s.ServiceID == "" { 194 empty = append(empty, s) 195 } 196 } 197 198 if len(empty) == 0 { 199 return nil 200 } 201 202 fmt.Println(fancy.Info("Multiple services found without LCP.json IDs:")) 203 204 for _, e := range empty { 205 fmt.Printf("%v %v\n", e.Location, fancy.Tip("using "+filepath.Base(e.Location))) 206 } 207 208 fmt.Println("") 209 210 q := fmt.Sprintf("Do you want to %s?", 211 color.Format(color.FgMagenta, color.Bold, "continue")) 212 213 switch ok, askErr := fancy.Boolean(q); { 214 case askErr != nil: 215 return askErr 216 case ok: 217 fmt.Println("") 218 return nil 219 } 220 221 return canceled.CancelCommand("deployment canceled") 222 } 223 224 func (rd *RemoteDeployment) checkServiceParameter() error { 225 if rd.Params.ServiceID == "" && rd.services[0].ServiceID != "" { 226 return nil 227 } 228 229 if rd.Params.ServiceID != "" { 230 rd.services[0].ServiceID = rd.Params.ServiceID 231 rd.remap = append(rd.remap, rd.services[0].Location) 232 return nil 233 } 234 235 if !isterm.Check() { 236 return errors.New("service ID is missing (and a terminal was not found for typing one)") 237 } 238 239 var optionServiceID = rd.getServiceIDFromBaseDirOrRandom(rd.services[0]) 240 fmt.Println(fancy.Question("Your service doesn't have an ID. Type one") + " " + 241 fancy.Tip("or use: "+optionServiceID)) 242 243 serviceID, err := fancy.Prompt() 244 switch { 245 case err != nil: 246 return err 247 case serviceID == "": 248 serviceID = optionServiceID 249 } 250 251 rd.Params.ServiceID = serviceID 252 rd.services[0].ServiceID = serviceID 253 rd.remap = append(rd.remap, rd.services[0].Location) 254 return nil 255 } 256 257 func (rd *RemoteDeployment) loadServicesListFromPath() (err error) { 258 var overview = inspector.ContextOverview{} 259 if err = overview.Load(rd.path); err != nil { 260 return err 261 } 262 263 rd.services = overview.Services 264 265 if len(rd.services) == 0 { 266 rd.services = append(rd.services, services.ServiceInfo{ 267 Location: rd.path, 268 ServiceID: rd.Params.ServiceID, 269 }) 270 } 271 272 for k := range rd.services { 273 rd.services[k].ProjectID = rd.Params.ProjectID 274 } 275 276 return nil 277 }