github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/storageprovisioner/filesystem_events.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package storageprovisioner 5 6 import ( 7 "github.com/juju/errors" 8 "gopkg.in/juju/names.v2" 9 10 "github.com/juju/juju/apiserver/params" 11 "github.com/juju/juju/core/instance" 12 "github.com/juju/juju/core/watcher" 13 "github.com/juju/juju/storage" 14 ) 15 16 // filesystemsChanged is called when the lifecycle states of the filesystems 17 // with the provided IDs have been seen to have changed. 18 func filesystemsChanged(ctx *context, changes []string) error { 19 tags := make([]names.Tag, len(changes)) 20 for i, change := range changes { 21 tags[i] = names.NewFilesystemTag(change) 22 } 23 alive, dying, dead, err := storageEntityLife(ctx, tags) 24 if err != nil { 25 return errors.Trace(err) 26 } 27 logger.Debugf("filesystems alive: %v, dying: %v, dead: %v", alive, dying, dead) 28 if len(alive)+len(dying)+len(dead) == 0 { 29 return nil 30 } 31 32 // Get filesystem information for filesystems, so we can provision, 33 // deprovision, attach and detach. 34 filesystemTags := make([]names.FilesystemTag, 0, len(alive)+len(dying)+len(dead)) 35 for _, tag := range alive { 36 filesystemTags = append(filesystemTags, tag.(names.FilesystemTag)) 37 } 38 for _, tag := range dying { 39 filesystemTags = append(filesystemTags, tag.(names.FilesystemTag)) 40 } 41 for _, tag := range dead { 42 filesystemTags = append(filesystemTags, tag.(names.FilesystemTag)) 43 } 44 filesystemResults, err := ctx.config.Filesystems.Filesystems(filesystemTags) 45 if err != nil { 46 return errors.Annotatef(err, "getting filesystem information") 47 } 48 49 aliveFilesystemTags := filesystemTags[:len(alive)] 50 dyingFilesystemTags := filesystemTags[len(alive) : len(alive)+len(dying)] 51 deadFilesystemTags := filesystemTags[len(alive)+len(dying):] 52 aliveFilesystemResults := filesystemResults[:len(alive)] 53 dyingFilesystemResults := filesystemResults[len(alive) : len(alive)+len(dying)] 54 deadFilesystemResults := filesystemResults[len(alive)+len(dying):] 55 56 if err := processDeadFilesystems(ctx, deadFilesystemTags, deadFilesystemResults); err != nil { 57 return errors.Annotate(err, "deprovisioning filesystems") 58 } 59 if err := processDyingFilesystems(ctx, dyingFilesystemTags, dyingFilesystemResults); err != nil { 60 return errors.Annotate(err, "processing dying filesystems") 61 } 62 if err := processAliveFilesystems(ctx, aliveFilesystemTags, aliveFilesystemResults); err != nil { 63 return errors.Annotate(err, "provisioning filesystems") 64 } 65 return nil 66 } 67 68 // filesystemAttachmentsChanged is called when the lifecycle states of the filesystem 69 // attachments with the provided IDs have been seen to have changed. 70 func filesystemAttachmentsChanged(ctx *context, watcherIds []watcher.MachineStorageId) error { 71 ids := copyMachineStorageIds(watcherIds) 72 alive, dying, dead, err := attachmentLife(ctx, ids) 73 if err != nil { 74 return errors.Trace(err) 75 } 76 logger.Debugf("filesystem attachment alive: %v, dying: %v, dead: %v", alive, dying, dead) 77 if len(dead) != 0 { 78 // We should not see dead filesystem attachments; 79 // attachments go directly from Dying to removed. 80 logger.Warningf("unexpected dead filesystem attachments: %v", dead) 81 } 82 if len(alive)+len(dying) == 0 { 83 return nil 84 } 85 86 // Get filesystem information for alive and dying filesystem attachments, so 87 // we can attach/detach. 88 ids = append(alive, dying...) 89 filesystemAttachmentResults, err := ctx.config.Filesystems.FilesystemAttachments(ids) 90 if err != nil { 91 return errors.Annotatef(err, "getting filesystem attachment information") 92 } 93 94 // Deprovision Dying filesystem attachments. 95 dyingFilesystemAttachmentResults := filesystemAttachmentResults[len(alive):] 96 if err := processDyingFilesystemAttachments(ctx, dying, dyingFilesystemAttachmentResults); err != nil { 97 return errors.Annotate(err, "destroying filesystem attachments") 98 } 99 100 // Provision Alive filesystem attachments. 101 aliveFilesystemAttachmentResults := filesystemAttachmentResults[:len(alive)] 102 if err := processAliveFilesystemAttachments(ctx, alive, aliveFilesystemAttachmentResults); err != nil { 103 return errors.Annotate(err, "creating filesystem attachments") 104 } 105 106 return nil 107 } 108 109 // processDyingFilesystems processes the FilesystemResults for Dying filesystems, 110 // removing them from provisioning-pending as necessary. 111 func processDyingFilesystems(ctx *context, tags []names.FilesystemTag, filesystemResults []params.FilesystemResult) error { 112 for _, tag := range tags { 113 removePendingFilesystem(ctx, tag) 114 } 115 return nil 116 } 117 118 func updateFilesystem(ctx *context, info storage.Filesystem) { 119 ctx.filesystems[info.Tag] = info 120 for id, params := range ctx.incompleteFilesystemAttachmentParams { 121 if params.FilesystemId == "" && id.AttachmentTag == info.Tag.String() { 122 updatePendingFilesystemAttachment(ctx, id, params) 123 } 124 } 125 } 126 127 func updatePendingFilesystem(ctx *context, params storage.FilesystemParams) { 128 if params.Volume != (names.VolumeTag{}) { 129 // The filesystem is volume-backed: we must watch for 130 // the corresponding block device. This will trigger a 131 // one-time (for the volume) forced update of block 132 // devices. If the block device is not immediately 133 // available, then we rely on the watcher. The forced 134 // update is necessary in case the block device was 135 // added to state already, and we didn't observe it. 136 if _, ok := ctx.volumeBlockDevices[params.Volume]; !ok { 137 ctx.pendingVolumeBlockDevices.Add(params.Volume) 138 ctx.incompleteFilesystemParams[params.Tag] = params 139 return 140 } 141 } 142 delete(ctx.incompleteFilesystemParams, params.Tag) 143 scheduleOperations(ctx, &createFilesystemOp{args: params}) 144 } 145 146 func removePendingFilesystem(ctx *context, tag names.FilesystemTag) { 147 delete(ctx.incompleteFilesystemParams, tag) 148 ctx.schedule.Remove(tag) 149 } 150 151 // updatePendingFilesystemAttachment adds the given filesystem attachment params to 152 // either the incomplete set or the schedule. If the params are incomplete 153 // due to a missing instance ID, updatePendingFilesystemAttachment will request 154 // that the machine be watched so its instance ID can be learned. 155 func updatePendingFilesystemAttachment( 156 ctx *context, 157 id params.MachineStorageId, 158 params storage.FilesystemAttachmentParams, 159 ) { 160 var incomplete bool 161 filesystem, ok := ctx.filesystems[params.Filesystem] 162 if !ok { 163 incomplete = true 164 } else { 165 params.FilesystemId = filesystem.FilesystemId 166 if filesystem.Volume != (names.VolumeTag{}) { 167 // The filesystem is volume-backed: if the filesystem 168 // was created in another session, then the block device 169 // may not have been seen yet. We must wait for the block 170 // device watcher to trigger. 171 if _, ok := ctx.volumeBlockDevices[filesystem.Volume]; !ok { 172 incomplete = true 173 } 174 } 175 } 176 if params.InstanceId == "" { 177 watchMachine(ctx, params.Machine.(names.MachineTag)) 178 incomplete = true 179 } 180 if params.FilesystemId == "" { 181 incomplete = true 182 } 183 if incomplete { 184 ctx.incompleteFilesystemAttachmentParams[id] = params 185 return 186 } 187 delete(ctx.incompleteFilesystemAttachmentParams, id) 188 scheduleOperations(ctx, &attachFilesystemOp{args: params}) 189 } 190 191 // removePendingFilesystemAttachment removes the specified pending filesystem 192 // attachment from the incomplete set and/or the schedule if it exists 193 // there. 194 func removePendingFilesystemAttachment(ctx *context, id params.MachineStorageId) { 195 delete(ctx.incompleteFilesystemAttachmentParams, id) 196 ctx.schedule.Remove(id) 197 } 198 199 // processDeadFilesystems processes the FilesystemResults for Dead filesystems, 200 // deprovisioning filesystems and removing from state as necessary. 201 func processDeadFilesystems(ctx *context, tags []names.FilesystemTag, filesystemResults []params.FilesystemResult) error { 202 for _, tag := range tags { 203 removePendingFilesystem(ctx, tag) 204 } 205 var destroy []names.FilesystemTag 206 var remove []names.Tag 207 for i, result := range filesystemResults { 208 tag := tags[i] 209 if result.Error == nil { 210 logger.Debugf("filesystem %s is provisioned, queuing for deprovisioning", tag.Id()) 211 filesystem, err := filesystemFromParams(result.Result) 212 if err != nil { 213 return errors.Annotate(err, "getting filesystem info") 214 } 215 updateFilesystem(ctx, filesystem) 216 destroy = append(destroy, tag) 217 continue 218 } 219 if params.IsCodeNotProvisioned(result.Error) { 220 logger.Debugf("filesystem %s is not provisioned, queuing for removal", tag.Id()) 221 remove = append(remove, tag) 222 continue 223 } 224 return errors.Annotatef(result.Error, "getting filesystem information for filesystem %s", tag.Id()) 225 } 226 if len(destroy) > 0 { 227 ops := make([]scheduleOp, len(destroy)) 228 for i, tag := range destroy { 229 ops[i] = &removeFilesystemOp{tag: tag} 230 } 231 scheduleOperations(ctx, ops...) 232 } 233 if err := removeEntities(ctx, remove); err != nil { 234 return errors.Annotate(err, "removing filesystems from state") 235 } 236 return nil 237 } 238 239 // processDyingFilesystemAttachments processes the FilesystemAttachmentResults for 240 // Dying filesystem attachments, detaching filesystems and updating state as necessary. 241 func processDyingFilesystemAttachments( 242 ctx *context, 243 ids []params.MachineStorageId, 244 filesystemAttachmentResults []params.FilesystemAttachmentResult, 245 ) error { 246 for _, id := range ids { 247 removePendingFilesystemAttachment(ctx, id) 248 } 249 detach := make([]params.MachineStorageId, 0, len(ids)) 250 remove := make([]params.MachineStorageId, 0, len(ids)) 251 for i, result := range filesystemAttachmentResults { 252 id := ids[i] 253 if result.Error == nil { 254 detach = append(detach, id) 255 continue 256 } 257 if params.IsCodeNotProvisioned(result.Error) { 258 remove = append(remove, id) 259 continue 260 } 261 return errors.Annotatef(result.Error, "getting information for filesystem attachment %v", id) 262 } 263 if len(detach) > 0 { 264 attachmentParams, err := filesystemAttachmentParams(ctx, detach) 265 if err != nil { 266 return errors.Trace(err) 267 } 268 ops := make([]scheduleOp, len(attachmentParams)) 269 for i, p := range attachmentParams { 270 ops[i] = &detachFilesystemOp{args: p} 271 } 272 scheduleOperations(ctx, ops...) 273 } 274 if err := removeAttachments(ctx, remove); err != nil { 275 return errors.Annotate(err, "removing attachments from state") 276 } 277 return nil 278 } 279 280 // processAliveFilesystems processes the FilesystemResults for Alive filesystems, 281 // provisioning filesystems and setting the info in state as necessary. 282 func processAliveFilesystems(ctx *context, tags []names.FilesystemTag, filesystemResults []params.FilesystemResult) error { 283 // Filter out the already-provisioned filesystems. 284 pending := make([]names.FilesystemTag, 0, len(tags)) 285 for i, result := range filesystemResults { 286 tag := tags[i] 287 if result.Error == nil { 288 // Filesystem is already provisioned: skip. 289 logger.Debugf("filesystem %q is already provisioned, nothing to do", tag.Id()) 290 filesystem, err := filesystemFromParams(result.Result) 291 if err != nil { 292 return errors.Annotate(err, "getting filesystem info") 293 } 294 updateFilesystem(ctx, filesystem) 295 if !ctx.isApplicationKind() { 296 if filesystem.Volume != (names.VolumeTag{}) { 297 // Ensure that volume-backed filesystems' block 298 // devices are present even after creating the 299 // filesystem, so that attachments can be made. 300 maybeAddPendingVolumeBlockDevice(ctx, filesystem.Volume) 301 } 302 } 303 continue 304 } 305 if !params.IsCodeNotProvisioned(result.Error) { 306 return errors.Annotatef( 307 result.Error, "getting filesystem information for filesystem %q", tag.Id(), 308 ) 309 } 310 // The filesystem has not yet been provisioned, so record its tag 311 // to enquire about parameters below. 312 pending = append(pending, tag) 313 } 314 if len(pending) == 0 { 315 return nil 316 } 317 params, err := filesystemParams(ctx, pending) 318 if err != nil { 319 return errors.Annotate(err, "getting filesystem params") 320 } 321 for _, params := range params { 322 if ctx.isApplicationKind() { 323 logger.Debugf("not queuing filesystem for %v unit", ctx.config.Scope.Id()) 324 continue 325 } 326 updatePendingFilesystem(ctx, params) 327 } 328 return nil 329 } 330 331 func maybeAddPendingVolumeBlockDevice(ctx *context, v names.VolumeTag) { 332 if _, ok := ctx.volumeBlockDevices[v]; !ok { 333 ctx.pendingVolumeBlockDevices.Add(v) 334 } 335 } 336 337 // processAliveFilesystemAttachments processes the FilesystemAttachmentResults 338 // for Alive filesystem attachments, attaching filesystems and setting the info 339 // in state as necessary. 340 func processAliveFilesystemAttachments( 341 ctx *context, 342 ids []params.MachineStorageId, 343 filesystemAttachmentResults []params.FilesystemAttachmentResult, 344 ) error { 345 // Filter out the already-attached. 346 pending := make([]params.MachineStorageId, 0, len(ids)) 347 for i, result := range filesystemAttachmentResults { 348 if result.Error == nil { 349 // Filesystem attachment is already provisioned: if we 350 // didn't (re)attach in this session, then we must do 351 // so now. 352 action := "nothing to do" 353 if _, ok := ctx.filesystemAttachments[ids[i]]; !ok { 354 // Not yet (re)attached in this session. 355 pending = append(pending, ids[i]) 356 action = "will reattach" 357 } 358 logger.Debugf( 359 "%s is already attached to %s, %s", 360 ids[i].AttachmentTag, ids[i].MachineTag, action, 361 ) 362 removePendingFilesystemAttachment(ctx, ids[i]) 363 continue 364 } 365 if !params.IsCodeNotProvisioned(result.Error) { 366 return errors.Annotatef( 367 result.Error, "getting information for attachment %v", ids[i], 368 ) 369 } 370 // The filesystem has not yet been attached, so 371 // record its tag to enquire about parameters below. 372 pending = append(pending, ids[i]) 373 } 374 if len(pending) == 0 { 375 return nil 376 } 377 params, err := filesystemAttachmentParams(ctx, pending) 378 if err != nil { 379 return errors.Trace(err) 380 } 381 for i, params := range params { 382 if params.Machine != nil && params.Machine.Kind() != names.MachineTagKind { 383 logger.Debugf("not queuing filesystem attachment for non-machine %v", params.Machine) 384 continue 385 } 386 updatePendingFilesystemAttachment(ctx, pending[i], params) 387 } 388 return nil 389 } 390 391 // filesystemAttachmentParams obtains the specified attachments' parameters. 392 func filesystemAttachmentParams( 393 ctx *context, ids []params.MachineStorageId, 394 ) ([]storage.FilesystemAttachmentParams, error) { 395 paramsResults, err := ctx.config.Filesystems.FilesystemAttachmentParams(ids) 396 if err != nil { 397 return nil, errors.Annotate(err, "getting filesystem attachment params") 398 } 399 attachmentParams := make([]storage.FilesystemAttachmentParams, len(ids)) 400 for i, result := range paramsResults { 401 if result.Error != nil { 402 return nil, errors.Annotate(result.Error, "getting filesystem attachment parameters") 403 } 404 params, err := filesystemAttachmentParamsFromParams(result.Result) 405 if err != nil { 406 return nil, errors.Annotate(err, "getting filesystem attachment parameters") 407 } 408 attachmentParams[i] = params 409 } 410 return attachmentParams, nil 411 } 412 413 // filesystemParams obtains the specified filesystems' parameters. 414 func filesystemParams(ctx *context, tags []names.FilesystemTag) ([]storage.FilesystemParams, error) { 415 paramsResults, err := ctx.config.Filesystems.FilesystemParams(tags) 416 if err != nil { 417 return nil, errors.Annotate(err, "getting filesystem params") 418 } 419 allParams := make([]storage.FilesystemParams, len(tags)) 420 for i, result := range paramsResults { 421 if result.Error != nil { 422 return nil, errors.Annotate(result.Error, "getting filesystem parameters") 423 } 424 params, err := filesystemParamsFromParams(result.Result) 425 if err != nil { 426 return nil, errors.Annotate(err, "getting filesystem parameters") 427 } 428 allParams[i] = params 429 } 430 return allParams, nil 431 } 432 433 // removeFilesystemParams obtains the specified filesystems' destruction parameters. 434 func removeFilesystemParams(ctx *context, tags []names.FilesystemTag) ([]params.RemoveFilesystemParams, error) { 435 paramsResults, err := ctx.config.Filesystems.RemoveFilesystemParams(tags) 436 if err != nil { 437 return nil, errors.Annotate(err, "getting filesystem params") 438 } 439 allParams := make([]params.RemoveFilesystemParams, len(tags)) 440 for i, result := range paramsResults { 441 if result.Error != nil { 442 return nil, errors.Annotate(result.Error, "getting filesystem removal parameters") 443 } 444 allParams[i] = result.Result 445 } 446 return allParams, nil 447 } 448 449 func filesystemFromParams(in params.Filesystem) (storage.Filesystem, error) { 450 filesystemTag, err := names.ParseFilesystemTag(in.FilesystemTag) 451 if err != nil { 452 return storage.Filesystem{}, errors.Trace(err) 453 } 454 var volumeTag names.VolumeTag 455 if in.VolumeTag != "" { 456 volumeTag, err = names.ParseVolumeTag(in.VolumeTag) 457 if err != nil { 458 return storage.Filesystem{}, errors.Trace(err) 459 } 460 } 461 return storage.Filesystem{ 462 filesystemTag, 463 volumeTag, 464 storage.FilesystemInfo{ 465 in.Info.FilesystemId, 466 in.Info.Size, 467 }, 468 }, nil 469 } 470 471 func filesystemParamsFromParams(in params.FilesystemParams) (storage.FilesystemParams, error) { 472 filesystemTag, err := names.ParseFilesystemTag(in.FilesystemTag) 473 if err != nil { 474 return storage.FilesystemParams{}, errors.Trace(err) 475 } 476 var volumeTag names.VolumeTag 477 if in.VolumeTag != "" { 478 volumeTag, err = names.ParseVolumeTag(in.VolumeTag) 479 if err != nil { 480 return storage.FilesystemParams{}, errors.Trace(err) 481 } 482 } 483 providerType := storage.ProviderType(in.Provider) 484 return storage.FilesystemParams{ 485 Tag: filesystemTag, 486 Volume: volumeTag, 487 Size: in.Size, 488 Provider: providerType, 489 Attributes: in.Attributes, 490 ResourceTags: in.Tags, 491 }, nil 492 } 493 494 func filesystemAttachmentParamsFromParams(in params.FilesystemAttachmentParams) (storage.FilesystemAttachmentParams, error) { 495 hostTag, err := names.ParseTag(in.MachineTag) 496 if err != nil { 497 return storage.FilesystemAttachmentParams{}, errors.Trace(err) 498 } 499 filesystemTag, err := names.ParseFilesystemTag(in.FilesystemTag) 500 if err != nil { 501 return storage.FilesystemAttachmentParams{}, errors.Trace(err) 502 } 503 return storage.FilesystemAttachmentParams{ 504 AttachmentParams: storage.AttachmentParams{ 505 Provider: storage.ProviderType(in.Provider), 506 Machine: hostTag, 507 InstanceId: instance.Id(in.InstanceId), 508 ReadOnly: in.ReadOnly, 509 }, 510 Filesystem: filesystemTag, 511 FilesystemId: in.FilesystemId, 512 Path: in.MountPoint, 513 }, nil 514 }