| OLD | NEW |
| (Empty) |
| 1 // Copyright 2016 The LUCI Authors. All rights reserved. | |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | |
| 3 // that can be found in the LICENSE file. | |
| 4 | |
| 5 package main | |
| 6 | |
| 7 import ( | |
| 8 "fmt" | |
| 9 "strings" | |
| 10 | |
| 11 "github.com/luci/luci-go/common/cli" | |
| 12 "github.com/luci/luci-go/common/errors" | |
| 13 log "github.com/luci/luci-go/common/logging" | |
| 14 "github.com/luci/luci-go/deploytool/api/deploy" | |
| 15 | |
| 16 "github.com/maruel/subcommands" | |
| 17 "golang.org/x/net/context" | |
| 18 ) | |
| 19 | |
| 20 var cmdManage = subcommands.Command{ | |
| 21 UsageLine: "manage [DEPLOYMENT.[.COMPONENT]]... -help|SUBCOMMANDS...", | |
| 22 ShortDesc: "Management subcommands for deployed components.", | |
| 23 LongDesc: "Offers subcommands to manage a Deployment or Component.", | |
| 24 CommandRun: func() subcommands.CommandRun { | |
| 25 return &cmdManageRun{} | |
| 26 }, | |
| 27 } | |
| 28 | |
| 29 type cmdManageRun struct { | |
| 30 subcommands.CommandRunBase | |
| 31 } | |
| 32 | |
| 33 func (cmd *cmdManageRun) Run(app subcommands.Application, args []string) int { | |
| 34 a, c := app.(*application), cli.GetContext(app, cmd) | |
| 35 | |
| 36 if len(args) == 0 { | |
| 37 log.Errorf(c, "Must supply a Deployment or Component to operate
on.") | |
| 38 return 1 | |
| 39 } | |
| 40 | |
| 41 // Load our frozen checkout. | |
| 42 if err := a.layout.loadFrozenLayout(c); err != nil { | |
| 43 logError(c, err, "Failed to load frozen checkout") | |
| 44 return 1 | |
| 45 } | |
| 46 | |
| 47 // Resolve to the specified Deployment or Component. | |
| 48 mApp, err := getManager(c, a, args[0]) | |
| 49 if err != nil { | |
| 50 logError(c, err, "Failed to resolve management target.") | |
| 51 return 1 | |
| 52 } | |
| 53 if mApp == nil { | |
| 54 fmt.Printf("no management options available for %q", args[0]) | |
| 55 return 0 | |
| 56 } | |
| 57 return subcommands.Run(mApp, args[1:]) | |
| 58 } | |
| 59 | |
| 60 type manageApp struct { | |
| 61 *application | |
| 62 | |
| 63 subcommands []*subcommands.Command | |
| 64 dp deploymentPlan | |
| 65 } | |
| 66 | |
| 67 func (a *manageApp) GetName() string { return a.application.
GetName() + " manage" } | |
| 68 func (a *manageApp) GetCommands() []*subcommands.Command { return a.subcommands
} | |
| 69 | |
| 70 func getManager(c context.Context, a *application, name string) (*manageApp, err
or) { | |
| 71 mApp := manageApp{ | |
| 72 application: a, | |
| 73 subcommands: []*subcommands.Command{ | |
| 74 subcommands.CmdHelp, | |
| 75 }, | |
| 76 } | |
| 77 | |
| 78 switch dep, comp, err := a.layout.getDeploymentComponent(name); { | |
| 79 case err != nil: | |
| 80 return nil, err | |
| 81 | |
| 82 case comp != nil: | |
| 83 mApp.dp.addProjectComponent(comp) | |
| 84 err := a.runWork(c, func(w *work) error { | |
| 85 return mApp.dp.initialize(w, &a.layout) | |
| 86 }) | |
| 87 if err != nil { | |
| 88 return nil, errors.Annotate(err).Reason("failed to initi
alize deployment plan").Err() | |
| 89 } | |
| 90 | |
| 91 switch comp.GetComponent().(type) { | |
| 92 case *deploy.Component_AppengineModule: | |
| 93 break | |
| 94 case *deploy.Component_GkePod: | |
| 95 mApp.subcommands = append(mApp.subcommands, []*subcomman
ds.Command{ | |
| 96 { | |
| 97 UsageLine: "kubectl <args...>", | |
| 98 ShortDesc: "run a kubectl command", | |
| 99 LongDesc: "Run a kubectl command in thi
s component's Container Engine context.", | |
| 100 CommandRun: func() subcommands.CommandRu
n { | |
| 101 cr := manageGKEPodKubectlCommand
Run{ | |
| 102 comp: comp, | |
| 103 } | |
| 104 cr.GetFlags().StringVar(&cr.clus
ter, "cluster", "", | |
| 105 "The cluster to operate
on. If only one cluster is bound, this can be left blank.") | |
| 106 return &cr | |
| 107 }, | |
| 108 }, | |
| 109 }...) | |
| 110 default: | |
| 111 break | |
| 112 } | |
| 113 | |
| 114 default: | |
| 115 mApp.dp.addDeployment(dep) | |
| 116 err := a.runWork(c, func(w *work) error { | |
| 117 return mApp.dp.initialize(w, &a.layout) | |
| 118 }) | |
| 119 if err != nil { | |
| 120 return nil, errors.Annotate(err).Reason("failed to initi
alize deployment plan").Err() | |
| 121 } | |
| 122 | |
| 123 if cp := dep.cloudProject; cp != nil { | |
| 124 mApp.subcommands = append(mApp.subcommands, &subcommands
.Command{ | |
| 125 UsageLine: "update_appengine", | |
| 126 ShortDesc: "update AppEngine parameters", | |
| 127 LongDesc: "Update AppEngine cron, index, and qu
eue configurations.", | |
| 128 CommandRun: func() subcommands.CommandRun { | |
| 129 return &updateGAECommandRun{ | |
| 130 project: cp, | |
| 131 } | |
| 132 }, | |
| 133 }) | |
| 134 } | |
| 135 break | |
| 136 } | |
| 137 | |
| 138 return &mApp, nil | |
| 139 } | |
| 140 | |
| 141 type manageGKEPodKubectlCommandRun struct { | |
| 142 subcommands.CommandRunBase | |
| 143 | |
| 144 comp *layoutDeploymentComponent | |
| 145 cluster string | |
| 146 } | |
| 147 | |
| 148 func (cmd *manageGKEPodKubectlCommandRun) Run(app subcommands.Application, args
[]string) int { | |
| 149 a, c := app.(*manageApp), cli.GetContext(app, cmd) | |
| 150 | |
| 151 // Figure out which cluster to use. | |
| 152 var bp *layoutDeploymentGKEPodBinding | |
| 153 switch { | |
| 154 case cmd.cluster != "": | |
| 155 cluster := cmd.comp.dep.cloudProject.gkeClusters[cmd.cluster] | |
| 156 if cluster == nil { | |
| 157 log.Errorf(c, "Invalid GKE cluster name %q.", cmd.cluste
r) | |
| 158 return 1 | |
| 159 } | |
| 160 for _, gkeBP := range cmd.comp.gkePods { | |
| 161 if gkeBP.cluster == cluster { | |
| 162 bp = gkeBP | |
| 163 break | |
| 164 } | |
| 165 } | |
| 166 | |
| 167 case len(cmd.comp.gkePods) == 0: | |
| 168 log.Errorf(c, "Pod is not bound to any clusters.") | |
| 169 return 1 | |
| 170 | |
| 171 case len(cmd.comp.gkePods) == 1: | |
| 172 bp = cmd.comp.gkePods[0] | |
| 173 | |
| 174 default: | |
| 175 clusters := make([]string, len(cmd.comp.gkePods)) | |
| 176 for i, cluster := range cmd.comp.gkePods { | |
| 177 clusters[i] = fmt.Sprintf("- %s", cluster.cluster.Name) | |
| 178 } | |
| 179 | |
| 180 log.Errorf(c, "Kubernetes pod is bound to multiple clusters. Spe
cify one with -cluster:\n", | |
| 181 strings.Join(clusters, "\n")) | |
| 182 return 1 | |
| 183 } | |
| 184 if bp == nil { | |
| 185 log.Errorf(c, "Could not identify cluster for pod.") | |
| 186 return 1 | |
| 187 } | |
| 188 | |
| 189 var rv int | |
| 190 err := a.runWork(c, func(w *work) error { | |
| 191 kubeCtx, err := getContainerEngineKubernetesContext(w, bp.cluste
r) | |
| 192 if err != nil { | |
| 193 return errors.Annotate(err).Err() | |
| 194 } | |
| 195 | |
| 196 kubectl, err := w.tools.kubectl(kubeCtx) | |
| 197 if err != nil { | |
| 198 return errors.Annotate(err).Err() | |
| 199 } | |
| 200 | |
| 201 rv, err = kubectl.exec(args...).forwardOutput().run(w) | |
| 202 return err | |
| 203 }) | |
| 204 if err != nil { | |
| 205 logError(c, err, "Failed to run kubectl command.") | |
| 206 } | |
| 207 return rv | |
| 208 } | |
| 209 | |
| 210 type updateGAECommandRun struct { | |
| 211 subcommands.CommandRunBase | |
| 212 | |
| 213 project *layoutDeploymentCloudProject | |
| 214 } | |
| 215 | |
| 216 func (cmd *updateGAECommandRun) Run(app subcommands.Application, args []string)
int { | |
| 217 a, c := app.(*manageApp), cli.GetContext(app, cmd) | |
| 218 | |
| 219 // Do not deploy any actual GAE modules. | |
| 220 a.dp.reg.appEngineModulesOnly() | |
| 221 for _, gae := range a.dp.reg.gaeProjects { | |
| 222 gae.clearModules() | |
| 223 } | |
| 224 | |
| 225 var rv int | |
| 226 err := a.runWork(c, func(w *work) error { | |
| 227 a.dp.params.stage = deployCommit | |
| 228 return a.dp.deploy(w) | |
| 229 }) | |
| 230 if err != nil { | |
| 231 logError(c, err, "Failed to update GAE parameters.") | |
| 232 return 1 | |
| 233 } | |
| 234 return rv | |
| 235 } | |
| OLD | NEW |