github.com/jrxfive/nomad@v0.6.1-0.20170802162750-1fef470e89bf/nomad/deployment_endpoint.go (about) 1 package nomad 2 3 import ( 4 "fmt" 5 "time" 6 7 metrics "github.com/armon/go-metrics" 8 memdb "github.com/hashicorp/go-memdb" 9 "github.com/hashicorp/nomad/nomad/state" 10 "github.com/hashicorp/nomad/nomad/structs" 11 ) 12 13 // Deployment endpoint is used for manipulating deployments 14 type Deployment struct { 15 srv *Server 16 } 17 18 // GetDeployment is used to request information about a specific deployment 19 func (d *Deployment) GetDeployment(args *structs.DeploymentSpecificRequest, 20 reply *structs.SingleDeploymentResponse) error { 21 if done, err := d.srv.forward("Deployment.GetDeployment", args, args, reply); done { 22 return err 23 } 24 defer metrics.MeasureSince([]string{"nomad", "deployment", "get_deployment"}, time.Now()) 25 26 // Setup the blocking query 27 opts := blockingOptions{ 28 queryOpts: &args.QueryOptions, 29 queryMeta: &reply.QueryMeta, 30 run: func(ws memdb.WatchSet, state *state.StateStore) error { 31 // Verify the arguments 32 if args.DeploymentID == "" { 33 return fmt.Errorf("missing deployment ID") 34 } 35 36 // Look for the deployment 37 out, err := state.DeploymentByID(ws, args.DeploymentID) 38 if err != nil { 39 return err 40 } 41 42 // Setup the output 43 reply.Deployment = out 44 if out != nil { 45 reply.Index = out.ModifyIndex 46 } else { 47 // Use the last index that affected the deployments table 48 index, err := state.Index("deployment") 49 if err != nil { 50 return err 51 } 52 reply.Index = index 53 } 54 55 // Set the query response 56 d.srv.setQueryMeta(&reply.QueryMeta) 57 return nil 58 }} 59 return d.srv.blockingRPC(&opts) 60 } 61 62 // Fail is used to force fail a deployment 63 func (d *Deployment) Fail(args *structs.DeploymentFailRequest, reply *structs.DeploymentUpdateResponse) error { 64 if done, err := d.srv.forward("Deployment.Fail", args, args, reply); done { 65 return err 66 } 67 defer metrics.MeasureSince([]string{"nomad", "deployment", "fail"}, time.Now()) 68 69 // Validate the arguments 70 if args.DeploymentID == "" { 71 return fmt.Errorf("missing deployment ID") 72 } 73 74 // Lookup the deployment 75 snap, err := d.srv.fsm.State().Snapshot() 76 if err != nil { 77 return err 78 } 79 80 ws := memdb.NewWatchSet() 81 deploy, err := snap.DeploymentByID(ws, args.DeploymentID) 82 if err != nil { 83 return err 84 } 85 if deploy == nil { 86 return fmt.Errorf("deployment not found") 87 } 88 89 if !deploy.Active() { 90 return fmt.Errorf("can't fail terminal deployment") 91 } 92 93 // Call into the deployment watcher 94 return d.srv.deploymentWatcher.FailDeployment(args, reply) 95 } 96 97 // Pause is used to pause a deployment 98 func (d *Deployment) Pause(args *structs.DeploymentPauseRequest, reply *structs.DeploymentUpdateResponse) error { 99 if done, err := d.srv.forward("Deployment.Pause", args, args, reply); done { 100 return err 101 } 102 defer metrics.MeasureSince([]string{"nomad", "deployment", "pause"}, time.Now()) 103 104 // Validate the arguments 105 if args.DeploymentID == "" { 106 return fmt.Errorf("missing deployment ID") 107 } 108 109 // Lookup the deployment 110 snap, err := d.srv.fsm.State().Snapshot() 111 if err != nil { 112 return err 113 } 114 115 ws := memdb.NewWatchSet() 116 deploy, err := snap.DeploymentByID(ws, args.DeploymentID) 117 if err != nil { 118 return err 119 } 120 if deploy == nil { 121 return fmt.Errorf("deployment not found") 122 } 123 124 if !deploy.Active() { 125 if args.Pause { 126 return fmt.Errorf("can't pause terminal deployment") 127 } 128 129 return fmt.Errorf("can't resume terminal deployment") 130 } 131 132 // Call into the deployment watcher 133 return d.srv.deploymentWatcher.PauseDeployment(args, reply) 134 } 135 136 // Promote is used to promote canaries in a deployment 137 func (d *Deployment) Promote(args *structs.DeploymentPromoteRequest, reply *structs.DeploymentUpdateResponse) error { 138 if done, err := d.srv.forward("Deployment.Promote", args, args, reply); done { 139 return err 140 } 141 defer metrics.MeasureSince([]string{"nomad", "deployment", "promote"}, time.Now()) 142 143 // Validate the arguments 144 if args.DeploymentID == "" { 145 return fmt.Errorf("missing deployment ID") 146 } 147 148 // Lookup the deployment 149 snap, err := d.srv.fsm.State().Snapshot() 150 if err != nil { 151 return err 152 } 153 154 ws := memdb.NewWatchSet() 155 deploy, err := snap.DeploymentByID(ws, args.DeploymentID) 156 if err != nil { 157 return err 158 } 159 if deploy == nil { 160 return fmt.Errorf("deployment not found") 161 } 162 163 if !deploy.Active() { 164 return fmt.Errorf("can't promote terminal deployment") 165 } 166 167 // Call into the deployment watcher 168 return d.srv.deploymentWatcher.PromoteDeployment(args, reply) 169 } 170 171 // SetAllocHealth is used to set the health of allocations that are part of the 172 // deployment. 173 func (d *Deployment) SetAllocHealth(args *structs.DeploymentAllocHealthRequest, reply *structs.DeploymentUpdateResponse) error { 174 if done, err := d.srv.forward("Deployment.SetAllocHealth", args, args, reply); done { 175 return err 176 } 177 defer metrics.MeasureSince([]string{"nomad", "deployment", "set_alloc_health"}, time.Now()) 178 179 // Validate the arguments 180 if args.DeploymentID == "" { 181 return fmt.Errorf("missing deployment ID") 182 } 183 184 if len(args.HealthyAllocationIDs)+len(args.UnhealthyAllocationIDs) == 0 { 185 return fmt.Errorf("must specify at least one healthy/unhealthy allocation ID") 186 } 187 188 // Lookup the deployment 189 snap, err := d.srv.fsm.State().Snapshot() 190 if err != nil { 191 return err 192 } 193 194 ws := memdb.NewWatchSet() 195 deploy, err := snap.DeploymentByID(ws, args.DeploymentID) 196 if err != nil { 197 return err 198 } 199 if deploy == nil { 200 return fmt.Errorf("deployment not found") 201 } 202 203 if !deploy.Active() { 204 return fmt.Errorf("can't set health of allocations for a terminal deployment") 205 } 206 207 // Call into the deployment watcher 208 return d.srv.deploymentWatcher.SetAllocHealth(args, reply) 209 } 210 211 // List returns the list of deployments in the system 212 func (d *Deployment) List(args *structs.DeploymentListRequest, reply *structs.DeploymentListResponse) error { 213 if done, err := d.srv.forward("Deployment.List", args, args, reply); done { 214 return err 215 } 216 defer metrics.MeasureSince([]string{"nomad", "deployment", "list"}, time.Now()) 217 218 // Setup the blocking query 219 opts := blockingOptions{ 220 queryOpts: &args.QueryOptions, 221 queryMeta: &reply.QueryMeta, 222 run: func(ws memdb.WatchSet, state *state.StateStore) error { 223 // Capture all the deployments 224 var err error 225 var iter memdb.ResultIterator 226 if prefix := args.QueryOptions.Prefix; prefix != "" { 227 iter, err = state.DeploymentsByIDPrefix(ws, prefix) 228 } else { 229 iter, err = state.Deployments(ws) 230 } 231 if err != nil { 232 return err 233 } 234 235 var deploys []*structs.Deployment 236 for { 237 raw := iter.Next() 238 if raw == nil { 239 break 240 } 241 deploy := raw.(*structs.Deployment) 242 deploys = append(deploys, deploy) 243 } 244 reply.Deployments = deploys 245 246 // Use the last index that affected the jobs table 247 index, err := state.Index("deployment") 248 if err != nil { 249 return err 250 } 251 reply.Index = index 252 253 // Set the query response 254 d.srv.setQueryMeta(&reply.QueryMeta) 255 return nil 256 }} 257 return d.srv.blockingRPC(&opts) 258 } 259 260 // Allocations returns the list of allocations that are a part of the deployment 261 func (d *Deployment) Allocations(args *structs.DeploymentSpecificRequest, reply *structs.AllocListResponse) error { 262 if done, err := d.srv.forward("Deployment.Allocations", args, args, reply); done { 263 return err 264 } 265 defer metrics.MeasureSince([]string{"nomad", "deployment", "allocations"}, time.Now()) 266 267 // Setup the blocking query 268 opts := blockingOptions{ 269 queryOpts: &args.QueryOptions, 270 queryMeta: &reply.QueryMeta, 271 run: func(ws memdb.WatchSet, state *state.StateStore) error { 272 // Capture all the allocations 273 allocs, err := state.AllocsByDeployment(ws, args.DeploymentID) 274 if err != nil { 275 return err 276 } 277 278 stubs := make([]*structs.AllocListStub, 0, len(allocs)) 279 for _, alloc := range allocs { 280 stubs = append(stubs, alloc.Stub()) 281 } 282 reply.Allocations = stubs 283 284 // Use the last index that affected the jobs table 285 index, err := state.Index("allocs") 286 if err != nil { 287 return err 288 } 289 reply.Index = index 290 291 // Set the query response 292 d.srv.setQueryMeta(&reply.QueryMeta) 293 return nil 294 }} 295 return d.srv.blockingRPC(&opts) 296 } 297 298 // Reap is used to cleanup terminal deployments 299 func (d *Deployment) Reap(args *structs.DeploymentDeleteRequest, 300 reply *structs.GenericResponse) error { 301 if done, err := d.srv.forward("Deployment.Reap", args, args, reply); done { 302 return err 303 } 304 defer metrics.MeasureSince([]string{"nomad", "deployment", "reap"}, time.Now()) 305 306 // Update via Raft 307 _, index, err := d.srv.raftApply(structs.DeploymentDeleteRequestType, args) 308 if err != nil { 309 return err 310 } 311 312 // Update the index 313 reply.Index = index 314 return nil 315 }