github.com/konsorten/ktn-build-info@v1.0.11/ver/consul_build_id.go (about) 1 package ver 2 3 import ( 4 "fmt" 5 "net/url" 6 "strconv" 7 8 "github.com/hashicorp/consul/api" 9 ) 10 11 func RetrieveBuildFromConsul(consulUrl string, kvProjectRoot string, vi *VersionInformation) error { 12 // check version info 13 if kvProjectRoot == "" { 14 return fmt.Errorf("Consul KV project root path may not be empty") 15 } 16 17 if vi.Revision == "" { 18 return fmt.Errorf("No revision information available") 19 } 20 21 // parse the url 22 cu, err := url.Parse(consulUrl) 23 24 if err != nil { 25 return fmt.Errorf("Failed to parse consul URL: %v", err) 26 } 27 28 // assign config 29 cfg := api.DefaultConfig() 30 31 cfg.Address = cu.Host 32 cfg.Scheme = cu.Scheme 33 cfg.Datacenter = cu.Path[1:] // trim first slash 34 35 if cu.User != nil { 36 if t, ok := cu.User.Password(); ok { 37 cfg.Token = t 38 } 39 } 40 41 // connect 42 client, err := api.NewClient(cfg) 43 44 if err != nil { 45 return fmt.Errorf("Failed to connect to consul: %v", err) 46 } 47 48 kv := client.KV() 49 50 // create session 51 session, _, err := client.Session().Create(&api.SessionEntry{TTL: "60s", Behavior: "delete"}, nil) 52 53 if err != nil { 54 return fmt.Errorf("Failed to create session: %v", err) 55 } 56 57 defer client.Session().Destroy(session, nil) 58 59 // acquire revision 60 revPath := fmt.Sprintf("%v/revs/%v", kvProjectRoot, vi.Revision) 61 lockPath := fmt.Sprintf("%v/~lock", revPath) 62 63 locked, _, err := kv.Acquire(&api.KVPair{Key: lockPath, Session: session}, nil) 64 65 if err != nil { 66 return fmt.Errorf("Failed to acquire lock: %v", err) 67 } 68 69 if !locked { 70 return fmt.Errorf("Failed to acquire lock on KV path: %v", lockPath) 71 } 72 73 defer kv.Release(&api.KVPair{Key: lockPath, Session: session}, nil) 74 75 // retrieve any existing 76 build, _, err := kv.Get(fmt.Sprintf("%v/build", revPath), nil) 77 78 if err != nil { 79 return fmt.Errorf("Failed to retrieve existing build id: %v", err) 80 } 81 82 if build != nil { 83 b, err := strconv.Atoi(string(build.Value)) 84 85 if err != nil { 86 return fmt.Errorf("Failed to parse existing build id: %v", err) 87 } 88 89 vi.Build = b 90 91 return nil 92 } 93 94 // get new build id 95 for true { 96 nextIdPath := fmt.Sprintf("%v/nextId", kvProjectRoot) 97 98 nextId, _, err := kv.Get(nextIdPath, nil) 99 100 if err != nil { 101 return fmt.Errorf("Failed to get next build id: %v", err) 102 } 103 104 if nextId != nil { 105 nextId.Session = session 106 107 // parse the id 108 vi.Build, err = strconv.Atoi(string(nextId.Value)) 109 110 if err != nil { 111 return fmt.Errorf("Failed to parse next build id: %v", err) 112 113 } 114 115 // increment the id 116 nextId.Value = []byte(strconv.Itoa(vi.Build + 1)) 117 } else { 118 // use 1 as first ID, and register 2 as the next one 119 vi.Build = 1 120 121 nextId = &api.KVPair{Key: nextIdPath, Value: []byte("2"), Session: session} 122 } 123 124 // update the id 125 ok, _, err := kv.CAS(nextId, nil) 126 127 if ok { 128 break 129 } 130 } 131 132 // write the revision 133 _, err = kv.Put(&api.KVPair{ 134 Key: fmt.Sprintf("%v/build", revPath), 135 Value: []byte(strconv.Itoa(vi.Build)), 136 Session: session, 137 }, nil) 138 139 if err != nil { 140 return fmt.Errorf("Failed to write build revision: %v", err) 141 } 142 143 // done 144 return nil 145 }