├── .gitignore ├── LICENSE ├── README.md ├── go.mod ├── go.sum ├── main.go ├── metadata ├── change.go ├── metadata.go ├── types.go └── utils.go └── util └── selector_util.go /.gitignore: -------------------------------------------------------------------------------- 1 | go-rancher-metadata 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Go bindings for Rancher-metadata 2 | 3 | This library is incomplete, but implements a variety of calls against [rancher-metadata](https://github.com/rancher/rancher-metadata) service 4 | 5 | #Example usage 6 | 7 | ```go 8 | package main 9 | 10 | import ( 11 | "time" 12 | 13 | "github.com/rancher/go-rancher-metadata/metadata" 14 | "github.com/sirupsen/logrus" 15 | ) 16 | 17 | const ( 18 | metadataUrl = "http://rancher-metadata/2015-12-19" 19 | ) 20 | 21 | func main() { 22 | 23 | m := metadata.NewClient(metadataUrl) 24 | 25 | version := "init" 26 | 27 | for { 28 | newVersion, err := m.GetVersion() 29 | if err != nil { 30 | logrus.Errorf("Error reading metadata version: %v", err) 31 | } else if version == newVersion { 32 | logrus.Debug("No changes in metadata version") 33 | } else { 34 | logrus.Debugf("Metadata Version has been changed. Old version: %s. New version: %s.", version, newVersion) 35 | version = newVersion 36 | } 37 | time.Sleep(5 * time.Second) 38 | } 39 | } 40 | ``` 41 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/rancher/go-rancher-metadata 2 | 3 | go 1.13 4 | 5 | require github.com/sirupsen/logrus v1.4.2 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= 4 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 5 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 6 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 7 | github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= 8 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 9 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 10 | github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= 11 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 12 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= 13 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 14 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/rancher/go-rancher-metadata/metadata" 7 | "github.com/sirupsen/logrus" 8 | ) 9 | 10 | const ( 11 | metadataUrl = "http://rancher-metadata/2015-12-19" 12 | ) 13 | 14 | func main() { 15 | m := metadata.NewClient(metadataUrl) 16 | 17 | version := "init" 18 | 19 | for { 20 | newVersion, err := m.GetVersion() 21 | if err != nil { 22 | logrus.Errorf("Error reading metadata version: %v", err) 23 | } else if version == newVersion { 24 | logrus.Debug("No changes in metadata version") 25 | } else { 26 | logrus.Debugf("Metadata version has changed, oldVersion=[%s], newVersion=[%s]", version, newVersion) 27 | version = newVersion 28 | } 29 | time.Sleep(5 * time.Second) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /metadata/change.go: -------------------------------------------------------------------------------- 1 | package metadata 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "time" 8 | 9 | "github.com/sirupsen/logrus" 10 | ) 11 | 12 | type timeout interface { 13 | Timeout() bool 14 | } 15 | 16 | func (m *client) OnChangeWithError(intervalSeconds int, do func(string)) error { 17 | return m.onChangeFromVersionWithError("init", intervalSeconds, do) 18 | } 19 | 20 | func (m *client) OnChange(intervalSeconds int, do func(string)) { 21 | version := "init" 22 | updateVersionAndDo := func(v string) { 23 | version = v 24 | do(version) 25 | } 26 | interval := time.Duration(intervalSeconds) 27 | for { 28 | if err := m.onChangeFromVersionWithError(version, intervalSeconds, updateVersionAndDo); err != nil { 29 | logrus.Errorf("Error reading metadata version: %v", err) 30 | } 31 | time.Sleep(interval * time.Second) 32 | } 33 | } 34 | 35 | func (m *client) onChangeFromVersionWithError(version string, intervalSeconds int, do func(string)) error { 36 | for { 37 | newVersion, err := m.waitVersion(intervalSeconds, version) 38 | if err != nil { 39 | return err 40 | } else if version == newVersion { 41 | logrus.Debug("No changes in metadata version") 42 | } else { 43 | logrus.Debugf("Metadata Version has been changed. Old version: %s. New version: %s.", version, newVersion) 44 | version = newVersion 45 | do(newVersion) 46 | } 47 | } 48 | } 49 | 50 | func (m *client) waitVersion(maxWait int, version string) (string, error) { 51 | for { 52 | resp, err := m.SendRequest(fmt.Sprintf("/version?wait=true&value=%s&maxWait=%d", version, maxWait)) 53 | if err != nil { 54 | t, ok := err.(timeout) 55 | if ok && t.Timeout() { 56 | continue 57 | } 58 | return "", err 59 | } 60 | 61 | err = json.Unmarshal(resp, &version) 62 | return version, err 63 | } 64 | } 65 | 66 | func (m *client) OnChangeCtx(ctx context.Context, intervalSeconds int, do func(string)) { 67 | m.onChangeFromVersionWithErrorCtx(ctx, "init", intervalSeconds, do) 68 | } 69 | 70 | func (m *client) onChangeFromVersionWithErrorCtx(ctx context.Context, version string, intervalSeconds int, do func(string)) { 71 | for { 72 | select { 73 | case <-ctx.Done(): 74 | return 75 | default: 76 | } 77 | 78 | newVersion, err := m.waitVersionCtx(ctx, intervalSeconds, version) 79 | if err != nil { 80 | t, ok := err.(timeout) 81 | if !ok || !t.Timeout() { 82 | logrus.Errorf("Error reading metadata version: %v", err) 83 | time.Sleep(time.Duration(intervalSeconds) * time.Second) 84 | } 85 | continue 86 | } 87 | 88 | if version == newVersion { 89 | logrus.Debug("No changes in metadata version") 90 | } else { 91 | logrus.Debugf("Metadata Version has been changed. Old version: %s. New version: %s.", version, newVersion) 92 | version = newVersion 93 | do(newVersion) 94 | } 95 | } 96 | } 97 | 98 | func (m *client) waitVersionCtx(ctx context.Context, maxWait int, version string) (string, error) { 99 | resp, err := m.SendRequestCtx(ctx, fmt.Sprintf("/version?wait=true&value=%s&maxWait=%d", version, maxWait)) 100 | if err != nil { 101 | return "", err 102 | } 103 | 104 | err = json.Unmarshal(resp, &version) 105 | return version, err 106 | } 107 | -------------------------------------------------------------------------------- /metadata/metadata.go: -------------------------------------------------------------------------------- 1 | package metadata 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "io/ioutil" 8 | "net/http" 9 | "time" 10 | ) 11 | 12 | type Client interface { 13 | OnChangeWithError(int, func(string)) error 14 | OnChange(int, func(string)) 15 | OnChangeCtx(context.Context, int, func(string)) 16 | SendRequest(string) ([]byte, error) 17 | GetVersion() (string, error) 18 | GetSelfHost() (Host, error) 19 | GetSelfContainer() (Container, error) 20 | GetSelfServiceByName(string) (Service, error) 21 | GetSelfService() (Service, error) 22 | GetSelfStack() (Stack, error) 23 | GetServices() ([]Service, error) 24 | GetStacks() ([]Stack, error) 25 | GetStackByName(string) (Stack, error) 26 | GetContainers() ([]Container, error) 27 | GetServiceContainers(string, string) ([]Container, error) 28 | GetHosts() ([]Host, error) 29 | GetHost(string) (Host, error) 30 | GetNetworks() ([]Network, error) 31 | } 32 | 33 | type client struct { 34 | url string 35 | ip string 36 | client *http.Client 37 | } 38 | 39 | func newClient(url, ip string) *client { 40 | return &client{url, ip, &http.Client{Timeout: 10 * time.Second}} 41 | } 42 | 43 | func NewClient(url string) Client { 44 | ip := "" 45 | return newClient(url, ip) 46 | } 47 | 48 | func NewClientWithIPAndWait(url, ip string) (Client, error) { 49 | client := newClient(url, ip) 50 | 51 | if err := testConnection(client); err != nil { 52 | return nil, err 53 | } 54 | 55 | return client, nil 56 | } 57 | 58 | func NewClientAndWait(url string) (Client, error) { 59 | ip := "" 60 | client := newClient(url, ip) 61 | 62 | if err := testConnection(client); err != nil { 63 | return nil, err 64 | } 65 | 66 | return client, nil 67 | } 68 | 69 | func (m *client) SendRequest(path string) ([]byte, error) { 70 | req, err := http.NewRequest("GET", m.url+path, nil) 71 | if err != nil { 72 | return nil, err 73 | } 74 | 75 | req.Header.Add("Accept", "application/json") 76 | if m.ip != "" { 77 | req.Header.Add("X-Forwarded-For", m.ip) 78 | } 79 | 80 | resp, err := m.client.Do(req) 81 | if err != nil { 82 | return nil, err 83 | } 84 | defer resp.Body.Close() 85 | 86 | if resp.StatusCode != 200 { 87 | return nil, fmt.Errorf("Error %v accessing %v path", resp.StatusCode, path) 88 | } 89 | 90 | body, err := ioutil.ReadAll(resp.Body) 91 | if err != nil { 92 | return nil, err 93 | } 94 | return body, nil 95 | } 96 | 97 | func (m *client) SendRequestCtx(ctx context.Context, path string) ([]byte, error) { 98 | req, err := http.NewRequest("GET", m.url+path, nil) 99 | if err != nil { 100 | return nil, err 101 | } 102 | 103 | req.Header.Add("Accept", "application/json") 104 | if m.ip != "" { 105 | req.Header.Add("X-Forwarded-For", m.ip) 106 | } 107 | 108 | resp, err := m.client.Do(req.WithContext(ctx)) 109 | if err != nil { 110 | return nil, err 111 | } 112 | defer resp.Body.Close() 113 | 114 | if resp.StatusCode != 200 { 115 | return nil, fmt.Errorf("error %v accessing %v path", resp.StatusCode, path) 116 | } 117 | 118 | body, err := ioutil.ReadAll(resp.Body) 119 | if err != nil { 120 | return nil, err 121 | } 122 | return body, nil 123 | } 124 | 125 | func (m *client) GetVersion() (string, error) { 126 | resp, err := m.SendRequest("/version") 127 | if err != nil { 128 | return "", err 129 | } 130 | return string(resp[:]), nil 131 | } 132 | 133 | func (m *client) GetSelfHost() (Host, error) { 134 | resp, err := m.SendRequest("/self/host") 135 | var host Host 136 | if err != nil { 137 | return host, err 138 | } 139 | 140 | if err = json.Unmarshal(resp, &host); err != nil { 141 | return host, err 142 | } 143 | 144 | return host, nil 145 | } 146 | 147 | func (m *client) GetSelfContainer() (Container, error) { 148 | resp, err := m.SendRequest("/self/container") 149 | var container Container 150 | if err != nil { 151 | return container, err 152 | } 153 | 154 | if err = json.Unmarshal(resp, &container); err != nil { 155 | return container, err 156 | } 157 | 158 | return container, nil 159 | } 160 | 161 | func (m *client) GetSelfServiceByName(name string) (Service, error) { 162 | resp, err := m.SendRequest("/self/stack/services/" + name) 163 | var service Service 164 | if err != nil { 165 | return service, err 166 | } 167 | 168 | if err = json.Unmarshal(resp, &service); err != nil { 169 | return service, err 170 | } 171 | 172 | return service, nil 173 | } 174 | 175 | func (m *client) GetSelfService() (Service, error) { 176 | resp, err := m.SendRequest("/self/service") 177 | var service Service 178 | if err != nil { 179 | return service, err 180 | } 181 | 182 | if err = json.Unmarshal(resp, &service); err != nil { 183 | return service, err 184 | } 185 | 186 | return service, nil 187 | } 188 | 189 | func (m *client) GetSelfStack() (Stack, error) { 190 | resp, err := m.SendRequest("/self/stack") 191 | var stack Stack 192 | if err != nil { 193 | return stack, err 194 | } 195 | 196 | if err = json.Unmarshal(resp, &stack); err != nil { 197 | return stack, err 198 | } 199 | 200 | return stack, nil 201 | } 202 | 203 | func (m *client) GetServices() ([]Service, error) { 204 | resp, err := m.SendRequest("/services") 205 | var services []Service 206 | if err != nil { 207 | return services, err 208 | } 209 | 210 | if err = json.Unmarshal(resp, &services); err != nil { 211 | return services, err 212 | } 213 | return services, nil 214 | } 215 | 216 | func (m *client) GetStacks() ([]Stack, error) { 217 | resp, err := m.SendRequest("/stacks") 218 | var stacks []Stack 219 | if err != nil { 220 | return stacks, err 221 | } 222 | 223 | if err = json.Unmarshal(resp, &stacks); err != nil { 224 | return stacks, err 225 | } 226 | return stacks, nil 227 | } 228 | 229 | func (m *client) GetStackByName(name string) (Stack, error) { 230 | resp, err := m.SendRequest("/stacks/" + name) 231 | var stack Stack 232 | if err != nil { 233 | return stack, err 234 | } 235 | 236 | if err = json.Unmarshal(resp, &stack); err != nil { 237 | return stack, err 238 | } 239 | 240 | return stack, nil 241 | } 242 | 243 | func (m *client) GetContainers() ([]Container, error) { 244 | resp, err := m.SendRequest("/containers") 245 | var containers []Container 246 | if err != nil { 247 | return containers, err 248 | } 249 | 250 | if err = json.Unmarshal(resp, &containers); err != nil { 251 | return containers, err 252 | } 253 | return containers, nil 254 | } 255 | 256 | func (m *client) GetServiceContainers(serviceName string, stackName string) ([]Container, error) { 257 | var serviceContainers = []Container{} 258 | containers, err := m.GetContainers() 259 | if err != nil { 260 | return serviceContainers, err 261 | } 262 | 263 | for _, container := range containers { 264 | if container.StackName == stackName && container.ServiceName == serviceName { 265 | serviceContainers = append(serviceContainers, container) 266 | } 267 | } 268 | 269 | return serviceContainers, nil 270 | } 271 | 272 | func (m *client) GetHosts() ([]Host, error) { 273 | resp, err := m.SendRequest("/hosts") 274 | var hosts []Host 275 | if err != nil { 276 | return hosts, err 277 | } 278 | 279 | if err = json.Unmarshal(resp, &hosts); err != nil { 280 | return hosts, err 281 | } 282 | return hosts, nil 283 | } 284 | 285 | func (m *client) GetHost(UUID string) (Host, error) { 286 | var host Host 287 | hosts, err := m.GetHosts() 288 | if err != nil { 289 | return host, err 290 | } 291 | for _, host := range hosts { 292 | if host.UUID == UUID { 293 | return host, nil 294 | } 295 | } 296 | 297 | return host, fmt.Errorf("could not find host by UUID %v", UUID) 298 | } 299 | 300 | func (m *client) GetNetworks() ([]Network, error) { 301 | resp, err := m.SendRequest("/networks") 302 | var networks []Network 303 | if err != nil { 304 | return networks, err 305 | } 306 | 307 | if err = json.Unmarshal(resp, &networks); err != nil { 308 | return networks, err 309 | } 310 | 311 | return networks, nil 312 | } 313 | -------------------------------------------------------------------------------- /metadata/types.go: -------------------------------------------------------------------------------- 1 | package metadata 2 | 3 | type Stack struct { 4 | EnvironmentName string `json:"environment_name"` 5 | EnvironmentUUID string `json:"environment_uuid"` 6 | Name string `json:"name"` 7 | UUID string `json:"uuid"` 8 | Services []Service `json:"services"` 9 | System bool `json:"system"` 10 | } 11 | 12 | type HealthCheck struct { 13 | HealthyThreshold int `json:"healthy_threshold"` 14 | Interval int `json:"interval"` 15 | Port int `json:"port"` 16 | RequestLine string `json:"request_line"` 17 | ResponseTimeout int `json:"response_timeout"` 18 | UnhealthyThreshold int `json:"unhealthy_threshold"` 19 | } 20 | 21 | type Service struct { 22 | Scale int `json:"scale"` 23 | Name string `json:"name"` 24 | StackName string `json:"stack_name"` 25 | StackUUID string `json:"stack_uuid"` 26 | Kind string `json:"kind"` 27 | Hostname string `json:"hostname"` 28 | Vip string `json:"vip"` 29 | CreateIndex int `json:"create_index"` 30 | UUID string `json:"uuid"` 31 | ExternalIps []string `json:"external_ips"` 32 | Sidekicks []string `json:"sidekicks"` 33 | Containers []Container `json:"containers"` 34 | Ports []string `json:"ports"` 35 | Labels map[string]string `json:"labels"` 36 | Links map[string]string `json:"links"` 37 | Metadata map[string]interface{} `json:"metadata"` 38 | Token string `json:"token"` 39 | Fqdn string `json:"fqdn"` 40 | HealthCheck HealthCheck `json:"health_check"` 41 | PrimaryServiceName string `json:"primary_service_name"` 42 | LBConfig LBConfig `json:"lb_config"` 43 | EnvironmentUUID string `json:"environment_uuid"` 44 | State string `json:"state"` 45 | System bool `json:"system"` 46 | EnvironmentName string `json:"environment_name"` 47 | Selector string `json:"selector"` 48 | } 49 | 50 | type Container struct { 51 | Name string `json:"name"` 52 | PrimaryIp string `json:"primary_ip"` 53 | PrimaryMacAddress string `json:"primary_mac_address"` 54 | Ips []string `json:"ips"` 55 | Ports []string `json:"ports"` 56 | ServiceName string `json:"service_name"` 57 | ServiceIndex string `json:"service_index"` 58 | StackName string `json:"stack_name"` 59 | StackUUID string `json:"stack_uuid"` 60 | Labels map[string]string `json:"labels"` 61 | CreateIndex int `json:"create_index"` 62 | HostUUID string `json:"host_uuid"` 63 | UUID string `json:"uuid"` 64 | State string `json:"state"` 65 | HealthState string `json:"health_state"` 66 | ExternalId string `json:"external_id"` 67 | StartCount int `json:"start_count"` 68 | MemoryReservation int64 `json:"memory_reservation"` 69 | MilliCPUReservation int64 `json:"milli_cpu_reservation"` 70 | Dns []string `json:"dns"` 71 | DnsSearch []string `json:"dns_search"` 72 | HealthCheckHosts []string `json:"health_check_hosts"` 73 | NetworkFromContainerUUID string `json:"network_from_container_uuid"` 74 | NetworkUUID string `json:"network_uuid"` 75 | Links map[string]string `json:"links"` 76 | System bool `json:"system"` 77 | EnvironmentUUID string `json:"environment_uuid"` 78 | HealthCheck HealthCheck `json:"health_check"` 79 | EnvironmentName string `json:"environment_name"` 80 | ServiceUUID string `json:"service_uuid"` 81 | } 82 | 83 | type Network struct { 84 | Name string `json:"name"` 85 | UUID string `json:"uuid"` 86 | EnvironmentUUID string `json:"environment_uuid"` 87 | Metadata map[string]interface{} `json:"metadata"` 88 | HostPorts bool `json:"host_ports"` 89 | Default bool `json:"is_default"` 90 | Policy []NetworkPolicyRule `json:"policy,omitempty"` 91 | DefaultPolicyAction string `json:"default_policy_action"` 92 | } 93 | 94 | type Host struct { 95 | Name string `json:"name"` 96 | AgentIP string `json:"agent_ip"` 97 | HostId int `json:"host_id"` 98 | Labels map[string]string `json:"labels"` 99 | UUID string `json:"uuid"` 100 | Hostname string `json:"hostname"` 101 | Memory int64 `json:"memory"` 102 | MilliCPU int64 `json:"milli_cpu"` 103 | LocalStorageMb int64 `json:"local_storage_mb"` 104 | EnvironmentUUID string `json:"environment_uuid"` 105 | State string `json:"state"` 106 | } 107 | 108 | type PortRule struct { 109 | SourcePort int `json:"source_port"` 110 | Protocol string `json:"protocol"` 111 | Path string `json:"path"` 112 | Hostname string `json:"hostname"` 113 | Service string `json:"service"` 114 | TargetPort int `json:"target_port"` 115 | Priority int `json:"priority"` 116 | BackendName string `json:"backend_name"` 117 | Selector string `json:"selector"` 118 | Container string `json:"container"` 119 | ContainerUUID string `json:"container_uuid"` 120 | } 121 | 122 | type LBConfig struct { 123 | CertificateIDs []string `json:"certificate_ids"` 124 | DefaultCertificateID string `json:"default_certificate_id"` 125 | PortRules []PortRule `json:"port_rules"` 126 | Config string `json:"config"` 127 | StickinessPolicy LBStickinessPolicy `json:"stickiness_policy"` 128 | } 129 | 130 | type LBStickinessPolicy struct { 131 | Name string `json:"name"` 132 | Cookie string `json:"cookie"` 133 | Domain string `json:"domain"` 134 | Indirect bool `json:"indirect"` 135 | Nocache bool `json:"nocache"` 136 | Postonly bool `json:"postonly"` 137 | Mode string `json:"mode"` 138 | } 139 | 140 | type NetworkPolicyRuleBetween struct { 141 | Selector string `yaml:"selector,omitempty"` 142 | GroupBy string `yaml:"groupBy,omitempty"` 143 | } 144 | 145 | type NetworkPolicyRuleMember struct { 146 | Selector string `yaml:"selector,omitempty"` 147 | } 148 | 149 | type NetworkPolicyRule struct { 150 | From *NetworkPolicyRuleMember `yaml:"from"` 151 | To *NetworkPolicyRuleMember `yaml:"to"` 152 | Ports []string `yaml:"ports"` 153 | Within string `yaml:"within"` 154 | Between *NetworkPolicyRuleBetween `yaml:"between"` 155 | Action string `yaml:"action"` 156 | } 157 | -------------------------------------------------------------------------------- /metadata/utils.go: -------------------------------------------------------------------------------- 1 | package metadata 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | func testConnection(mdClient Client) error { 8 | var err error 9 | maxTime := 20 * time.Second 10 | 11 | for i := 1 * time.Second; i < maxTime; i *= time.Duration(2) { 12 | if _, err = mdClient.GetVersion(); err != nil { 13 | time.Sleep(i) 14 | } else { 15 | return nil 16 | } 17 | } 18 | return err 19 | } 20 | -------------------------------------------------------------------------------- /util/selector_util.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | // supported protocols 9 | const ( 10 | NEQ = "!=" 11 | EQ = "=" 12 | NOTIN = " notin " 13 | IN = " in " 14 | NOOP = "" 15 | ) 16 | 17 | type SelectorConstraint interface { 18 | IsSelectorMatch(labels map[string]string) bool 19 | } 20 | 21 | type SelectorConstraintIn struct { 22 | Key string 23 | Value []string 24 | } 25 | 26 | func (s SelectorConstraintIn) IsSelectorMatch(labels map[string]string) bool { 27 | var found bool 28 | for k := range labels { 29 | if strings.EqualFold(k, s.Key) { 30 | for _, v := range s.Value { 31 | if strings.EqualFold(labels[k], v) { 32 | found = true 33 | break 34 | } 35 | } 36 | } 37 | } 38 | return found 39 | } 40 | 41 | type SelectorConstraintNotIn struct { 42 | Key string 43 | Value []string 44 | } 45 | 46 | func (s SelectorConstraintNotIn) IsSelectorMatch(labels map[string]string) bool { 47 | var found bool 48 | for k := range labels { 49 | if strings.EqualFold(k, s.Key) { 50 | for _, v := range s.Value { 51 | if !strings.EqualFold(labels[k], v) { 52 | found = true 53 | break 54 | } 55 | } 56 | } 57 | } 58 | return found 59 | } 60 | 61 | type SelectorConstraintEq struct { 62 | Key string 63 | Value string 64 | } 65 | 66 | func (s SelectorConstraintEq) IsSelectorMatch(labels map[string]string) bool { 67 | var found bool 68 | for k := range labels { 69 | if strings.EqualFold(k, s.Key) { 70 | if strings.EqualFold(labels[k], s.Value) { 71 | found = true 72 | break 73 | } 74 | } 75 | } 76 | return found 77 | } 78 | 79 | type SelectorConstraintNEq struct { 80 | Key string 81 | Value string 82 | } 83 | 84 | func (s SelectorConstraintNEq) IsSelectorMatch(labels map[string]string) bool { 85 | var found bool 86 | for k := range labels { 87 | if strings.EqualFold(k, s.Key) { 88 | if !strings.EqualFold(labels[k], s.Value) { 89 | found = true 90 | break 91 | } 92 | } 93 | } 94 | return found 95 | } 96 | 97 | type SelectorConstraintNoop struct { 98 | Key string 99 | } 100 | 101 | func (s SelectorConstraintNoop) IsSelectorMatch(labels map[string]string) bool { 102 | _, ok := labels[s.Key] 103 | return ok 104 | } 105 | 106 | func IsSelectorMatch(selector string, labels map[string]string) bool { 107 | if len(selector) == 0 { 108 | return false 109 | } 110 | 111 | if labels == nil { 112 | labels = map[string]string{} 113 | } 114 | 115 | constraints := GetSelectorConstraints(selector) 116 | if len(constraints) == 0 { 117 | return false 118 | } 119 | 120 | found := 0 121 | for _, constraint := range constraints { 122 | if constraint.IsSelectorMatch(labels) { 123 | found = found + 1 124 | } 125 | } 126 | if found != len(constraints) { 127 | return false 128 | } 129 | return true 130 | } 131 | 132 | func GetSelectorConstraints(selector string) []SelectorConstraint { 133 | var constraints []SelectorConstraint 134 | var constraintsStr []string 135 | inList := false 136 | constraint := "" 137 | for i, char := range selector { 138 | c := string(char) 139 | finishConstraint := (i == len(selector)-1) 140 | if c == "(" { 141 | inList = true 142 | } else if c == ")" { 143 | inList = false 144 | } else if c == "," { 145 | if inList { 146 | constraint = fmt.Sprintf("%s%s", constraint, c) 147 | } else { 148 | finishConstraint = true 149 | } 150 | } else { 151 | constraint = fmt.Sprintf("%s%s", constraint, c) 152 | } 153 | if finishConstraint { 154 | constraintsStr = append(constraintsStr, constraint) 155 | constraint = "" 156 | } 157 | } 158 | for _, constraintStr := range constraintsStr { 159 | c := GetSelectorConstraint(constraintStr) 160 | if c == nil { 161 | continue 162 | } 163 | constraints = append(constraints, c) 164 | } 165 | return constraints 166 | } 167 | 168 | func GetSelectorConstraint(selector string) SelectorConstraint { 169 | var ops []string 170 | opts := append(ops, NOOP, EQ, NOTIN, IN, NEQ) 171 | finalOp := NOOP 172 | key := "" 173 | value := "" 174 | for _, op := range opts { 175 | if op == NOOP { 176 | continue 177 | } 178 | exp := strings.Split(selector, op) 179 | if len(exp) == 2 { 180 | finalOp = op 181 | key = strings.TrimSpace(exp[0]) 182 | value = strings.TrimSpace(exp[1]) 183 | } 184 | if finalOp == EQ { 185 | return &SelectorConstraintEq{ 186 | Key: key, 187 | Value: value, 188 | } 189 | } else if finalOp == EQ { 190 | return &SelectorConstraintNEq{ 191 | Key: key, 192 | Value: value, 193 | } 194 | } else if finalOp == NOOP { 195 | return &SelectorConstraintNoop{ 196 | Key: key, 197 | } 198 | } else if finalOp == IN { 199 | return &SelectorConstraintIn{ 200 | Key: key, 201 | Value: strings.Split(value, ","), 202 | } 203 | } else if finalOp == NOTIN { 204 | return &SelectorConstraintNotIn{ 205 | Key: key, 206 | Value: strings.Split(value, ","), 207 | } 208 | } 209 | } 210 | return nil 211 | } 212 | --------------------------------------------------------------------------------