github.com/dahs81/otto@v0.2.1-0.20160126165905-6400716cf085/helper/vagrant/layered.go (about) 1 package vagrant 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "io" 8 "log" 9 "os" 10 "path/filepath" 11 "time" 12 13 "github.com/boltdb/bolt" 14 "github.com/hashicorp/otto/context" 15 "github.com/hashicorp/terraform/dag" 16 ) 17 18 // Layered is a Vagrant environment that is created using a series of 19 // "layers". Otto manages these layers and this library automatically prunes 20 // unused layers. This library will also do the multi-process locking 21 // necessary to prevent races. 22 // 23 // To update a layer (change it), you should create a Layer with a new ID. 24 // IDs should be considered immutable for all time. This is to prevent breaking 25 // other environments. Once a layer is safely no longer in use by anybody 26 // for a sufficient period of time, Otto will automatically prune it. 27 // 28 // Layered itself doesn't manage the final Vagrant environment. This should 29 // be done outside of this using functions like Dev. Accounting should be done 30 // to avoid layers being pruned with `AddLeaf`, `RemoveLeaf`. If these 31 // aren't called layers underneath may be pruned which can corrupt leaves. 32 type Layered struct { 33 // Layers are layers that are important for this run. This must include 34 // all the Vagrantfiles for all the potential layers since we might need 35 // to run all of them. 36 Layers []*Layer 37 38 // DataDir is the directory where Layered can write data to. 39 DataDir string 40 } 41 42 // Layer is a single layer of the Layered Vagrant environment. 43 type Layer struct { 44 // ID is a unique ID for the layer. See the note in Layered about 45 // generating a new ID for every change/iteration in the Vagrantfile. 46 ID string 47 48 // Vagrantfile is the path to the Vagrantfile to bring up for this 49 // layer. The Vagrantfile should handle all provisioning. This 50 // Vagrantfile will be copied to another directory, so any paths 51 // in it should be relative to the Vagrantfile. 52 Vagrantfile string 53 } 54 55 // Graph will return the full graph that is currently encoded. 56 func (l *Layered) Graph() (*dag.AcyclicGraph, error) { 57 db, err := l.db() 58 if err != nil { 59 return nil, err 60 } 61 defer db.Close() 62 63 return l.graph(db) 64 } 65 66 // Build will build all the layers that are defined in this Layered 67 // struct. It will automatically output to the UI as needed. 68 // 69 // This will automatically acquire a process-lock to ensure that no duplicate 70 // layers are ever built. The process lock usually assumes that Otto is 71 // being run by the same user. 72 func (l *Layered) Build(ctx *context.Shared) error { 73 // Grab the DB and initialize all the layers. This just inserts a 74 // pending layer if it doesn't exist, as well as sets up the edges. 75 db, err := l.db() 76 if err != nil { 77 return err 78 } 79 vs, err := l.init(db) 80 db.Close() 81 if err != nil { 82 return err 83 } 84 85 // Go through each layer and build it. This will be a no-op if the 86 // layer is already built. 87 for i, v := range vs { 88 var last *layerVertex 89 if i > 0 { 90 last = vs[i-1] 91 } 92 93 if err := l.buildLayer(v, last, ctx); err != nil { 94 return err 95 } 96 } 97 98 return nil 99 } 100 101 // Prune will destroy all layers that haven't been used in a certain 102 // amount of time. 103 // 104 // TODO: "certain amount of time" for now we just prune any orphans 105 func (l *Layered) Prune(ctx *context.Shared) (int, error) { 106 db, err := l.db() 107 if err != nil { 108 return 0, err 109 } 110 defer db.Close() 111 112 graph, err := l.graph(db) 113 if err != nil { 114 return 0, err 115 } 116 117 log.Printf("[DEBUG] vagrant: layer graph: \n%s", graph.String()) 118 119 // Get all the bad roots. These are anything without something depending 120 // on it except for the main "root" 121 roots := make([]dag.Vertex, 0) 122 for _, v := range graph.Vertices() { 123 if v == "root" { 124 continue 125 } 126 if graph.UpEdges(v).Len() == 0 { 127 roots = append(roots, v) 128 } 129 } 130 if len(roots) == 0 { 131 return 0, nil 132 } 133 134 // Go through the remaining roots, these are the environments 135 // that must be destroyed. 136 count := 0 137 for _, root := range roots { 138 err := graph.DepthFirstWalk([]dag.Vertex{root}, 139 func(v dag.Vertex, depth int) error { 140 if err := l.pruneLayer(db, v.(*layerVertex), ctx); err != nil { 141 return err 142 } 143 144 count++ 145 return nil 146 }) 147 if err != nil { 148 return count, err 149 } 150 } 151 152 return count, nil 153 } 154 155 // ConfigureEnv configures the Vagrant instance with the proper environment 156 // variables to be able to execute things. 157 // 158 // Once the env is used, SetEnvStatus should be used to modify the env 159 // status around it. This is critical to make sure layers don't get pruned. 160 func (l *Layered) ConfigureEnv(v *Vagrant) error { 161 // Get the final layer 162 layer := l.Layers[len(l.Layers)-1] 163 164 // Get the path for the final layer and add it to the environment 165 path := filepath.Join(l.layerPath(layer), "Vagrantfile") 166 if v.Env == nil { 167 v.Env = make(map[string]string) 168 } 169 v.Env[layerID] = layer.ID 170 v.Env[layerPathEnv] = path 171 172 return nil 173 } 174 175 // SetEnv configures the status of an env, persisting that its ready or 176 // deleted which controls whether layers get pruned or not. 177 // 178 // The Vagrant pointer given must already be configured using ConfigureEnv. 179 func (l *Layered) SetEnv(v *Vagrant, state envState) error { 180 // Update the DB with our environment 181 db, err := l.db() 182 if err != nil { 183 return err 184 } 185 defer db.Close() 186 return db.Update(func(tx *bolt.Tx) error { 187 bucket := tx.Bucket(boltEnvsBucket) 188 key := []byte(v.DataDir) 189 190 // If the state is deleted then call it good 191 if state == envStateDeleted { 192 return bucket.Delete(key) 193 } 194 195 // Otherwise we're inserting 196 layerId, ok := v.Env[layerID] 197 if !ok { 198 return fmt.Errorf("Vagrant environment not configured with layer ID.") 199 } 200 201 return bucket.Put(key, []byte(layerId)) 202 }) 203 } 204 205 // RemoveEnv will remove the environment from the tracked layers. 206 func (l *Layered) RemoveEnv(v *Vagrant) error { 207 db, err := l.db() 208 if err != nil { 209 return err 210 } 211 defer db.Close() 212 213 return db.Update(func(tx *bolt.Tx) error { 214 bucket := tx.Bucket(boltEnvsBucket) 215 key := []byte(v.DataDir) 216 return bucket.Delete(key) 217 }) 218 } 219 220 // Pending returns a list of layers that are pending creation. 221 // Note that between calling this and calling something like Build(), 222 // this state may be different. 223 func (l *Layered) Pending() ([]string, error) { 224 // Grab the DB and initialize all the layers. This just inserts a 225 // pending layer if it doesn't exist, as well as sets up the edges. 226 db, err := l.db() 227 if err != nil { 228 return nil, err 229 } 230 vs, err := l.init(db) 231 db.Close() 232 if err != nil { 233 return nil, err 234 } 235 236 result := make([]string, 0, len(vs)) 237 for _, v := range vs { 238 if v.State != layerStateReady { 239 result = append(result, v.Layer.ID) 240 } 241 } 242 243 return result, nil 244 } 245 246 func (l *Layered) buildLayer(v *layerVertex, lastV *layerVertex, ctx *context.Shared) error { 247 log.Printf("[DEBUG] vagrant: building layer: %s", v.Layer.ID) 248 249 layer := v.Layer 250 path := v.Path 251 252 // Build the Vagrant instance. We use this a bit later 253 vagrant := &Vagrant{ 254 Dir: path, 255 DataDir: filepath.Join(path, ".vagrant"), 256 Ui: ctx.Ui, 257 } 258 if lastV != nil { 259 vagrant.Env = map[string]string{ 260 layerPathEnv: filepath.Join(lastV.Path, "Vagrantfile"), 261 } 262 } 263 264 // Layer isn't ready, so grab the lock on the layer and build it 265 // TODO: multi-process lock 266 267 // Once we have the lock, we check shortly in the DB if it is already 268 // ready. If it is ready, we yield the lock and we're done! 269 db, err := l.db() 270 if err != nil { 271 return err 272 } 273 layerV, err := l.readLayer(db, layer) 274 if err != nil { 275 db.Close() 276 return err 277 } 278 if layerV.State == layerStateReady { 279 log.Printf("[DEBUG] vagrant: layer already ready, will verify: %s", v.Layer.ID) 280 281 // Touch the layer so that it is recently used, even if its not 282 // actually ready. 283 err = l.updateLayer(db, layer, func(v *layerVertex) { 284 v.Touch() 285 }) 286 if err != nil { 287 db.Close() 288 return err 289 } 290 291 // Verify the layer is actually ready! If it isn't, then 292 // we have to recreate it. 293 ctx.Ui.Header(fmt.Sprintf("Verifying created layer: %s", layer.ID)) 294 ok, err := l.verifyLayer(vagrant) 295 if ok || err != nil { 296 // It is ready or there is an error. 297 db.Close() 298 return err 299 } 300 301 // Layer is invalid! Delete it from the DB and then recreate it 302 err = l.updateLayer(db, layer, func(v *layerVertex) { 303 v.State = layerStatePending 304 }) 305 if err != nil { 306 db.Close() 307 return err 308 } 309 310 // Continue! 311 } 312 db.Close() 313 314 // Tell the user things are happening 315 ctx.Ui.Header(fmt.Sprintf("Creating layer: %s", layer.ID)) 316 317 // Prepare the build directory 318 if err := os.MkdirAll(path, 0755); err != nil { 319 return err 320 } 321 322 // Copy the Vagrantfile into the destination path 323 src, err := os.Open(layer.Vagrantfile) 324 if err != nil { 325 return err 326 } 327 dst, err := os.Create(filepath.Join(path, "Vagrantfile")) 328 if err == nil { 329 _, err = io.Copy(dst, src) 330 } 331 src.Close() 332 dst.Close() 333 if err != nil { 334 return err 335 } 336 337 // Destroy and recreate the machine 338 if err := vagrant.ExecuteSilent("destroy", "-f"); err != nil { 339 return err 340 } 341 if err := vagrant.Execute("up"); err != nil { 342 return err 343 } 344 if err := vagrant.Execute("halt"); err != nil { 345 return err 346 } 347 348 // Update the layer state that it is "ready" 349 db, err = l.db() 350 if err != nil { 351 return err 352 } 353 defer db.Close() 354 355 return l.updateLayer(db, layer, func(v *layerVertex) { 356 v.State = layerStateReady 357 v.Touch() 358 }) 359 } 360 361 func (l *Layered) pruneLayer(db *bolt.DB, v *layerVertex, ctx *context.Shared) error { 362 log.Printf("[DEBUG] vagrant: pruning layer: %s", v.Layer.ID) 363 364 layer := v.Layer 365 path := v.Path 366 367 // First check if the layer even exists 368 exists, err := l.checkLayer(db, layer) 369 if err != nil { 370 return err 371 } 372 if !exists { 373 log.Printf("[DEBUG] vagrant: layer doesn't exist already: %s", v.Layer.ID) 374 return l.deleteLayer(db, layer, path) 375 } 376 377 ctx.Ui.Header(fmt.Sprintf( 378 "Deleting layer '%s'...", layer.ID)) 379 380 // First, note that the layer is no longer ready 381 err = l.updateLayer(db, layer, func(v *layerVertex) { 382 v.State = layerStatePending 383 }) 384 if err != nil { 385 return err 386 } 387 388 // Check the path. If the path doesn't exist, then it is already destroyed. 389 // If the path does exist, then we do an actual vagrant destroy 390 _, err = os.Stat(path) 391 if err != nil && !os.IsNotExist(err) { 392 return err 393 } 394 if err == nil { 395 vagrant := &Vagrant{ 396 Dir: path, 397 DataDir: filepath.Join(path, ".vagrant"), 398 Ui: ctx.Ui, 399 } 400 if err := vagrant.Execute("destroy", "-f"); err != nil { 401 return err 402 } 403 } 404 405 // Delete the layer 406 return l.deleteLayer(db, layer, path) 407 } 408 409 func (l *Layered) layerPath(layer *Layer) string { 410 return filepath.Join(l.DataDir, "layers", layer.ID) 411 } 412 413 // db returns the database handle, and sets up the DB if it has never been created. 414 func (l *Layered) db() (*bolt.DB, error) { 415 // Make the directory to store our DB 416 if err := os.MkdirAll(l.DataDir, 0755); err != nil { 417 return nil, err 418 } 419 420 // Create/Open the DB 421 db, err := bolt.Open(filepath.Join(l.DataDir, "vagrant-layered.db"), 0644, nil) 422 if err != nil { 423 return nil, err 424 } 425 426 // Create the buckets 427 err = db.Update(func(tx *bolt.Tx) error { 428 for _, b := range boltBuckets { 429 if _, err := tx.CreateBucketIfNotExists(b); err != nil { 430 return err 431 } 432 } 433 434 return nil 435 }) 436 if err != nil { 437 return nil, err 438 } 439 440 // Check the data version 441 var version byte 442 err = db.Update(func(tx *bolt.Tx) error { 443 bucket := tx.Bucket(boltVagrantBucket) 444 data := bucket.Get([]byte("version")) 445 if data == nil || len(data) == 0 { 446 version = boltDataVersion 447 return bucket.Put([]byte("version"), []byte{boltDataVersion}) 448 } 449 450 version = data[0] 451 return nil 452 }) 453 if err != nil { 454 return nil, err 455 } 456 457 if version > boltDataVersion { 458 return nil, fmt.Errorf( 459 "Vagrant layer data version is higher than this version of Otto knows how\n"+ 460 "to handle! This version of Otto can read up to version %d,\n"+ 461 "but version %d data file found.\n\n"+ 462 "This means that a newer version of Otto touched this data,\n"+ 463 "or the data was corrupted in some other way.", 464 boltDataVersion, version) 465 } 466 467 return db, nil 468 } 469 470 // verifyLayer verifies that a layer is valid/ready. 471 func (l *Layered) verifyLayer(v *Vagrant) (bool, error) { 472 // The callback for checking the state 473 var ok bool 474 cb := func(o *Output) { 475 if o.Type == "state" && len(o.Data) > 0 { 476 ok = o.Data[0] != "not_created" 477 } 478 } 479 480 // Save the old callbacks 481 oldCb := v.Callbacks 482 defer func() { v.Callbacks = oldCb }() 483 484 // Register a callback for the state 485 v.Callbacks = map[string]OutputCallback{"state": cb} 486 487 // Check it 488 err := v.ExecuteSilent("status") 489 return ok, err 490 } 491 492 // init initializes the database for this layer setup. 493 func (l *Layered) init(db *bolt.DB) ([]*layerVertex, error) { 494 layerVertices := make([]*layerVertex, len(l.Layers)) 495 for i, layer := range l.Layers { 496 var parent *Layer 497 if i > 0 { 498 parent = l.Layers[i-1] 499 } 500 501 layerVertex, err := l.initLayer(db, layer, parent) 502 if err != nil { 503 return nil, err 504 } 505 506 layerVertices[i] = layerVertex 507 if parent != nil { 508 // We have a prior layer, so setup the edge pointer 509 err = db.Update(func(tx *bolt.Tx) error { 510 bucket := tx.Bucket(boltEdgesBucket) 511 return bucket.Put( 512 []byte(layer.ID), 513 []byte(parent.ID)) 514 }) 515 if err != nil { 516 return nil, err 517 } 518 } 519 } 520 521 return layerVertices, nil 522 } 523 524 // initLayer sets up the layer in the database 525 func (l *Layered) initLayer(db *bolt.DB, layer *Layer, parent *Layer) (*layerVertex, error) { 526 var parentID string 527 if parent != nil { 528 parentID = parent.ID 529 } 530 531 var result layerVertex 532 err := db.Update(func(tx *bolt.Tx) error { 533 bucket := tx.Bucket(boltLayersBucket) 534 key := []byte(layer.ID) 535 data := bucket.Get(key) 536 if len(data) > 0 { 537 var v layerVertex 538 if err := l.structRead(&v, data); err != nil { 539 return err 540 } 541 542 if v.Parent == parentID { 543 result = v 544 return nil 545 } 546 547 // The parent didn't match, so we just initialize a new 548 // entry below. This will also force the destruction of the 549 // old environment. 550 } 551 552 // Vertex doesn't exist. Create it and save it 553 result = layerVertex{ 554 Layer: layer, 555 State: layerStatePending, 556 Parent: parentID, 557 Path: l.layerPath(layer), 558 } 559 data, err := l.structData(&result) 560 if err != nil { 561 return err 562 } 563 564 // Write the pending layer 565 return bucket.Put(key, data) 566 }) 567 568 return &result, err 569 } 570 571 func (l *Layered) checkLayer(db *bolt.DB, layer *Layer) (bool, error) { 572 var result bool 573 err := db.View(func(tx *bolt.Tx) error { 574 bucket := tx.Bucket(boltLayersBucket) 575 key := []byte(layer.ID) 576 data := bucket.Get(key) 577 result = len(data) > 0 578 return nil 579 }) 580 581 return result, err 582 } 583 584 func (l *Layered) readLayer(db *bolt.DB, layer *Layer) (*layerVertex, error) { 585 var result layerVertex 586 err := db.View(func(tx *bolt.Tx) error { 587 bucket := tx.Bucket(boltLayersBucket) 588 key := []byte(layer.ID) 589 data := bucket.Get(key) 590 if len(data) > 0 { 591 return l.structRead(&result, data) 592 } 593 594 return fmt.Errorf("layer %s not found", layer.ID) 595 }) 596 597 return &result, err 598 } 599 600 func (l *Layered) updateLayer(db *bolt.DB, layer *Layer, f func(*layerVertex)) error { 601 return db.Update(func(tx *bolt.Tx) error { 602 bucket := tx.Bucket(boltLayersBucket) 603 key := []byte(layer.ID) 604 data := bucket.Get(key) 605 if len(data) == 0 { 606 // This should never happen through this struct 607 panic(fmt.Errorf("layer %s not found", layer.ID)) 608 } 609 610 // Read the vertex, call the function to modify it 611 var v layerVertex 612 if err := l.structRead(&v, data); err != nil { 613 return err 614 } 615 f(&v) 616 617 // Save the resulting layer data 618 data, err := l.structData(&v) 619 if err != nil { 620 return err 621 } 622 return bucket.Put(key, data) 623 }) 624 } 625 626 func (l *Layered) deleteLayer(db *bolt.DB, layer *Layer, path string) error { 627 if err := os.RemoveAll(path); err != nil { 628 return err 629 } 630 631 return db.Update(func(tx *bolt.Tx) error { 632 // Delete the layer itself 633 bucket := tx.Bucket(boltLayersBucket) 634 key := []byte(layer.ID) 635 if err := bucket.Delete(key); err != nil { 636 return err 637 } 638 639 // Delete all the edges 640 bucket = tx.Bucket(boltEdgesBucket) 641 if err := bucket.Delete(key); err != nil { 642 return err 643 } 644 645 // Find any values 646 return bucket.ForEach(func(k, data []byte) error { 647 if string(data) == layer.ID { 648 return bucket.Delete(k) 649 } 650 651 return nil 652 }) 653 }) 654 } 655 656 func (l *Layered) graph(db *bolt.DB) (*dag.AcyclicGraph, error) { 657 graph := new(dag.AcyclicGraph) 658 graph.Add("root") 659 660 // First, add all the layers 661 layers := make(map[string]*layerVertex) 662 err := db.View(func(tx *bolt.Tx) error { 663 bucket := tx.Bucket(boltLayersBucket) 664 return bucket.ForEach(func(k, data []byte) error { 665 var v layerVertex 666 if err := l.structRead(&v, data); err != nil { 667 return err 668 } 669 670 // Add this layer to the graph 671 graph.Add(&v) 672 673 // Store the mapping for later 674 layers[v.Layer.ID] = &v 675 return nil 676 }) 677 }) 678 if err != nil { 679 return nil, err 680 } 681 682 // Next, connect the layers 683 err = db.View(func(tx *bolt.Tx) error { 684 bucket := tx.Bucket(boltEdgesBucket) 685 return bucket.ForEach(func(k, data []byte) error { 686 from := layers[string(k)] 687 to := layers[string(data)] 688 if from != nil && to != nil { 689 graph.Connect(dag.BasicEdge(from, to)) 690 } 691 692 return nil 693 }) 694 }) 695 if err != nil { 696 return nil, err 697 } 698 699 // Finally, add and connect all the envs 700 err = db.View(func(tx *bolt.Tx) error { 701 bucket := tx.Bucket(boltEnvsBucket) 702 return bucket.ForEach(func(k, data []byte) error { 703 key := fmt.Sprintf("env-%s", string(k)) 704 graph.Add(key) 705 706 // Connect the env to the layer it depends on 707 to := &layerVertex{Layer: &Layer{ID: string(data)}} 708 graph.Connect(dag.BasicEdge(key, to)) 709 710 // Connect the root to the environment that is active 711 graph.Connect(dag.BasicEdge("root", key)) 712 return nil 713 }) 714 }) 715 if err != nil { 716 return nil, err 717 } 718 719 return graph, nil 720 } 721 722 func (l *Layered) structData(d interface{}) ([]byte, error) { 723 // Let's just output it in human-readable format to make it easy 724 // for debugging. Disk space won't matter that much for this data. 725 return json.MarshalIndent(d, "", "\t") 726 } 727 728 func (l *Layered) structRead(d interface{}, raw []byte) error { 729 dec := json.NewDecoder(bytes.NewReader(raw)) 730 return dec.Decode(d) 731 } 732 733 var ( 734 boltVagrantBucket = []byte("vagrant") 735 boltLayersBucket = []byte("layers") 736 boltEdgesBucket = []byte("edges") 737 boltEnvsBucket = []byte("envs") 738 boltBuckets = [][]byte{ 739 boltVagrantBucket, 740 boltLayersBucket, 741 boltEdgesBucket, 742 boltEnvsBucket, 743 } 744 ) 745 746 var ( 747 boltDataVersion byte = 1 748 ) 749 750 const ( 751 // layerPathEnv is the path to the previous layer 752 layerPathEnv = "OTTO_VAGRANT_LAYER_PATH" 753 754 // layerID is the ID of the previous layer 755 layerID = "OTTO_VAGRANT_LAYER_ID" 756 ) 757 758 // layerVertex is the type of vertex in the graph that is used to track 759 // layer usage throughout Otto. 760 type layerVertex struct { 761 Layer *Layer `json:"layer"` 762 State layerState `json:"state"` 763 Parent string `json:"parent"` 764 Path string `json:"path"` 765 LastUsed time.Time `json:"last_used"` 766 } 767 768 func (v *layerVertex) Hashcode() interface{} { 769 return fmt.Sprintf("layer-%s", v.Layer.ID) 770 } 771 772 func (v *layerVertex) Name() string { 773 return v.Layer.ID 774 } 775 776 // Touch is used to update the last used time 777 func (v *layerVertex) Touch() { 778 v.LastUsed = time.Now().UTC() 779 } 780 781 type layerState byte 782 783 const ( 784 layerStateInvalid layerState = iota 785 layerStatePending 786 layerStateReady 787 ) 788 789 type envState byte 790 791 const ( 792 envStateInvalid envState = iota 793 envStateDeleted 794 envStateReady 795 )